1<?php
2
3use ILIAS\BackgroundTasks\Bucket;
4use ILIAS\BackgroundTasks\Implementation\Bucket\State;
5use ILIAS\BackgroundTasks\Implementation\Tasks\AbstractTask;
6use ILIAS\BackgroundTasks\Implementation\UI\StateTranslator;
7use ILIAS\BackgroundTasks\Task\UserInteraction;
8use ILIAS\UI\Component\Button\Button;
9use ILIAS\UI\Component\Button\Shy;
10use ILIAS\UI\Component\Legacy\Legacy;
11
12/**
13 * Class ilBTPopOverGUI
14 *
15 * @author Oskar Truffer <ot@studer-raimann.ch>
16 * @author Fabian Schmid <fs@studer-raimann.ch>
17 */
18class ilBTPopOverGUI
19{
20    use StateTranslator;
21    /**
22     * @var \ILIAS\DI\Container
23     */
24    protected $dic;
25
26
27    public function __construct(\ILIAS\DI\Container $dic)
28    {
29        $this->dic = $dic;
30    }
31
32
33    /**
34     * Get the Notification Items. DOES NOT DO ANY PERMISSION CHECKS.
35     */
36    public function getNotificationItem(int $nr_buckets) : ILIAS\UI\Component\Item\Notification
37    {
38        $ui_factory = $this->dic->ui()->factory();
39
40        $title = $ui_factory->link()->standard($this->txt('background_tasks'), '#');
41        $icon = $ui_factory->symbol()->icon()->standard('bgtk', $this->txt('background_tasks'));
42
43        return $this->dic->ui()->factory()
44            ->item()
45            ->notification($title, $icon)
46            ->withDescription("$nr_buckets {$this->txt('background_tasks')}")
47            ->withAggregateNotifications($this->getAggregateItems());
48    }
49
50
51    /**
52     * @return ILIAS\UI\Component\Item\Notification[]
53     */
54    protected function getAggregateItems() : array
55    {
56        $persistence = $this->dic->backgroundTasks()->persistence();
57        $items = [];
58        $observer_ids = $persistence->getBucketIdsOfUser($this->dic->user()->getId(), 'id', 'DESC');
59        foreach ($persistence->loadBuckets($observer_ids) as $observer) {
60            $items[] = $this->getItemForObserver($observer);
61        }
62
63        return $items;
64    }
65
66
67    public function getItemForObserver(Bucket $observer) : ILIAS\UI\Component\Item\Notification
68    {
69        $redirect_uri = "//{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
70
71        $f = $this->dic->ui()->factory();
72
73        $state = (int) $observer->getState();
74        $current_task = $observer->getCurrentTask();
75
76        $icon = $f->symbol()->icon()->standard("bgtk", $this->txt("bg_task"));
77        $title = $observer->getTitle() . ($state === State::SCHEDULED ? " ({$this->txt('scheduled')})" : "");
78
79        if ($state === State::USER_INTERACTION) {
80            $actions = $this->getUserInteractionContent($observer, $redirect_uri);
81            $primary_action = array_pop($actions);
82            if ($primary_action instanceof Button) {
83                $title = $primary_action->withLabel($title);
84            }
85            $item = $f->item()->notification($title, $icon);
86
87//            $item = $item->withProperties([
88//                $this->dic->language()->txt('nc_mail_prop_time') => \ilDatePresentation::formatDate(
89//                    new \ilDateTime(time(), IL_CAL_UNIX)
90//                )
91//            ]);
92
93            $item = $item->withActions($f->dropdown()->standard($actions));
94            $input = $current_task->getInput();
95            $message = $current_task->getMessage($input);
96
97            if ((!empty($message)) and ($message != null)) {
98                $item = $item->withDescription($message);
99            } else {
100                $item = $item->withAdditionalContent($this->getProgressbar($observer));
101            }
102
103            return $item->withCloseAction(
104                $this->getCloseButtonAction($current_task->getRemoveOption(), $redirect_uri, $observer)
105            );
106        }
107
108        $item = $f->item()->notification($title, $icon);
109
110        if ($state === State::RUNNING) {
111            $url = $this->getRefreshUrl($observer);
112            //Running Items probably need to refresh themselves, right?
113            $item = $item->withAdditionalOnLoadCode(function ($id) use ($url) {
114                //Note this is only for demo purposes, adapt as needed.
115                return "var notification_item = il.UI.item.notification.getNotificationItemObject($('#$id'));
116                    il.BGTask.refreshItem(notification_item,'$url');";
117            });
118
119            $expected = $current_task->getExpectedTimeOfTaskInSeconds();
120            $possibly_failed = ($observer->getLastHeartbeat() < (time() - $expected));
121            if ($possibly_failed === true) {
122                $item = $item->withDescription($this->txt('task_might_be_failed'));
123                $item = $item->withCloseAction(
124                    $this->getCloseButtonAction($current_task->getAbortOption(), $redirect_uri, $observer)
125                );
126            }
127        }
128
129        return $item->withAdditionalContent($this->getDefaultCardContent($observer));
130    }
131
132
133    private function getDefaultCardContent(Bucket $observer) : Legacy
134    {
135        return $this->getProgressbar($observer);
136    }
137
138
139    /**
140     * @return Shy[]
141     */
142    public function getUserInteractionContent(Bucket $observer, string $redirect_uri) : array
143    {
144        $factory = $this->dic->ui()->factory();
145        $language = $this->dic->language();
146        $persistence = $this->dic->backgroundTasks()->persistence();
147        $ctrl = $this->dic->ctrl();
148
149        if (!$observer->getCurrentTask() instanceof UserInteraction) {
150            return [$factory->legacy('')];
151        }
152        /** @var UserInteraction $userInteraction */
153        $userInteraction = $observer->getCurrentTask();
154        $options = $userInteraction->getOptions($userInteraction->getInput());
155
156        $shy_buttons = array_map(
157            function (UserInteraction\Option $option) use ($ctrl, $factory, $observer, $persistence, $redirect_uri, $language) {
158                $ctrl->setParameterByClass(
159                    ilBTControllerGUI::class,
160                    ilBTControllerGUI::FROM_URL,
161                    ilBTControllerGUI::hash("//{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}")
162                );
163                $ctrl->setParameterByClass(
164                    ilBTControllerGUI::class,
165                    ilBTControllerGUI::SELECTED_OPTION,
166                    $option->getValue()
167                );
168                $ctrl->setParameterByClass(
169                    ilBTControllerGUI::class,
170                    ilBTControllerGUI::OBSERVER_ID,
171                    $persistence->getBucketContainerId($observer)
172                );
173                $this->addFromUrlToNextRequest($redirect_uri);
174
175                return $factory->button()
176                    ->shy(
177                        $language->txt($option->getLangVar()),
178                        $ctrl->getLinkTargetByClass([ilBTControllerGUI::class], ilBTControllerGUI::CMD_USER_INTERACTION)
179                    );
180            },
181            $options
182        );
183
184        return $shy_buttons;
185    }
186
187
188    private function getProgressbar(Bucket $observer) : Legacy
189    {
190        $percentage = $observer->getOverallPercentage();
191
192        switch (true) {
193            case ((int) $percentage === 100):
194                $running = "";
195                $content = $this->dic->language()->txt("completed");
196                break;
197            case ((int) $observer->getState() === State::USER_INTERACTION):
198                $running = "";
199                $content = $this->dic->language()->txt("waiting");
200                break;
201            default:
202                $running = "active";
203                $content = "{$percentage}%";
204                break;
205        }
206
207        return $this->dic->ui()->factory()->legacy(" <div class='progress'>
208                    <div class='progress-bar progress-bar-striped {$running}' role='progressbar' aria-valuenow='{$percentage}'
209                        aria-valuemin='0' aria-valuemax='100' style='width:{$percentage}%'>
210                        {$content}
211                    </div>
212				</div> ");
213    }
214
215
216    private function getCloseButtonAction(UserInteraction\Option $option, $redirect_uri, Bucket $observer) : string
217    {
218        $ctrl = $this->dic->ctrl();
219        $persistence = $this->dic->backgroundTasks()->persistence();
220        $ctrl->setParameterByClass(ilBTControllerGUI::class, ilBTControllerGUI::OBSERVER_ID, $persistence->getBucketContainerId($observer));
221        $this->addFromUrlToNextRequest($redirect_uri);
222        $ctrl->setParameterByClass(ilBTControllerGUI::class, ilBTControllerGUI::IS_ASYNC, "true");
223
224        switch ($option->getValue()) {
225            case AbstractTask::MAIN_ABORT:
226                $action = $ctrl->getLinkTargetByClass([ilBTControllerGUI::class], ilBTControllerGUI::CMD_ABORT);
227                break;
228            case AbstractTask::MAIN_REMOVE:
229                $action = $ctrl->getLinkTargetByClass([ilBTControllerGUI::class], ilBTControllerGUI::CMD_REMOVE);
230                break;
231            default:
232                $ctrl->setParameterByClass(ilBTControllerGUI::class, ilBTControllerGUI::SELECTED_OPTION, $option->getValue());
233                $action = $ctrl->getLinkTargetByClass([ilBTControllerGUI::class], ilBTControllerGUI::CMD_USER_INTERACTION);
234                break;
235        }
236
237        return $action;
238    }
239
240
241    private function getRefreshUrl(Bucket $observer) : string
242    {
243        $ctrl = $this->dic->ctrl();
244        $persistence = $this->dic->backgroundTasks()->persistence();
245        $ctrl->setParameterByClass(ilBTControllerGUI::class, ilBTControllerGUI::OBSERVER_ID, $persistence->getBucketContainerId($observer));
246
247        return $ctrl->getLinkTargetByClass([ilBTControllerGUI::class], ilBTControllerGUI::CMD_GET_REPLACEMENT_ITEM);
248    }
249
250
251    private function addFromUrlToNextRequest(string $redirect_uri) : void
252    {
253        $this->dic->ctrl()->setParameterByClass(ilBTControllerGUI::class, ilBTControllerGUI::FROM_URL, ilBTControllerGUI::hash($redirect_uri));
254    }
255
256
257    private function txt(string $id) : string
258    {
259        return $this->dic->language()->txt($id);
260    }
261}
262