Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
CRAP | |
0.00% |
0 / 1 |
BackendController | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
756 | |
0.00% |
0 / 1 |
viewTaskDashboard | n/a |
0 / 0 |
n/a |
0 / 0 |
10 | |||||
viewDashboard | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
viewTaskView | n/a |
0 / 0 |
n/a |
0 / 0 |
13 | |||||
viewTaskCreate | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
viewTaskAnalysis | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
openNav | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * Jingga |
4 | * |
5 | * PHP Version 8.1 |
6 | * |
7 | * @package Modules\Tasks |
8 | * @copyright Dennis Eichhorn |
9 | * @license OMS License 2.0 |
10 | * @version 1.0.0 |
11 | * @link https://jingga.app |
12 | */ |
13 | declare(strict_types=1); |
14 | |
15 | namespace Modules\Tasks\Controller; |
16 | |
17 | use Modules\Dashboard\Models\DashboardElementInterface; |
18 | use Modules\Media\Models\MediaMapper; |
19 | use Modules\Profile\Models\SettingsEnum; |
20 | use Modules\Tasks\Models\AccountRelationMapper; |
21 | use Modules\Tasks\Models\PermissionCategory; |
22 | use Modules\Tasks\Models\TaskElementMapper; |
23 | use Modules\Tasks\Models\TaskMapper; |
24 | use Modules\Tasks\Models\TaskSeen; |
25 | use Modules\Tasks\Models\TaskSeenMapper; |
26 | use Modules\Tasks\Models\TaskStatus; |
27 | use Modules\Tasks\Views\TaskView; |
28 | use phpOMS\Account\PermissionType; |
29 | use phpOMS\Asset\AssetType; |
30 | use phpOMS\Contract\RenderableInterface; |
31 | use phpOMS\DataStorage\Database\Query\Builder; |
32 | use phpOMS\DataStorage\Database\Query\OrderType; |
33 | use phpOMS\Message\Http\RequestStatusCode; |
34 | use phpOMS\Message\RequestAbstract; |
35 | use phpOMS\Message\ResponseAbstract; |
36 | use phpOMS\Views\View; |
37 | |
38 | /** |
39 | * Backend controller for the tasks module. |
40 | * |
41 | * @property \Web\WebApplication $app |
42 | * |
43 | * @package Modules\Tasks |
44 | * @license OMS License 2.0 |
45 | * @link https://jingga.app |
46 | * @since 1.0.0 |
47 | */ |
48 | final class BackendController extends Controller implements DashboardElementInterface |
49 | { |
50 | /** |
51 | * Routing end-point for application behaviour. |
52 | * |
53 | * @param RequestAbstract $request Request |
54 | * @param ResponseAbstract $response Response |
55 | * @param array $data Generic data |
56 | * |
57 | * @return RenderableInterface Returns a renderable object |
58 | * |
59 | * @since 1.0.0 |
60 | * @codeCoverageIgnore |
61 | */ |
62 | public function viewTaskDashboard(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface |
63 | { |
64 | $view = new View($this->app->l11nManager, $request, $response); |
65 | |
66 | /** @var \phpOMS\Model\Html\Head $head */ |
67 | $head = $response->data['Content']->head; |
68 | $head->addAsset(AssetType::CSS, 'Modules/Tasks/Theme/Backend/css/styles.css?v=1.0.0'); |
69 | |
70 | $view->setTemplate('/Modules/Tasks/Theme/Backend/task-dashboard'); |
71 | $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1001101001, $request, $response); |
72 | |
73 | $mapperQuery = TaskMapper::getAnyRelatedToUser($request->header->account) |
74 | ->with('tags') |
75 | ->with('tags/title') |
76 | ->where('status', TaskStatus::OPEN, '!=') |
77 | ->where('tags/title/language', $response->header->l11n->language) |
78 | ->sort('createdAt', OrderType::DESC) |
79 | ->limit(25); |
80 | |
81 | if ($request->getData('ptype') === 'p') { |
82 | $view->data['tasks'] = $mapperQuery->with('createdBy') |
83 | ->where('id', $request->getDataInt('id') ?? 0, '<') |
84 | ->execute(); |
85 | } elseif ($request->getData('ptype') === 'n') { |
86 | $view->data['tasks'] = $mapperQuery->with('createdBy') |
87 | ->where('id', $request->getDataInt('id') ?? 0, '>') |
88 | ->execute(); |
89 | } else { |
90 | $view->data['tasks'] = $mapperQuery->with('createdBy') |
91 | ->where('id', 0, '>') |
92 | ->execute(); |
93 | } |
94 | |
95 | $view->data['task_media'] = []; |
96 | foreach ($view->data['tasks'] as $task) { |
97 | $view->data['task_media'][$task->id] = TaskMapper::has() |
98 | ->with('media') |
99 | ->where('id', $task->id) |
100 | ->execute(); |
101 | } |
102 | |
103 | $openQuery = new Builder($this->app->dbPool->get(), true); |
104 | $openQuery->innerJoin(TaskElementMapper::TABLE) |
105 | ->on(TaskMapper::TABLE . '_d1.' . TaskMapper::PRIMARYFIELD, '=', TaskElementMapper::TABLE . '.task_element_task') |
106 | ->innerJoin(AccountRelationMapper::TABLE) |
107 | ->on(TaskElementMapper::TABLE . '.' . TaskElementMapper::PRIMARYFIELD, '=', AccountRelationMapper::TABLE . '.task_account_task_element') |
108 | ->andWhere(AccountRelationMapper::TABLE . '.task_account_account', '=', $request->header->account); |
109 | |
110 | /** @var \Modules\Tasks\Models\Task[] $open */ |
111 | $open = TaskMapper::getAll() |
112 | ->with('createdBy') |
113 | ->with('tags') |
114 | ->with('tags/title') |
115 | ->where('tags/title/language', $response->header->l11n->language) |
116 | ->where('status', TaskStatus::OPEN) |
117 | ->sort('createdAt', OrderType::DESC) |
118 | ->query($openQuery) |
119 | ->execute(); |
120 | |
121 | $view->data['open'] = $open; |
122 | |
123 | foreach ($view->data['open'] as $task) { |
124 | $view->data['task_media'][$task->id] = TaskMapper::has() |
125 | ->with('media') |
126 | ->where('id', $task->id) |
127 | ->execute(); |
128 | } |
129 | |
130 | // given |
131 | // @todo: this should also include forwarded tasks |
132 | /** @var \Modules\Tasks\Models\Task[] $given */ |
133 | $given = TaskMapper::getAll() |
134 | ->with('createdBy') |
135 | ->with('tags') |
136 | ->with('tags/title') |
137 | ->where('tags/title/language', $response->header->l11n->language) |
138 | ->where('status', TaskStatus::OPEN) |
139 | ->where('createdBy', $response->header->account, '=') |
140 | ->sort('createdAt', OrderType::DESC) |
141 | ->execute(); |
142 | |
143 | $view->data['given'] = $given; |
144 | |
145 | foreach ($view->data['given'] as $task) { |
146 | $view->data['task_media'][$task->id] = TaskMapper::has() |
147 | ->with('media') |
148 | ->where('id', $task->id) |
149 | ->execute(); |
150 | } |
151 | |
152 | /** @var \Modules\Tasks\Models\TaskSeen[] $unread */ |
153 | $unread = TaskSeenMapper::getAll() |
154 | ->where('task', \array_keys($open), 'in') |
155 | ->where('seenBy', $request->header->account) |
156 | ->execute(); |
157 | |
158 | $unseen = []; |
159 | foreach ($unread as $read) { |
160 | if ($read->isRemindered && ($read->reminderAt?->getTimestamp() ?? 0) > $request->header->getRequestTime()) { |
161 | continue; |
162 | } |
163 | |
164 | $unseen[$read->task] = true; |
165 | } |
166 | |
167 | $view->data['unread'] = $unseen; |
168 | |
169 | foreach ($view->data['unread'] as $task) { |
170 | $view->data['task_media'][$task->id] = TaskMapper::has() |
171 | ->with('media') |
172 | ->where('id', $task->id) |
173 | ->execute(); |
174 | } |
175 | |
176 | return $view; |
177 | } |
178 | |
179 | /** |
180 | * {@inheritdoc} |
181 | * @codeCoverageIgnore |
182 | */ |
183 | public function viewDashboard(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface |
184 | { |
185 | /** @var \phpOMS\Model\Html\Head $head */ |
186 | $head = $response->data['Content']->head; |
187 | $head->addAsset(AssetType::CSS, 'Modules/Tasks/Theme/Backend/css/styles.css?v=1.0.0'); |
188 | |
189 | $view = new View($this->app->l11nManager, $request, $response); |
190 | $view->setTemplate('/Modules/Tasks/Theme/Backend/dashboard-task'); |
191 | $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1001101001, $request, $response); |
192 | |
193 | /** @var \Modules\Tasks\Models\Task[] $tasks */ |
194 | $tasks = TaskMapper::getAnyRelatedToUser($request->header->account) |
195 | ->with('tags') |
196 | ->with('tags/title') |
197 | ->sort('taskElements/createdAt', OrderType::DESC) |
198 | ->limit(5) |
199 | ->where('id', 0, '>') |
200 | ->where('tags/title/language', $response->header->l11n->language) |
201 | ->execute(); |
202 | |
203 | $view->data['tasks'] = $tasks; |
204 | |
205 | return $view; |
206 | } |
207 | |
208 | /** |
209 | * Routing end-point for application behaviour. |
210 | * |
211 | * @param RequestAbstract $request Request |
212 | * @param ResponseAbstract $response Response |
213 | * @param array $data Generic data |
214 | * |
215 | * @return RenderableInterface Returns a renderable object |
216 | * |
217 | * @since 1.0.0 |
218 | * @codeCoverageIgnore |
219 | */ |
220 | public function viewTaskView(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface |
221 | { |
222 | $view = new TaskView($this->app->l11nManager, $request, $response); |
223 | |
224 | /** @var \Model\Setting $profileImage */ |
225 | $profileImage = $this->app->appSettings->get(names: SettingsEnum::DEFAULT_PROFILE_IMAGE, module: 'Profile'); |
226 | |
227 | /** @var \Modules\Media\Models\Media $image */ |
228 | $image = MediaMapper::get()->where('id', (int) $profileImage->content)->execute(); |
229 | $view->defaultProfileImage = $image; |
230 | |
231 | if (!TaskMapper::hasReadingPermission($request->header->account, (int) $request->getData('id'))) { |
232 | $response->header->status = RequestStatusCode::R_403; |
233 | $view->setTemplate('/Web/Backend/Error/403'); |
234 | |
235 | $this->app->loadLanguageFromPath( |
236 | $response->header->l11n->language, |
237 | __DIR__ . '/../../../Web/Backend/Error/lang/' . $response->header->l11n->language . '.lang.php' |
238 | ); |
239 | |
240 | return $view; |
241 | } |
242 | |
243 | /** @var \phpOMS\Model\Html\Head $head */ |
244 | $head = $response->data['Content']->head; |
245 | $head->addAsset(AssetType::CSS, 'Modules/Tasks/Theme/Backend/css/styles.css?v=1.0.0'); |
246 | |
247 | /** @var \Modules\Tasks\Models\Task $task */ |
248 | $task = TaskMapper::get() |
249 | ->with('createdBy') |
250 | ->with('media') |
251 | ->with('tags') |
252 | ->with('tags/title') |
253 | ->with('taskElements') |
254 | ->with('taskElements/createdBy') |
255 | ->with('taskElements/media') |
256 | ->with('taskElements/accRelation') |
257 | ->with('taskElements/accRelation/relation') |
258 | ->where('id', (int) $request->getData('id')) |
259 | ->where('tags/title/language', $request->header->l11n->language) |
260 | ->execute(); |
261 | |
262 | $accountId = $request->header->account; |
263 | |
264 | if (!($task->createdBy->id === $accountId |
265 | || $task->isCCAccount($accountId) |
266 | || $task->isToAccount($accountId)) |
267 | && !$this->app->accountManager->get($accountId)->hasPermission( |
268 | PermissionType::READ, $this->app->unitId, $this->app->appId, self::NAME, PermissionCategory::TASK, $task->id) |
269 | ) { |
270 | $view->setTemplate('/Web/Backend/Error/403_inline'); |
271 | $response->header->status = RequestStatusCode::R_403; |
272 | return $view; |
273 | } |
274 | |
275 | $reminderStatus = []; |
276 | |
277 | // Set task as seen |
278 | if ($task->id !== 0) { |
279 | /** @var \Modules\Tasks\Models\TaskSeen[] $taskSeen */ |
280 | $taskSeen = TaskSeenMapper::getAll() |
281 | ->with('reminderBy') |
282 | ->where('task', $task->id) |
283 | ->where('seenBy', $request->header->account) |
284 | ->execute(); |
285 | |
286 | foreach ($taskSeen as $unseen) { |
287 | // Shows all reminders |
288 | if ($unseen->reminderBy !== null |
289 | && ($unseen->reminderAt?->getTimestamp() ?? 0) < $request->header->getRequestTime() |
290 | && ($unseen->reminderAt?->getTimestamp() ?? 0) > ($unseen->seenAt?->getTimestamp() ?? 0) - 300 |
291 | ) { |
292 | $reminderStatus[] = $unseen; |
293 | |
294 | if ($unseen->isRemindered) { |
295 | $new = clone $unseen; |
296 | $new->seenAt = new \DateTime('now'); |
297 | $new->isRemindered = false; |
298 | |
299 | $this->updateModel($request->header->account, $unseen, $new, TaskSeenMapper::class, 'reminder_seen', $request->getOrigin()); |
300 | } |
301 | } |
302 | } |
303 | |
304 | if (empty($taskSeen)) { |
305 | $taskSeen = new TaskSeen(); |
306 | $taskSeen->seenBy = $request->header->account; |
307 | $taskSeen->task = (int) $request->getData('id'); |
308 | |
309 | $this->createModel($request->header->account, $taskSeen, TaskSeenMapper::class, 'task_seen', $request->getOrigin()); |
310 | } |
311 | } |
312 | |
313 | $view->data['reminder'] = $reminderStatus; |
314 | |
315 | $view->setTemplate('/Modules/Tasks/Theme/Backend/task-single'); |
316 | $view->data['task'] = $task; |
317 | $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1001101001, $request, $response); |
318 | |
319 | $accGrpSelector = new \Modules\Profile\Theme\Backend\Components\AccountGroupSelector\BaseView($this->app->l11nManager, $request, $response); |
320 | $view->data['accGrpSelector'] = $accGrpSelector; |
321 | |
322 | $editor = new \Modules\Editor\Theme\Backend\Components\Editor\BaseView($this->app->l11nManager, $request, $response); |
323 | $view->data['editor'] = $editor; |
324 | |
325 | return $view; |
326 | } |
327 | |
328 | /** |
329 | * Routing end-point for application behaviour. |
330 | * |
331 | * @param RequestAbstract $request Request |
332 | * @param ResponseAbstract $response Response |
333 | * @param array $data Generic data |
334 | * |
335 | * @return RenderableInterface Returns a renderable object |
336 | * |
337 | * @since 1.0.0 |
338 | * @codeCoverageIgnore |
339 | */ |
340 | public function viewTaskCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface |
341 | { |
342 | $view = new View($this->app->l11nManager, $request, $response); |
343 | |
344 | $view->setTemplate('/Modules/Tasks/Theme/Backend/task-create'); |
345 | $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1001101001, $request, $response); |
346 | |
347 | $accGrpSelector = new \Modules\Profile\Theme\Backend\Components\AccountGroupSelector\BaseView($this->app->l11nManager, $request, $response); |
348 | $view->data['accGrpSelector'] = $accGrpSelector; |
349 | |
350 | $editor = new \Modules\Editor\Theme\Backend\Components\Editor\BaseView($this->app->l11nManager, $request, $response); |
351 | $view->data['editor'] = $editor; |
352 | |
353 | return $view; |
354 | } |
355 | |
356 | /** |
357 | * Routing end-point for application behaviour. |
358 | * |
359 | * @param RequestAbstract $request Request |
360 | * @param ResponseAbstract $response Response |
361 | * @param array $data Generic data |
362 | * |
363 | * @return RenderableInterface Returns a renderable object |
364 | * |
365 | * @since 1.0.0 |
366 | * @codeCoverageIgnore |
367 | */ |
368 | public function viewTaskAnalysis(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface |
369 | { |
370 | $view = new View($this->app->l11nManager, $request, $response); |
371 | $view->setTemplate('/Modules/Tasks/Theme/Backend/task-analysis'); |
372 | $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1001101001, $request, $response); |
373 | |
374 | return $view; |
375 | } |
376 | |
377 | /** |
378 | * Count unread messages |
379 | * |
380 | * @param int $account Account id |
381 | * |
382 | * @return int Returns the amount of unread tasks |
383 | * |
384 | * @since 1.0.0 |
385 | */ |
386 | public function openNav(int $account) : int |
387 | { |
388 | return TaskMapper::countUnread($account); |
389 | } |
390 | } |