ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
Renderer.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
26use ILIAS\UI\Renderer as RendererInterface;
29use ILIAS\UI\Implementation\Component\Input\Container\Form\FormWithoutSubmitButton;
32
37{
41 public function render(Component\Component $component, RendererInterface $default_renderer): string
42 {
43 if (!$component instanceof Component\Modal\Modal) {
44 $this->cannotHandleComponent($component);
45 }
46
48 $component = $this->addTriggererOnLoadCode($component);
49
50 // If the modal is rendered async, we just create a fake container which will be
51 // replaced by the modal upon successful ajax request
52 if ($component->getAsyncRenderUrl()) {
53 return $this->renderAsync($component);
54 }
55
56 if ($component instanceof Component\Modal\Interruptive) {
57 return $this->renderInterruptive($component, $default_renderer);
58 } elseif ($component instanceof Component\Modal\RoundTrip) {
59 return $this->renderRoundTrip($component, $default_renderer);
60 } elseif ($component instanceof Component\Modal\Lightbox) {
61 return $this->renderLightbox($component, $default_renderer);
62 }
63 $this->cannotHandleComponent($component);
64 }
65
69 public function registerResources(ResourceRegistry $registry): void
70 {
71 parent::registerResources($registry);
72 $registry->register('assets/js/modal.min.js');
73 }
74
76 {
77 $show = $modal->getShowSignal();
78 $close = $modal->getCloseSignal();
79
80 $replace = "";
81 if ($modal instanceof Component\Modal\RoundTrip) {
82 $replace = $modal->getReplaceSignal();
83 }
84
85 $options = array(
86 'ajaxRenderUrl' => $modal->getAsyncRenderUrl(),
87 'keyboard' => $modal->getCloseWithKeyboard()
88 );
89 // ATTENTION, ATTENTION:
90 // with(Additional)OnLoadCode opens a wormhole into the future, where some unspecified
91 // entity magically created an id for the component that can be used to refer to it
92 // via javascript.
93 // This replaced a pattern, where an id was created manually and the java script
94 // code was manually inserted to the (now internal) js-binding of the
95 // AbstractComponentRenderer. (see commit 192144fd1f0e040cadc0149c3dc15fbc4b67858e).
96 // The wormhole solution is considered superior over the manual creation of ids because:
97 // * withAdditionalOnLoadCode introduces no new principles to the UI framework but reuses
98 // an existing one
99 // * withAdditionalOnLoadCode does not require it to expose internals (js-binding) from
100 // the AbstractComponentRenderer and thus does have less coupling
101 // * withAdditionalOnLoadCode allows the framework to decide, when ids are actually
102 // created
103 // * since withAdditionalOnLoadCode refers to some yet unknown future, it disencourages
104 // tempering with the id _here_.
105 return $modal->withAdditionalOnLoadCode(function ($id) use ($show, $close, $options, $replace): string {
106 $options["url"] = "#$id";
107 $options = json_encode($options);
108 $code =
109 "$(document).on('$show', function(event, signalData) {
110 let modal = document.getElementById('$id');
111 il.UI.modal.showModal(modal, $options, signalData, '$close');
112 });";
113
114 if ($replace != "") {
115 $code .= "$(document).on('$replace', function(event, signalData) {
116 const id = event.target.closest('.c-modal').id;
117 il.UI.core.replaceContent(id, signalData.options.url, 'component');
118 });";
119 }
120 return $code;
121 });
122 }
123
124 protected function renderAsync(Component\Modal\Modal $modal): string
125 {
126 $modal = $this->registerSignals($modal);
127 $id = $this->bindJavaScript($modal);
128 return "<span id='$id'></span>";
129 }
130
131 protected function renderInterruptive(
133 RendererInterface $default_renderer
134 ): string {
135 $tpl = $this->getTemplate('tpl.interruptive.html', true, true);
136 $modal = $this->registerSignals($modal);
137 $id = $this->bindJavaScript($modal);
138 $tpl->setVariable('ID', $id);
139 $value = $modal->getFormAction();
140 $tpl->setVariable('FORM_ACTION', $value);
141 $tpl->setVariable('TITLE', $modal->getTitle());
142 $tpl->setVariable('MESSAGE', $modal->getMessage());
143
144 $standard_items = $this->renderInterruptiveItemsByClass(
145 Component\Modal\InterruptiveItem\Standard::class,
146 $modal->getAffectedItems(),
147 $default_renderer
148 );
149 if ($standard_items) {
150 $tpl->setCurrentBlock('with_standard_items');
151 $tpl->setVariable('STANDARD_ITEMS', $standard_items);
152 }
153
154 $key_value_items = $this->renderInterruptiveItemsByClass(
155 Component\Modal\InterruptiveItem\KeyValue::class,
156 $modal->getAffectedItems(),
157 $default_renderer
158 );
159 if ($key_value_items) {
160 $tpl->setCurrentBlock('with_key_value_items');
161 $tpl->setVariable('KEY_VALUE_ITEMS', $key_value_items);
162 }
163
164 $tpl->setVariable('ACTION_BUTTON_LABEL', $modal->getActionButtonLabel() ?? $this->txt('delete'));
165 $tpl->setVariable('CANCEL_BUTTON_LABEL', $modal->getCancelButtonLabel() ?? $this->txt('cancel'));
166 $tpl->setVariable('CLOSE_LABEL', $modal->getCancelButtonLabel() ?? $this->txt('cancel'));
167
168 return $tpl->get();
169 }
170
179 string $class_name,
180 array $items,
181 RendererInterface $default_renderer
182 ): string {
183 $items_of_class = array_filter(
184 $items,
185 fn($i) => $i instanceof $class_name
186 );
187 $rendered_items = '';
188 foreach ($items_of_class as $item) {
189 $rendered_items .= $default_renderer->render($item);
190 }
191 return $rendered_items;
192 }
193
194 protected function renderRoundTrip(Component\Modal\RoundTrip $modal, RendererInterface $default_renderer): string
195 {
196 $tpl = $this->getTemplate('tpl.roundtrip.html', true, true);
198 $modal = $this->registerSignals($modal);
199 $id = $this->bindJavaScript($modal);
200 $tpl->setVariable('ID', $id);
201 $tpl->setVariable('TITLE', $modal->getTitle());
202 $tpl->setVariable('CLOSE_LABEL', $this->txt('close'));
203
204 foreach ($modal->getContent() as $content) {
205 $tpl->setCurrentBlock('with_content');
206 $tpl->setVariable('CONTENT', $default_renderer->render($content));
207 $tpl->parseCurrentBlock();
208 }
209 foreach ($modal->getActionButtons() as $button) {
210 $tpl->setCurrentBlock('with_buttons');
211 $tpl->setVariable('BUTTON', $default_renderer->render($button));
212 $tpl->parseCurrentBlock();
213 }
214
215 // only render form if it contains any inputs (for now).
216 if (!empty($modal->getInputs())) {
217 // render form in modal body.
218 $tpl->setCurrentBlock('with_form');
219 $tpl->setVariable('FORM', $default_renderer->render($modal->getForm()));
220 $tpl->parseCurrentBlock();
221
222 // render submit in modal footer.
223 $submit = $this->getUIFactory()->button()->standard(
224 $modal->getSubmitLabel() ?? $this->txt('save'),
225 ''
226 )->withOnClick($modal->getForm()->getSubmitSignal());
227 $tpl->setCurrentBlock('with_submit');
228 $tpl->setVariable('SUBMIT_BUTTON', $default_renderer->render($submit));
229 $tpl->parseCurrentBlock();
230 }
231
232 $tpl->setVariable('CANCEL_BUTTON_LABEL', $modal->getCancelButtonLabel() ?? $this->txt('cancel'));
233 return $tpl->get();
234 }
235
236 protected function renderLightbox(Component\Modal\Lightbox $modal, RendererInterface $default_renderer): string
237 {
238 $tpl = $this->getTemplate('tpl.lightbox.html', true, true);
239 $modal = $this->registerSignals($modal);
240 $id = $this->bindJavaScript($modal);
241 $tpl->setVariable('ID', $id);
242 $id_carousel = "{$id}_carousel";
243 $pages = $modal->getPages();
244 $tpl->setVariable('TITLE', $pages[0]->getTitle());
245 $tpl->setVariable('ID_CAROUSEL', $id_carousel);
246 $tpl->setVariable('CLOSE_LABEL', $this->txt('close'));
247 $tpl->setVariable('COLOR_SCHEME', $modal->getScheme());
248
249 if (count($pages) > 1) {
250 $tpl->setCurrentBlock('has_indicators');
251 foreach ($pages as $index => $page) {
252 $tpl->setCurrentBlock('indicators');
253 $tpl->setVariable('INDEX', $index);
254 $tpl->setVariable('CLASS_ACTIVE', ($index == 0) ? 'active' : '');
255 $tpl->setVariable('ID_CAROUSEL2', $id_carousel);
256 $tpl->parseCurrentBlock();
257 }
258 }
259 $first = true;
260 foreach ($pages as $page) {
261 $this->renderPage($page, $first, $tpl, $default_renderer);
262 $first = false;
263 }
264 if (count($pages) > 1) {
265 $tpl->setCurrentBlock('controls');
266 $tpl->setVariable('ID_CAROUSEL3', $id_carousel);
267 $tpl->parseCurrentBlock();
268 }
269 $tpl->setVariable('ID_CAROUSEL4', $id_carousel);
270 return $tpl->get();
271 }
272
273 private function renderPage(LightboxPage $page, bool $first, Template $tpl, RendererInterface $default_renderer): void
274 {
275 $vertical = false;
276 $components = [$page->getComponent()];
277 if ($page instanceof LightboxTextPage) {
278 $tpl->setCurrentBlock('pages');
279 $tpl->touchBlock('page_type_text');
280 $tpl->parseCurrentBlock();
281 } elseif ($page instanceof LightboxCardPage) {
282 $components = array_merge(
283 $page->getComponent()->getSections(),
284 $page->getComponent()->getHiddenSections()
285 );
286 $vertical = true;
287 }
288 $tpl->setCurrentBlock('pages');
289 $tpl->setVariable('CLASS_ACTIVE', $first ? 'active' : '');
290 $tpl->setVariable('ORIENTATION', $vertical ? 'item-vertical' : '');
291 $tpl->setVariable('TITLE2', htmlentities($page->getTitle(), ENT_QUOTES, 'UTF-8'));
292 $tpl->setVariable('CONTENT', $default_renderer->render($components));
293 if ($page instanceof LightboxDescriptionEnabledPage) {
294 $tpl->setVariable('DESCRIPTION', $page->getDescription());
295 }
296 $tpl->parseCurrentBlock();
297 }
298}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$components
renderPage(LightboxPage $page, bool $first, Template $tpl, RendererInterface $default_renderer)
Definition: Renderer.php:273
renderInterruptiveItemsByClass(string $class_name, array $items, RendererInterface $default_renderer)
Filters items by provided class, and renders only those.
Definition: Renderer.php:178
renderInterruptive(Component\Modal\Interruptive $modal, RendererInterface $default_renderer)
Definition: Renderer.php:131
registerSignals(Component\Modal\Modal $modal)
Definition: Renderer.php:75
renderAsync(Component\Modal\Modal $modal)
Definition: Renderer.php:124
renderLightbox(Component\Modal\Lightbox $modal, RendererInterface $default_renderer)
Definition: Renderer.php:236
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:69
cannotHandleComponent(Component $component)
This method MUST be called by derived component renderers, if.
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.
addTriggererOnLoadCode(Triggerer $triggerer)
Add onload-code for triggerer.
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
return true
Interface to be extended by components that have the possibility to bind to Javascript.
getTitle()
Get the title of this page, displayed as title in the lightbox modal.
getComponent()
Get the component representing the media item to be displayed in the modals content section,...
render(Component $component, Renderer $default_renderer)
Render the component if possible and delegate additional rendering to the default_renderer.
Registry for resources required by rendered output like Javascript or CSS.
register(string $name)
Add a dependency.
Interface to templating as it is used in the UI framework.
Definition: Template.php:29
setVariable(string $name, $value)
Set a variable in the current block.
setCurrentBlock(string $name)
Set the block to work on.
touchBlock(string $name)
Touch a block without working further on it.
parseCurrentBlock()
Parse the block that is currently worked on.
An entity that renders components to a string output.
Definition: Renderer.php:31