1<?php
2namespace ILIAS\UI\Implementation\Component\Modal;
3
4use ILIAS\UI\Component\Modal\LightboxDescriptionEnabledPage;
5use ILIAS\UI\Implementation\Render\AbstractComponentRenderer;
6use ILIAS\UI\Implementation\Render\ResourceRegistry;
7use ILIAS\UI\Renderer as RendererInterface;
8use ILIAS\UI\Component;
9
10/**
11 * @author Stefan Wanzenried <sw@studer-raimann.ch>
12 */
13class Renderer extends AbstractComponentRenderer
14{
15
16    /**
17     * @inheritdoc
18     */
19    public function render(Component\Component $component, RendererInterface $default_renderer)
20    {
21        $this->checkComponent($component);
22
23        // If the modal is rendered async, we just create a fake container which will be
24        // replaced by the modal upon successful ajax request
25        /** @var Modal $component */
26        if ($component->getAsyncRenderUrl()) {
27            return $this->renderAsync($component);
28        }
29
30        if ($component instanceof Component\Modal\Interruptive) {
31            return $this->renderInterruptive($component, $default_renderer);
32        } elseif ($component instanceof Component\Modal\RoundTrip) {
33            return $this->renderRoundTrip($component, $default_renderer);
34        } elseif ($component instanceof Component\Modal\Lightbox) {
35            return $this->renderLightbox($component, $default_renderer);
36        }
37        return '';
38    }
39
40    /**
41     * @inheritdoc
42     */
43    public function registerResources(ResourceRegistry $registry)
44    {
45        parent::registerResources($registry);
46        $registry->register('./src/UI/templates/js/Modal/modal.js');
47    }
48
49
50    /**
51     * @param Component\Modal\Modal $modal
52     * @param string $id
53     */
54    protected function registerSignals(Component\Modal\Modal $modal)
55    {
56        $show = $modal->getShowSignal();
57        $close = $modal->getCloseSignal();
58
59        $replace = "";
60        if ($modal instanceof Component\Modal\RoundTrip) {
61            $replace = $modal->getReplaceSignal();
62        }
63
64        $options = array(
65            'ajaxRenderUrl' => $modal->getAsyncRenderUrl(),
66            'keyboard' => $modal->getCloseWithKeyboard()
67        );
68        // ATTENTION, ATTENTION:
69        // with(Additional)OnLoadCode opens a wormhole into the future, where some unspecified
70        // entity magically created an id for the component that can be used to refer to it
71        // via javascript.
72        // This replaced a pattern, where an id was created manually and the java script
73        // code was manually inserted to the (now internal) js-binding of the
74        // AbstractComponentRenderer. (see commit 192144fd1f0e040cadc0149c3dc15fbc4b67858e).
75        // The wormhole solution is considered superior over the manual creation of ids because:
76        // * withAdditionalOnLoadCode introduces no new principles to the UI framework but reuses
77        //   an existing one
78        // * withAdditionalOnLoadCode does not require it to expose internals (js-binding) from
79        //   the AbstractComponentRenderer and thus does have less coupling
80        // * withAdditionalOnLoadCode allows the framework to decide, when ids are actually
81        //   created
82        // * since withAdditionalOnLoadCode refers to some yet unknown future, it disencourages
83        //   tempering with the id _here_.
84        return $modal->withAdditionalOnLoadCode(function ($id) use ($show, $close, $options, $replace) {
85            $options["url"] = "#{$id}";
86            $options = json_encode($options);
87            $code =
88                "$(document).on('{$show}', function(event, signalData) { il.UI.modal.showModal('{$id}', {$options}, signalData); return false; });" .
89                "$(document).on('{$close}', function() { il.UI.modal.closeModal('{$id}'); return false; });";
90            if ($replace != "") {
91                $code .= "$(document).on('{$replace}', function(event, signalData) { il.UI.modal.replaceFromSignal('{$id}', signalData);});";
92            }
93            return $code;
94        });
95    }
96
97    /**
98     * @param Component\Modal\Modal $modal
99     * @return string
100     */
101    protected function renderAsync(Component\Modal\Modal $modal)
102    {
103        $modal = $this->registerSignals($modal);
104        $id = $this->bindJavaScript($modal);
105        return "<span id='{$id}'></span>";
106    }
107
108    /**
109     * @param Component\Modal\Interruptive $modal
110     * @param RendererInterface $default_renderer
111     *
112     * @return string
113     */
114    protected function renderInterruptive(Component\Modal\Interruptive $modal, RendererInterface $default_renderer)
115    {
116        $tpl = $this->getTemplate('tpl.interruptive.html', true, true);
117        $modal = $this->registerSignals($modal);
118        $id = $this->bindJavaScript($modal);
119        $tpl->setVariable('ID', $id);
120        $tpl->setVariable('FORM_ACTION', $modal->getFormAction());
121        $tpl->setVariable('TITLE', $modal->getTitle());
122        $tpl->setVariable('MESSAGE', $modal->getMessage());
123        if (count($modal->getAffectedItems())) {
124            $tpl->setCurrentBlock('with_items');
125            foreach ($modal->getAffectedItems() as $item) {
126                $tpl->setCurrentBlock('item');
127                $icon = ($item->getIcon()) ? $default_renderer->render($item->getIcon()) : '';
128                $desc = ($item->getDescription()) ? '<br>' . $item->getDescription() : '';
129                $tpl->setVariable('ITEM_ICON', $icon);
130                $tpl->setVariable('ITEM_ID', $item->getId());
131                $tpl->setVariable('ITEM_TITLE', $item->getTitle());
132                $tpl->setVariable('ITEM_DESCRIPTION', $desc);
133                $tpl->parseCurrentBlock();
134            }
135        }
136        $tpl->setVariable('ACTION_BUTTON_LABEL', $this->txt($modal->getActionButtonLabel()));
137        $tpl->setVariable('CANCEL_BUTTON_LABEL', $this->txt($modal->getCancelButtonLabel()));
138        return $tpl->get();
139    }
140
141
142    /**
143     * @param Component\Modal\RoundTrip $modal
144     * @param RendererInterface $default_renderer
145     *
146     * @return string
147     */
148    protected function renderRoundTrip(Component\Modal\RoundTrip $modal, RendererInterface $default_renderer)
149    {
150        $tpl = $this->getTemplate('tpl.roundtrip.html', true, true);
151        $modal = $this->registerSignals($modal);
152        $id = $this->bindJavaScript($modal);
153        $tpl->setVariable('ID', $id);
154        $tpl->setVariable('TITLE', $modal->getTitle());
155        foreach ($modal->getContent() as $content) {
156            $tpl->setCurrentBlock('with_content');
157            $tpl->setVariable('CONTENT', $default_renderer->render($content));
158            $tpl->parseCurrentBlock();
159        }
160        foreach ($modal->getActionButtons() as $button) {
161            $tpl->setCurrentBlock('with_buttons');
162            $tpl->setVariable('BUTTON', $default_renderer->render($button));
163            $tpl->parseCurrentBlock();
164        }
165        $tpl->setVariable('CANCEL_BUTTON_LABEL', $this->txt($modal->getCancelButtonLabel()));
166        return $tpl->get();
167    }
168
169
170    /**
171     * @param Component\Modal\Lightbox $modal
172     * @param RendererInterface $default_renderer
173     *
174     * @return string
175     */
176    protected function renderLightbox(Component\Modal\Lightbox $modal, RendererInterface $default_renderer)
177    {
178        $tpl = $this->getTemplate('tpl.lightbox.html', true, true);
179        $modal = $this->registerSignals($modal);
180        $id = $this->bindJavaScript($modal);
181        $tpl->setVariable('ID', $id);
182        $id_carousel = "{$id}_carousel";
183        $pages = $modal->getPages();
184        $tpl->setVariable('TITLE', $pages[0]->getTitle());
185        $tpl->setVariable('ID_CAROUSEL', $id_carousel);
186        if (count($pages) > 1) {
187            $tpl->setCurrentBlock('has_indicators');
188            foreach ($pages as $index => $page) {
189                $tpl->setCurrentBlock('indicators');
190                $tpl->setVariable('INDEX', $index);
191                $tpl->setVariable('CLASS_ACTIVE', ($index == 0) ? 'active' : '');
192                $tpl->setVariable('ID_CAROUSEL2', $id_carousel);
193                $tpl->parseCurrentBlock();
194            }
195        }
196        foreach ($pages as $i => $page) {
197            if ($page instanceof LightboxTextPage) {
198                $tpl->setCurrentBlock('pages');
199                $tpl->touchBlock('page_type_text');
200                $tpl->parseCurrentBlock();
201            }
202            $tpl->setCurrentBlock('pages');
203            $tpl->setVariable('CLASS_ACTIVE', ($i == 0) ? ' active' : '');
204            $tpl->setVariable('TITLE2', htmlentities($page->getTitle(), ENT_QUOTES, 'UTF-8'));
205            $tpl->setVariable('CONTENT', $default_renderer->render($page->getComponent()));
206            if ($page instanceof LightboxDescriptionEnabledPage) {
207                $tpl->setVariable('DESCRIPTION', $page->getDescription());
208            }
209            $tpl->parseCurrentBlock();
210        }
211        if (count($pages) > 1) {
212            $tpl->setCurrentBlock('controls');
213            $tpl->setVariable('ID_CAROUSEL3', $id_carousel);
214            $tpl->parseCurrentBlock();
215        }
216        $tpl->setVariable('ID_CAROUSEL4', $id_carousel);
217        return $tpl->get();
218    }
219
220
221    /**
222     * @inheritdoc
223     */
224    protected function getComponentInterfaceName()
225    {
226        return array(
227            Component\Modal\Interruptive::class,
228            Component\Modal\RoundTrip::class,
229            Component\Modal\Lightbox::class,
230        );
231    }
232}
233