ILIAS  trunk Revision v11.0_alpha-1744-gb0451eebef4
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
Renderer.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
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 
175  protected function renderInterruptiveItemsByClass(
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 }
Registry for resources required by rendered output like Javascript or CSS.
renderPage(LightboxPage $page, bool $first, Template $tpl, RendererInterface $default_renderer)
Definition: Renderer.php:270
txt(string $id)
Get a text from the language file.
setCurrentBlock(string $name)
Set the block to work on.
$components
getTitle()
Get the title of this page, displayed as title in the lightbox modal.
setVariable(string $name, $value)
Set a variable in the current block.
renderLightbox(Component\Modal\Lightbox $modal, RendererInterface $default_renderer)
Definition: Renderer.php:233
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:66
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
renderInterruptive(Component\Modal\Interruptive $modal, RendererInterface $default_renderer)
Definition: Renderer.php:128
cannotHandleComponent(Component $component)
This method MUST be called by derived component renderers, if.
register(string $name)
Add a dependency.
getComponent()
Get the component representing the media item to be displayed in the modals content section...
parseCurrentBlock()
Parse the block that is currently worked on.
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
renderAsync(Component\Modal\Modal $modal)
Definition: Renderer.php:121
touchBlock(string $name)
Touch a block without working further on it.
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:41
renderInterruptiveItemsByClass(string $class_name, array $items, RendererInterface $default_renderer)
Filters items by provided class, and renders only those.
Definition: Renderer.php:175
registerSignals(Component\Modal\Modal $modal)
Definition: Renderer.php:72
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.