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