Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 455 |
|
0.00% |
0 / 33 |
CRAP | |
0.00% |
0 / 1 |
ApiController | |
0.00% |
0 / 455 |
|
0.00% |
0 / 33 |
10920 | |
0.00% |
0 / 1 |
apiTaskReminderCreate | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
validateTaskReminderCreate | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
validateTaskCreate | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
createTaskReminderFromRequest | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
apiTaskCreate | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 | |||
createTaskMedia | |
0.00% |
0 / 92 |
|
0.00% |
0 / 1 |
90 | |||
createTaskDir | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
createTaskFromRequest | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
42 | |||
apiTaskGet | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
apiTaskSet | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
updateTaskFromRequest | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
validateTaskElementCreate | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
42 | |||
apiTaskElementCreate | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
42 | |||
createTaskElementMedia | |
0.00% |
0 / 85 |
|
0.00% |
0 / 1 |
56 | |||
createTaskElementFromRequest | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
30 | |||
apiTaskElementGet | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
apiTaskElementSet | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
30 | |||
updateTaskElementFromRequest | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
30 | |||
apiTaskAttributeCreate | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
createTaskAttributeFromRequest | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
validateTaskAttributeCreate | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
30 | |||
apiTaskAttributeTypeL11nCreate | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
createTaskAttributeTypeL11nFromRequest | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
validateTaskAttributeTypeL11nCreate | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
apiTaskAttributeTypeCreate | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
createTaskAttributeTypeFromRequest | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
validateTaskAttributeTypeCreate | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
apiTaskAttributeValueCreate | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
12 | |||
createTaskAttributeValueFromRequest | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 | |||
validateTaskAttributeValueCreate | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
apiTaskAttributeValueL11nCreate | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
createTaskAttributeValueL11nFromRequest | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
validateTaskAttributeValueL11nCreate | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 |
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\Admin\Models\AccountMapper; |
18 | use Modules\Admin\Models\NullAccount; |
19 | use Modules\Media\Models\CollectionMapper; |
20 | use Modules\Media\Models\MediaMapper; |
21 | use Modules\Media\Models\NullMedia; |
22 | use Modules\Media\Models\PathSettings; |
23 | use Modules\Media\Models\Reference; |
24 | use Modules\Media\Models\ReferenceMapper; |
25 | use Modules\Tag\Models\NullTag; |
26 | use Modules\Tasks\Models\NullTaskAttributeType; |
27 | use Modules\Tasks\Models\NullTaskAttributeValue; |
28 | use Modules\Tasks\Models\Task; |
29 | use Modules\Tasks\Models\TaskAttribute; |
30 | use Modules\Tasks\Models\TaskAttributeMapper; |
31 | use Modules\Tasks\Models\TaskAttributeType; |
32 | use Modules\Tasks\Models\TaskAttributeTypeL11nMapper; |
33 | use Modules\Tasks\Models\TaskAttributeTypeMapper; |
34 | use Modules\Tasks\Models\TaskAttributeValue; |
35 | use Modules\Tasks\Models\TaskAttributeValueL11nMapper; |
36 | use Modules\Tasks\Models\TaskAttributeValueMapper; |
37 | use Modules\Tasks\Models\TaskElement; |
38 | use Modules\Tasks\Models\TaskElementMapper; |
39 | use Modules\Tasks\Models\TaskMapper; |
40 | use Modules\Tasks\Models\TaskSeen; |
41 | use Modules\Tasks\Models\TaskSeenMapper; |
42 | use Modules\Tasks\Models\TaskStatus; |
43 | use Modules\Tasks\Models\TaskType; |
44 | use phpOMS\Localization\BaseStringL11n; |
45 | use phpOMS\Localization\ISO639x1Enum; |
46 | use phpOMS\Message\Http\HttpResponse; |
47 | use phpOMS\Message\Http\RequestStatusCode; |
48 | use phpOMS\Message\RequestAbstract; |
49 | use phpOMS\Message\ResponseAbstract; |
50 | use phpOMS\Utils\Parser\Markdown\Markdown; |
51 | |
52 | /** |
53 | * Api controller for the tasks module. |
54 | * |
55 | * @package Modules\Tasks |
56 | * @license OMS License 2.0 |
57 | * @link https://jingga.app |
58 | * @since 1.0.0 |
59 | */ |
60 | final class ApiController extends Controller |
61 | { |
62 | /** |
63 | * Api method to remind a task |
64 | * |
65 | * @param RequestAbstract $request Request |
66 | * @param ResponseAbstract $response Response |
67 | * @param array $data Generic data |
68 | * |
69 | * @return void |
70 | * |
71 | * @api |
72 | * |
73 | * @since 1.0.0 |
74 | */ |
75 | public function apiTaskReminderCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
76 | { |
77 | if (!empty($val = $this->validateTaskReminderCreate($request))) { |
78 | $response->header->status = RequestStatusCode::R_400; |
79 | $this->createInvalidCreateResponse($request, $response, $val); |
80 | |
81 | return; |
82 | } |
83 | |
84 | /** @var TaskSeen[] $reminder */ |
85 | $reminder = $this->createTaskReminderFromRequest($request); |
86 | $this->createModel($request->header->account, $reminder, TaskSeenMapper::class, 'reminder', $request->getOrigin()); |
87 | $this->createStandardCreateResponse($request, $response, $reminder); |
88 | } |
89 | |
90 | /** |
91 | * Validate reminder create request |
92 | * |
93 | * @param RequestAbstract $request Request |
94 | * |
95 | * @return array<string, bool> Returns the validation array of the request |
96 | * |
97 | * @since 1.0.0 |
98 | */ |
99 | private function validateTaskReminderCreate(RequestAbstract $request) : array |
100 | { |
101 | $val = []; |
102 | if (($val['id'] = !$request->hasData('id'))) { |
103 | return $val; |
104 | } |
105 | |
106 | return []; |
107 | } |
108 | |
109 | /** |
110 | * Validate task create request |
111 | * |
112 | * @param RequestAbstract $request Request |
113 | * |
114 | * @return array<string, bool> Returns the validation array of the request |
115 | * |
116 | * @since 1.0.0 |
117 | */ |
118 | private function validateTaskCreate(RequestAbstract $request) : array |
119 | { |
120 | $val = []; |
121 | if (($val['title'] = !$request->hasData('title')) |
122 | || ($val['plain'] = !$request->hasData('plain')) |
123 | ) { |
124 | return $val; |
125 | } |
126 | |
127 | return []; |
128 | } |
129 | |
130 | /** |
131 | * Method to create remeinder from request. |
132 | * |
133 | * @param RequestAbstract $request Request |
134 | * |
135 | * @return TaskSeen[] Returns the created reminders from the request |
136 | * |
137 | * @since 1.0.0 |
138 | */ |
139 | public function createTaskReminderFromRequest(RequestAbstract $request) : array |
140 | { |
141 | /** @var AccountRelation[] $responsible */ |
142 | $responsible = TaskMapper::getResponsible((int) $request->getData('id')); |
143 | |
144 | $reminder = []; |
145 | foreach ($responsible as $account) { |
146 | $unseen = new TaskSeen(); |
147 | $unseen->task = (int) $request->getData('id'); |
148 | $unseen->seenBy = $account->relation->id; |
149 | $unseen->reminderBy = new NullAccount($request->header->account); |
150 | $unseen->reminderAt = new \DateTime('now'); |
151 | |
152 | $remidner[] = $unseen; |
153 | } |
154 | |
155 | return $reminder; |
156 | } |
157 | |
158 | /** |
159 | * Api method to create a task |
160 | * |
161 | * @param RequestAbstract $request Request |
162 | * @param ResponseAbstract $response Response |
163 | * @param array $data Generic data |
164 | * |
165 | * @return void |
166 | * |
167 | * @api |
168 | * |
169 | * @since 1.0.0 |
170 | */ |
171 | public function apiTaskCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
172 | { |
173 | if (!empty($val = $this->validateTaskCreate($request))) { |
174 | $response->header->status = RequestStatusCode::R_400; |
175 | $this->createInvalidCreateResponse($request, $response, $val); |
176 | |
177 | return; |
178 | } |
179 | |
180 | /** @var Task $task */ |
181 | $task = $this->createTaskFromRequest($request); |
182 | $this->createModel($request->header->account, $task, TaskMapper::class, 'task', $request->getOrigin()); |
183 | |
184 | if (!empty($request->files) |
185 | || !empty($request->getDataJson('media')) |
186 | ) { |
187 | $this->createTaskMedia($task, $request); |
188 | } |
189 | |
190 | $this->createStandardCreateResponse($request, $response, $task); |
191 | } |
192 | |
193 | /** |
194 | * Create media files for task |
195 | * |
196 | * @param Task $task Task |
197 | * @param RequestAbstract $request Request incl. media do upload |
198 | * |
199 | * @return void |
200 | * |
201 | * @since 1.0.0 |
202 | */ |
203 | private function createTaskMedia(Task $task, RequestAbstract $request) : void |
204 | { |
205 | $path = $this->createTaskDir($task); |
206 | |
207 | /** @var \Modules\Admin\Models\Account $account */ |
208 | $account = AccountMapper::get()->where('id', $request->header->account)->execute(); |
209 | |
210 | if (!empty($uploadedFiles = $request->files)) { |
211 | $uploaded = $this->app->moduleManager->get('Media')->uploadFiles( |
212 | names: [], |
213 | fileNames: [], |
214 | files: $uploadedFiles, |
215 | account: $request->header->account, |
216 | basePath: __DIR__ . '/../../../Modules/Media/Files' . $path, |
217 | virtualPath: $path, |
218 | pathSettings: PathSettings::FILE_PATH |
219 | ); |
220 | |
221 | $collection = null; |
222 | foreach ($uploaded as $media) { |
223 | $this->createModelRelation( |
224 | $request->header->account, |
225 | $task->id, |
226 | $media->id, |
227 | TaskMapper::class, |
228 | 'media', |
229 | '', |
230 | $request->getOrigin() |
231 | ); |
232 | |
233 | $accountPath = '/Accounts/' |
234 | . $account->id . ' ' |
235 | . $account->login . '/Tasks/' |
236 | . $task->createdAt->format('Y') . '/' |
237 | . $task->createdAt->format('m') . '/' |
238 | . $task->id; |
239 | |
240 | $ref = new Reference(); |
241 | $ref->name = $media->name; |
242 | $ref->source = new NullMedia($media->id); |
243 | $ref->createdBy = new NullAccount($request->header->account); |
244 | $ref->setVirtualPath($accountPath); |
245 | |
246 | $this->createModel($request->header->account, $ref, ReferenceMapper::class, 'media_reference', $request->getOrigin()); |
247 | |
248 | if ($collection === null) { |
249 | /** @var \Modules\Media\Models\Collection $collection */ |
250 | $collection = MediaMapper::getParentCollection($path)->limit(1)->execute(); |
251 | |
252 | if ($collection->id === 0) { |
253 | $collection = $this->app->moduleManager->get('Media')->createRecursiveMediaCollection( |
254 | $accountPath, |
255 | $request->header->account, |
256 | __DIR__ . '/../../../Modules/Media/Files/Accounts/' |
257 | . $account->id . '/Tasks/' |
258 | . $task->createdAt->format('Y') . '/' |
259 | . $task->createdAt->format('m') . '/' |
260 | . $task->id |
261 | ); |
262 | } |
263 | } |
264 | |
265 | $this->createModelRelation( |
266 | $request->header->account, |
267 | $collection->id, |
268 | $ref->id, |
269 | CollectionMapper::class, |
270 | 'sources', |
271 | '', |
272 | $request->getOrigin() |
273 | ); |
274 | } |
275 | } |
276 | |
277 | if (!empty($mediaFiles = $request->getDataJson('media'))) { |
278 | $collection = null; |
279 | |
280 | foreach ($mediaFiles as $file) { |
281 | /** @var \Modules\Media\Models\Media $media */ |
282 | $media = MediaMapper::get()->where('id', (int) $file)->limit(1)->execute(); |
283 | |
284 | $this->createModelRelation( |
285 | $request->header->account, |
286 | $task->id, |
287 | $media->id, |
288 | TaskMapper::class, |
289 | 'media', |
290 | '', |
291 | $request->getOrigin() |
292 | ); |
293 | |
294 | $ref = new Reference(); |
295 | $ref->name = $media->name; |
296 | $ref->source = new NullMedia($media->id); |
297 | $ref->createdBy = new NullAccount($request->header->account); |
298 | $ref->setVirtualPath($path); |
299 | |
300 | $this->createModel($request->header->account, $ref, ReferenceMapper::class, 'media_reference', $request->getOrigin()); |
301 | |
302 | if ($collection === null) { |
303 | /** @var \Modules\Media\Models\Collection $collection */ |
304 | $collection = MediaMapper::getParentCollection($path)->limit(1)->execute(); |
305 | |
306 | if ($collection->id === 0) { |
307 | $collection = $this->app->moduleManager->get('Media')->createRecursiveMediaCollection( |
308 | $path, |
309 | $request->header->account, |
310 | __DIR__ . '/../../../Modules/Media/Files' . $path |
311 | ); |
312 | } |
313 | } |
314 | |
315 | $this->createModelRelation( |
316 | $request->header->account, |
317 | $collection->id, |
318 | $ref->id, |
319 | CollectionMapper::class, |
320 | 'sources', |
321 | '', |
322 | $request->getOrigin() |
323 | ); |
324 | } |
325 | } |
326 | } |
327 | |
328 | /** |
329 | * Create media directory path |
330 | * |
331 | * @param Task $task Task |
332 | * |
333 | * @return string |
334 | * |
335 | * @since 1.0.0 |
336 | */ |
337 | private function createTaskDir(Task $task) : string |
338 | { |
339 | return '/Modules/Tasks/' |
340 | . $task->createdAt->format('Y') . '/' |
341 | . $task->createdAt->format('m') . '/' |
342 | . $task->createdAt->format('d') . '/' |
343 | . $task->id; |
344 | } |
345 | |
346 | /** |
347 | * Method to create task from request. |
348 | * |
349 | * @param RequestAbstract $request Request |
350 | * |
351 | * @return Task Returns the created task from the request |
352 | * |
353 | * @since 1.0.0 |
354 | */ |
355 | public function createTaskFromRequest(RequestAbstract $request) : Task |
356 | { |
357 | $task = new Task(); |
358 | $task->title = $request->getDataString('title') ?? ''; |
359 | $task->description = Markdown::parse($request->getDataString('plain') ?? ''); |
360 | $task->descriptionRaw = $request->getDataString('plain') ?? ''; |
361 | $task->setCreatedBy(new NullAccount($request->header->account)); |
362 | $task->setStatus(TaskStatus::OPEN); |
363 | $task->setType(TaskType::SINGLE); |
364 | $task->redirect = $request->getDataString('redirect') ?? ''; |
365 | |
366 | if (!$request->hasData('priority')) { |
367 | $task->due = $request->getDataDateTime('due'); |
368 | } else { |
369 | $task->setPriority((int) $request->getData('priority')); |
370 | } |
371 | |
372 | if (!empty($tags = $request->getDataJson('tags'))) { |
373 | foreach ($tags as $tag) { |
374 | if (!isset($tag['id'])) { |
375 | $request->setData('title', $tag['title'], true); |
376 | $request->setData('color', $tag['color'], true); |
377 | $request->setData('icon', $tag['icon'] ?? null, true); |
378 | $request->setData('language', $tag['language'], true); |
379 | |
380 | $internalResponse = new HttpResponse(); |
381 | $this->app->moduleManager->get('Tag')->apiTagCreate($request, $internalResponse); |
382 | |
383 | if (!\is_array($data = $internalResponse->getDataArray($request->uri->__toString()))) { |
384 | continue; |
385 | } |
386 | |
387 | $task->addTag($data['response']); |
388 | } else { |
389 | $task->addTag(new NullTag((int) $tag['id'])); |
390 | } |
391 | } |
392 | } |
393 | |
394 | $element = new TaskElement(); |
395 | $element->addTo(new NullAccount($request->getDataInt('forward') ?? $request->header->account)); |
396 | $element->createdBy = $task->getCreatedBy(); |
397 | $element->due = $task->due; |
398 | $element->setPriority($task->getPriority()); |
399 | $element->setStatus(TaskStatus::OPEN); |
400 | |
401 | $task->addElement($element); |
402 | |
403 | return $task; |
404 | } |
405 | |
406 | /** |
407 | * Api method to get a task |
408 | * |
409 | * @param RequestAbstract $request Request |
410 | * @param ResponseAbstract $response Response |
411 | * @param array $data Generic data |
412 | * |
413 | * @return void |
414 | * |
415 | * @api |
416 | * |
417 | * @since 1.0.0 |
418 | */ |
419 | public function apiTaskGet(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
420 | { |
421 | /** @var Task $task */ |
422 | $task = TaskMapper::get()->where('id', (int) $request->getData('id'))->execute(); |
423 | $this->createStandardReturnResponse($request, $response, $task); |
424 | } |
425 | |
426 | /** |
427 | * Api method to update a task |
428 | * |
429 | * @param RequestAbstract $request Request |
430 | * @param ResponseAbstract $response Response |
431 | * @param array $data Generic data |
432 | * |
433 | * @return void |
434 | * |
435 | * @api |
436 | * |
437 | * @since 1.0.0 |
438 | */ |
439 | public function apiTaskSet(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
440 | { |
441 | /** @var Task $old */ |
442 | $old = TaskMapper::get()->where('id', (int) $request->getData('id'))->execute(); |
443 | |
444 | /** @var Task $new */ |
445 | $new = $this->updateTaskFromRequest($request, clone $old); |
446 | $this->updateModel($request->header->account, $old, $new, TaskMapper::class, 'task', $request->getOrigin()); |
447 | |
448 | if (!empty($new->trigger)) { |
449 | $this->app->eventManager->triggerSimilar($new->trigger, '', $new); |
450 | } |
451 | |
452 | $this->createStandardUpdateResponse($request, $response, $new); |
453 | } |
454 | |
455 | /** |
456 | * Method to update an task from a request |
457 | * |
458 | * @param RequestAbstract $request Request |
459 | * |
460 | * @return Task Returns the updated task from the request |
461 | * |
462 | * @since 1.0.0 |
463 | */ |
464 | private function updateTaskFromRequest(RequestAbstract $request, Task $task) : Task |
465 | { |
466 | $task->title = $request->getDataString('title') ?? $task->title; |
467 | $task->description = Markdown::parse($request->getDataString('plain') ?? $task->descriptionRaw); |
468 | $task->descriptionRaw = $request->getDataString('plain') ?? $task->descriptionRaw; |
469 | $task->due = $request->hasData('due') ? new \DateTime($request->getDataString('due') ?? 'now') : $task->due; |
470 | $task->setStatus($request->getDataInt('status') ?? $task->getStatus()); |
471 | $task->setType($request->getDataInt('type') ?? $task->getType()); |
472 | $task->setPriority($request->getDataInt('priority') ?? $task->getPriority()); |
473 | $task->completion = $request->getDataInt('completion') ?? $task->completion; |
474 | |
475 | return $task; |
476 | } |
477 | |
478 | /** |
479 | * Validate task element create request |
480 | * |
481 | * @param RequestAbstract $request Request |
482 | * |
483 | * @return array<string, bool> Returns the validation array of the request |
484 | * |
485 | * @since 1.0.0 |
486 | */ |
487 | private function validateTaskElementCreate(RequestAbstract $request) : array |
488 | { |
489 | $val = []; |
490 | if (($val['status'] = !TaskStatus::isValidValue((int) $request->getData('status'))) |
491 | || ($val['due'] = !((bool) \strtotime((string) $request->getData('due')))) |
492 | || ($val['task'] = !(\is_numeric($request->getData('task')))) |
493 | || ($val['forward'] = !(\is_numeric($request->hasData('forward') ? $request->getData('forward') : $request->header->account))) |
494 | ) { |
495 | return $val; |
496 | } |
497 | |
498 | return []; |
499 | } |
500 | |
501 | /** |
502 | * Api method to create a task element |
503 | * |
504 | * @param RequestAbstract $request Request |
505 | * @param ResponseAbstract $response Response |
506 | * @param array $data Generic data |
507 | * |
508 | * @return void |
509 | * |
510 | * @api |
511 | * |
512 | * @since 1.0.0 |
513 | */ |
514 | public function apiTaskElementCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
515 | { |
516 | if (!empty($val = $this->validateTaskElementCreate($request))) { |
517 | $response->header->status = RequestStatusCode::R_400; |
518 | $this->createInvalidCreateResponse($request, $response, $val); |
519 | |
520 | return; |
521 | } |
522 | |
523 | /** @var \Modules\Tasks\Models\Task $task */ |
524 | $task = TaskMapper::get()->where('id', (int) ($request->getData('task')))->execute(); |
525 | $element = $this->createTaskElementFromRequest($request, $task); |
526 | |
527 | $task->due = $element->due; |
528 | $task->completion = $request->getDataInt('completion') ?? $task->completion; |
529 | $task->setPriority($element->getPriority()); |
530 | $task->setStatus($element->getStatus()); |
531 | |
532 | if ($task->getStatus() === TaskStatus::DONE) { |
533 | $task->completion = 100; |
534 | } |
535 | |
536 | $this->createModel($request->header->account, $element, TaskElementMapper::class, 'taskelement', $request->getOrigin()); |
537 | |
538 | if (!empty($request->files) |
539 | || !empty($request->getDataJson('media')) |
540 | ) { |
541 | $this->createTaskElementMedia($task, $element, $request); |
542 | } |
543 | |
544 | $this->updateModel($request->header->account, $task, $task, TaskMapper::class, 'task', $request->getOrigin()); |
545 | |
546 | if (!empty($task->trigger)) { |
547 | $this->app->eventManager->triggerSimilar($task->trigger, '', $task); |
548 | } |
549 | |
550 | $this->createStandardCreateResponse($request, $response, $element); |
551 | } |
552 | |
553 | /** |
554 | * Create media files for task element |
555 | * |
556 | * @param Task $task Task |
557 | * @param TaskElement $element Task element |
558 | * @param RequestAbstract $request Request incl. media do upload |
559 | * |
560 | * @return void |
561 | * |
562 | * @since 1.0.0 |
563 | */ |
564 | private function createTaskElementMedia(Task $task, TaskElement $element, RequestAbstract $request) : void |
565 | { |
566 | $path = $this->createTaskDir($task); |
567 | |
568 | /** @var \Modules\Admin\Models\Account $account */ |
569 | $account = AccountMapper::get()->where('id', $request->header->account)->execute(); |
570 | |
571 | if (!empty($uploadedFiles = $request->files)) { |
572 | $uploaded = $this->app->moduleManager->get('Media')->uploadFiles( |
573 | [], |
574 | [], |
575 | $uploadedFiles, |
576 | $request->header->account, |
577 | __DIR__ . '/../../../Modules/Media/Files' . $path, |
578 | $path, |
579 | ); |
580 | |
581 | $collection = null; |
582 | foreach ($uploaded as $media) { |
583 | $this->createModelRelation( |
584 | $request->header->account, |
585 | $element->id, |
586 | $media->id, |
587 | TaskElementMapper::class, |
588 | 'media', |
589 | '', |
590 | $request->getOrigin() |
591 | ); |
592 | |
593 | $accountPath = '/Accounts/' . $account->id . ' ' |
594 | . $account->login . '/Tasks/' |
595 | . $task->createdAt->format('Y') . '/' |
596 | . $task->createdAt->format('m') . '/' |
597 | . $task->id; |
598 | |
599 | $ref = new Reference(); |
600 | $ref->name = $media->name; |
601 | $ref->source = new NullMedia($media->id); |
602 | $ref->createdBy = new NullAccount($request->header->account); |
603 | $ref->setVirtualPath($accountPath); |
604 | |
605 | $this->createModel($request->header->account, $ref, ReferenceMapper::class, 'media_reference', $request->getOrigin()); |
606 | |
607 | if ($collection === null) { |
608 | $collection = $this->app->moduleManager->get('Media')->createRecursiveMediaCollection( |
609 | $accountPath, |
610 | $request->header->account, |
611 | __DIR__ . '/../../../Modules/Media/Files/Accounts/' . $account->id |
612 | . '/Tasks/' . $task->createdAt->format('Y') . '/' |
613 | . $task->createdAt->format('m') . '/' |
614 | . $task->id |
615 | ); |
616 | } |
617 | |
618 | $this->createModelRelation( |
619 | $request->header->account, |
620 | $collection->id, |
621 | $ref->id, |
622 | CollectionMapper::class, |
623 | 'sources', |
624 | '', |
625 | $request->getOrigin() |
626 | ); |
627 | } |
628 | } |
629 | |
630 | if (!empty($mediaFiles = $request->getDataJson('media'))) { |
631 | $collection = null; |
632 | |
633 | foreach ($mediaFiles as $file) { |
634 | /** @var \Modules\Media\Models\Media $media */ |
635 | $media = MediaMapper::get()->where('id', (int) $file)->limit(1)->execute(); |
636 | |
637 | $this->createModelRelation( |
638 | $request->header->account, |
639 | $element->id, |
640 | $media->id, |
641 | TaskElementMapper::class, |
642 | 'media', |
643 | '', |
644 | $request->getOrigin() |
645 | ); |
646 | |
647 | $ref = new Reference(); |
648 | $ref->name = $media->name; |
649 | $ref->source = new NullMedia($media->id); |
650 | $ref->createdBy = new NullAccount($request->header->account); |
651 | $ref->setVirtualPath($path); |
652 | |
653 | $this->createModel($request->header->account, $ref, ReferenceMapper::class, 'media_reference', $request->getOrigin()); |
654 | |
655 | if ($collection === null) { |
656 | $collection = $this->app->moduleManager->get('Media')->createRecursiveMediaCollection( |
657 | $path, |
658 | $request->header->account, |
659 | __DIR__ . '/../../../Modules/Media/Files' . $path |
660 | ); |
661 | } |
662 | |
663 | $this->createModelRelation( |
664 | $request->header->account, |
665 | $collection->id, |
666 | $ref->id, |
667 | CollectionMapper::class, |
668 | 'sources', |
669 | '', |
670 | $request->getOrigin() |
671 | ); |
672 | } |
673 | } |
674 | } |
675 | |
676 | /** |
677 | * Method to create task element from request. |
678 | * |
679 | * @param RequestAbstract $request Request |
680 | * @param Task $task Task |
681 | * |
682 | * @return TaskElement Returns the task created from the request |
683 | * |
684 | * @since 1.0.0 |
685 | */ |
686 | public function createTaskElementFromRequest(RequestAbstract $request, Task $task) : TaskElement |
687 | { |
688 | $element = new TaskElement(); |
689 | $element->createdBy = new NullAccount($request->header->account); |
690 | $element->due = $request->getDataDateTime('due') ?? $task->due; |
691 | $element->setPriority($request->getDataInt('priority') ?? $task->getPriority()); |
692 | $element->setStatus((int) ($request->getData('status'))); |
693 | $element->task = $task->id; |
694 | $element->description = Markdown::parse($request->getDataString('plain') ?? ''); |
695 | $element->descriptionRaw = $request->getDataString('plain') ?? ''; |
696 | |
697 | $tos = $request->getData('to') ?? $request->header->account; |
698 | if (!\is_array($tos)) { |
699 | $tos = [$tos]; |
700 | } |
701 | |
702 | $ccs = $request->getData('cc') ?? []; |
703 | if (!\is_array($ccs)) { |
704 | $ccs = [$ccs]; |
705 | } |
706 | |
707 | foreach ($tos as $to) { |
708 | $element->addTo(new NullAccount((int) $to)); |
709 | } |
710 | |
711 | foreach ($ccs as $cc) { |
712 | $element->addCC(new NullAccount((int) $cc)); |
713 | } |
714 | |
715 | return $element; |
716 | } |
717 | |
718 | /** |
719 | * Api method to get a task |
720 | * |
721 | * @param RequestAbstract $request Request |
722 | * @param ResponseAbstract $response Response |
723 | * @param array $data Generic data |
724 | * |
725 | * @return void |
726 | * |
727 | * @api |
728 | * |
729 | * @since 1.0.0 |
730 | */ |
731 | public function apiTaskElementGet(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
732 | { |
733 | /** @var TaskElement $task */ |
734 | $task = TaskElementMapper::get()->where('id', (int) $request->getData('id'))->execute(); |
735 | $this->createStandardReturnResponse($request, $response, $task); |
736 | } |
737 | |
738 | /** |
739 | * Api method to update a task element |
740 | * |
741 | * @param RequestAbstract $request Request |
742 | * @param ResponseAbstract $response Response |
743 | * @param array $data Generic data |
744 | * |
745 | * @return void |
746 | * |
747 | * @api |
748 | * |
749 | * @since 1.0.0 |
750 | */ |
751 | public function apiTaskElementSet(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
752 | { |
753 | /** @var TaskElement $old */ |
754 | $old = TaskElementMapper::get()->where('id', (int) $request->getData('id'))->execute(); |
755 | |
756 | /** @var TaskElement $new */ |
757 | $new = $this->updateTaskElementFromRequest($request, clone $old); |
758 | $this->updateModel($request->header->account, $old, $new, TaskElementMapper::class, 'taskelement', $request->getOrigin()); |
759 | |
760 | if ($old->getStatus() !== $new->getStatus() |
761 | || $old->getPriority() !== $new->getPriority() |
762 | || $old->due !== $new->due |
763 | ) { |
764 | /** @var Task $task */ |
765 | $task = TaskMapper::get()->where('id', $new->task)->execute(); |
766 | |
767 | $task->setStatus($new->getStatus()); |
768 | $task->setPriority($new->getPriority()); |
769 | $task->due = $new->due; |
770 | |
771 | $this->updateModel($request->header->account, $task, $task, TaskMapper::class, 'task', $request->getOrigin()); |
772 | |
773 | if (!empty($task->trigger)) { |
774 | $this->app->eventManager->triggerSimilar($task->trigger, '', $task); |
775 | } |
776 | } |
777 | |
778 | $this->createStandardUpdateResponse($request, $response, $new); |
779 | } |
780 | |
781 | /** |
782 | * Method to update an task element from a request |
783 | * |
784 | * @param RequestAbstract $request Request |
785 | * |
786 | * @return TaskElement Returns the updated task element from the request |
787 | * |
788 | * @since 1.0.0 |
789 | */ |
790 | private function updateTaskElementFromRequest(RequestAbstract $request, TaskElement $element) : TaskElement |
791 | { |
792 | $element->due = $request->getDataDateTime('due') ?? $element->due; |
793 | $element->setStatus($request->getDataInt('status') ?? $element->getStatus()); |
794 | $element->description = Markdown::parse($request->getDataString('plain') ?? $element->descriptionRaw); |
795 | $element->descriptionRaw = $request->getDataString('plain') ?? $element->descriptionRaw; |
796 | |
797 | $tos = $request->getData('to') ?? $request->header->account; |
798 | if (!\is_array($tos)) { |
799 | $tos = [$tos]; |
800 | } |
801 | |
802 | $ccs = $request->getData('cc') ?? []; |
803 | if (!\is_array($ccs)) { |
804 | $ccs = [$ccs]; |
805 | } |
806 | |
807 | foreach ($tos as $to) { |
808 | $element->addTo($to); |
809 | } |
810 | |
811 | foreach ($ccs as $cc) { |
812 | $element->addCC($cc); |
813 | } |
814 | |
815 | return $element; |
816 | } |
817 | |
818 | /** |
819 | * Api method to create task attribute |
820 | * |
821 | * @param RequestAbstract $request Request |
822 | * @param ResponseAbstract $response Response |
823 | * @param array $data Generic data |
824 | * |
825 | * @return void |
826 | * |
827 | * @api |
828 | * |
829 | * @since 1.0.0 |
830 | */ |
831 | public function apiTaskAttributeCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
832 | { |
833 | if (!empty($val = $this->validateTaskAttributeCreate($request))) { |
834 | $response->header->status = RequestStatusCode::R_400; |
835 | $this->createInvalidCreateResponse($request, $response, $val); |
836 | |
837 | return; |
838 | } |
839 | |
840 | $attribute = $this->createTaskAttributeFromRequest($request); |
841 | $this->createModel($request->header->account, $attribute, TaskAttributeMapper::class, 'attribute', $request->getOrigin()); |
842 | $this->createStandardCreateResponse($request, $response, $attribute); |
843 | } |
844 | |
845 | /** |
846 | * Method to create task attribute from request. |
847 | * |
848 | * @param RequestAbstract $request Request |
849 | * |
850 | * @return TaskAttribute |
851 | * |
852 | * @since 1.0.0 |
853 | */ |
854 | private function createTaskAttributeFromRequest(RequestAbstract $request) : TaskAttribute |
855 | { |
856 | $attribute = new TaskAttribute(); |
857 | $attribute->task = (int) $request->getData('task'); |
858 | $attribute->type = new NullTaskAttributeType((int) $request->getData('type')); |
859 | |
860 | if ($request->hasData('value_id')) { |
861 | $attribute->value = new NullTaskAttributeValue((int) $request->getData('value_id')); |
862 | } else { |
863 | $newRequest = clone $request; |
864 | $newRequest->setData('value', $request->getData('value'), true); |
865 | |
866 | $value = $this->createTaskAttributeValueFromRequest($request); |
867 | |
868 | $attribute->value = $value; |
869 | } |
870 | |
871 | return $attribute; |
872 | } |
873 | |
874 | /** |
875 | * Validate task attribute create request |
876 | * |
877 | * @param RequestAbstract $request Request |
878 | * |
879 | * @return array<string, bool> |
880 | * |
881 | * @since 1.0.0 |
882 | */ |
883 | private function validateTaskAttributeCreate(RequestAbstract $request) : array |
884 | { |
885 | $val = []; |
886 | if (($val['type'] = !$request->hasData('type')) |
887 | || ($val['value'] = (!$request->hasData('value') && !$request->hasData('custom'))) |
888 | || ($val['task'] = !$request->hasData('task')) |
889 | ) { |
890 | return $val; |
891 | } |
892 | |
893 | return []; |
894 | } |
895 | |
896 | /** |
897 | * Api method to create task attribute l11n |
898 | * |
899 | * @param RequestAbstract $request Request |
900 | * @param ResponseAbstract $response Response |
901 | * @param array $data Generic data |
902 | * |
903 | * @return void |
904 | * |
905 | * @api |
906 | * |
907 | * @since 1.0.0 |
908 | */ |
909 | public function apiTaskAttributeTypeL11nCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
910 | { |
911 | if (!empty($val = $this->validateTaskAttributeTypeL11nCreate($request))) { |
912 | $response->header->status = RequestStatusCode::R_400; |
913 | $this->createInvalidCreateResponse($request, $response, $val); |
914 | |
915 | return; |
916 | } |
917 | |
918 | $attrL11n = $this->createTaskAttributeTypeL11nFromRequest($request); |
919 | $this->createModel($request->header->account, $attrL11n, TaskAttributeTypeL11nMapper::class, 'attr_type_l11n', $request->getOrigin()); |
920 | $this->createStandardCreateResponse($request, $response, $attrL11n); |
921 | } |
922 | |
923 | /** |
924 | * Method to create task attribute l11n from request. |
925 | * |
926 | * @param RequestAbstract $request Request |
927 | * |
928 | * @return BaseStringL11n |
929 | * |
930 | * @since 1.0.0 |
931 | */ |
932 | private function createTaskAttributeTypeL11nFromRequest(RequestAbstract $request) : BaseStringL11n |
933 | { |
934 | $attrL11n = new BaseStringL11n(); |
935 | $attrL11n->ref = $request->getDataInt('type') ?? 0; |
936 | $attrL11n->setLanguage( |
937 | $request->getDataString('language') ?? $request->header->l11n->language |
938 | ); |
939 | $attrL11n->content = $request->getDataString('title') ?? ''; |
940 | |
941 | return $attrL11n; |
942 | } |
943 | |
944 | /** |
945 | * Validate task attribute l11n create request |
946 | * |
947 | * @param RequestAbstract $request Request |
948 | * |
949 | * @return array<string, bool> |
950 | * |
951 | * @since 1.0.0 |
952 | */ |
953 | private function validateTaskAttributeTypeL11nCreate(RequestAbstract $request) : array |
954 | { |
955 | $val = []; |
956 | if (($val['title'] = !$request->hasData('title')) |
957 | || ($val['type'] = !$request->hasData('type')) |
958 | ) { |
959 | return $val; |
960 | } |
961 | |
962 | return []; |
963 | } |
964 | |
965 | /** |
966 | * Api method to create task attribute type |
967 | * |
968 | * @param RequestAbstract $request Request |
969 | * @param ResponseAbstract $response Response |
970 | * @param array $data Generic data |
971 | * |
972 | * @return void |
973 | * |
974 | * @api |
975 | * |
976 | * @since 1.0.0 |
977 | */ |
978 | public function apiTaskAttributeTypeCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
979 | { |
980 | if (!empty($val = $this->validateTaskAttributeTypeCreate($request))) { |
981 | $response->header->status = RequestStatusCode::R_400; |
982 | $this->createInvalidCreateResponse($request, $response, $val); |
983 | |
984 | return; |
985 | } |
986 | |
987 | $attrType = $this->createTaskAttributeTypeFromRequest($request); |
988 | $this->createModel($request->header->account, $attrType, TaskAttributeTypeMapper::class, 'attr_type', $request->getOrigin()); |
989 | $this->createStandardCreateResponse($request, $response, $attrType); |
990 | } |
991 | |
992 | /** |
993 | * Method to create task attribute from request. |
994 | * |
995 | * @param RequestAbstract $request Request |
996 | * |
997 | * @return TaskAttributeType |
998 | * |
999 | * @since 1.0.0 |
1000 | */ |
1001 | private function createTaskAttributeTypeFromRequest(RequestAbstract $request) : TaskAttributeType |
1002 | { |
1003 | $attrType = new TaskAttributeType($request->getDataString('name') ?? ''); |
1004 | $attrType->setL11n($request->getDataString('title') ?? '', $request->getDataString('language') ?? ISO639x1Enum::_EN); |
1005 | $attrType->setFields($request->getDataInt('fields') ?? 0); |
1006 | $attrType->custom = $request->getDataBool('custom') ?? false; |
1007 | $attrType->isRequired = $request->getDataBool('is_required') ?? false; |
1008 | $attrType->validationPattern = $request->getDataString('validation_pattern') ?? ''; |
1009 | |
1010 | return $attrType; |
1011 | } |
1012 | |
1013 | /** |
1014 | * Validate task attribute create request |
1015 | * |
1016 | * @param RequestAbstract $request Request |
1017 | * |
1018 | * @return array<string, bool> |
1019 | * |
1020 | * @since 1.0.0 |
1021 | */ |
1022 | private function validateTaskAttributeTypeCreate(RequestAbstract $request) : array |
1023 | { |
1024 | $val = []; |
1025 | if (($val['title'] = !$request->hasData('title')) |
1026 | || ($val['name'] = !$request->hasData('name')) |
1027 | ) { |
1028 | return $val; |
1029 | } |
1030 | |
1031 | return []; |
1032 | } |
1033 | |
1034 | /** |
1035 | * Api method to create task attribute value |
1036 | * |
1037 | * @param RequestAbstract $request Request |
1038 | * @param ResponseAbstract $response Response |
1039 | * @param array $data Generic data |
1040 | * |
1041 | * @return void |
1042 | * |
1043 | * @api |
1044 | * |
1045 | * @since 1.0.0 |
1046 | */ |
1047 | public function apiTaskAttributeValueCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
1048 | { |
1049 | if (!empty($val = $this->validateTaskAttributeValueCreate($request))) { |
1050 | $response->header->status = RequestStatusCode::R_400; |
1051 | $this->createInvalidCreateResponse($request, $response, $val); |
1052 | |
1053 | return; |
1054 | } |
1055 | |
1056 | $attrValue = $this->createTaskAttributeValueFromRequest($request); |
1057 | $this->createModel($request->header->account, $attrValue, TaskAttributeValueMapper::class, 'attr_value', $request->getOrigin()); |
1058 | |
1059 | if ($attrValue->isDefault) { |
1060 | $this->createModelRelation( |
1061 | $request->header->account, |
1062 | (int) $request->getData('attributetype'), |
1063 | $attrValue->id, |
1064 | TaskAttributeTypeMapper::class, 'defaults', '', $request->getOrigin() |
1065 | ); |
1066 | } |
1067 | |
1068 | $this->createStandardCreateResponse($request, $response, $attrValue); |
1069 | } |
1070 | |
1071 | /** |
1072 | * Method to create task attribute value from request. |
1073 | * |
1074 | * @param RequestAbstract $request Request |
1075 | * |
1076 | * @return TaskAttributeValue |
1077 | * |
1078 | * @since 1.0.0 |
1079 | */ |
1080 | private function createTaskAttributeValueFromRequest(RequestAbstract $request) : TaskAttributeValue |
1081 | { |
1082 | /** @var TaskAttributeType $type */ |
1083 | $type = TaskAttributeTypeMapper::get() |
1084 | ->where('id', $request->getDataInt('type') ?? 0) |
1085 | ->execute(); |
1086 | |
1087 | $attrValue = new TaskAttributeValue(); |
1088 | $attrValue->isDefault = $request->getDataBool('default') ?? false; |
1089 | $attrValue->setValue($request->getDataString('value'), $type->datatype); |
1090 | |
1091 | if ($request->hasData('title')) { |
1092 | $attrValue->setL11n( |
1093 | $request->getDataString('title') ?? '', |
1094 | $request->getDataString('language') ?? ISO639x1Enum::_EN |
1095 | ); |
1096 | } |
1097 | |
1098 | return $attrValue; |
1099 | } |
1100 | |
1101 | /** |
1102 | * Validate task attribute value create request |
1103 | * |
1104 | * @param RequestAbstract $request Request |
1105 | * |
1106 | * @return array<string, bool> |
1107 | * |
1108 | * @since 1.0.0 |
1109 | */ |
1110 | private function validateTaskAttributeValueCreate(RequestAbstract $request) : array |
1111 | { |
1112 | $val = []; |
1113 | if (($val['attributetype'] = !$request->hasData('attributetype')) |
1114 | || ($val['value'] = !$request->hasData('value')) |
1115 | ) { |
1116 | return $val; |
1117 | } |
1118 | |
1119 | return []; |
1120 | } |
1121 | |
1122 | /** |
1123 | * Api method to create task attribute l11n |
1124 | * |
1125 | * @param RequestAbstract $request Request |
1126 | * @param ResponseAbstract $response Response |
1127 | * @param array $data Generic data |
1128 | * |
1129 | * @return void |
1130 | * |
1131 | * @api |
1132 | * |
1133 | * @since 1.0.0 |
1134 | */ |
1135 | public function apiTaskAttributeValueL11nCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void |
1136 | { |
1137 | if (!empty($val = $this->validateTaskAttributeValueL11nCreate($request))) { |
1138 | $response->header->status = RequestStatusCode::R_400; |
1139 | $this->createInvalidCreateResponse($request, $response, $val); |
1140 | |
1141 | return; |
1142 | } |
1143 | |
1144 | $attrL11n = $this->createTaskAttributeValueL11nFromRequest($request); |
1145 | $this->createModel($request->header->account, $attrL11n, TaskAttributeValueL11nMapper::class, 'attr_value_l11n', $request->getOrigin()); |
1146 | $this->createStandardCreateResponse($request, $response, $attrL11n); |
1147 | } |
1148 | |
1149 | /** |
1150 | * Method to create task attribute l11n from request. |
1151 | * |
1152 | * @param RequestAbstract $request Request |
1153 | * |
1154 | * @return BaseStringL11n |
1155 | * |
1156 | * @since 1.0.0 |
1157 | */ |
1158 | private function createTaskAttributeValueL11nFromRequest(RequestAbstract $request) : BaseStringL11n |
1159 | { |
1160 | $attrL11n = new BaseStringL11n(); |
1161 | $attrL11n->ref = $request->getDataInt('value') ?? 0; |
1162 | $attrL11n->setLanguage( |
1163 | $request->getDataString('language') ?? $request->header->l11n->language |
1164 | ); |
1165 | $attrL11n->content = $request->getDataString('title') ?? ''; |
1166 | |
1167 | return $attrL11n; |
1168 | } |
1169 | |
1170 | /** |
1171 | * Validate task attribute l11n create request |
1172 | * |
1173 | * @param RequestAbstract $request Request |
1174 | * |
1175 | * @return array<string, bool> |
1176 | * |
1177 | * @since 1.0.0 |
1178 | */ |
1179 | private function validateTaskAttributeValueL11nCreate(RequestAbstract $request) : array |
1180 | { |
1181 | $val = []; |
1182 | if (($val['title'] = !$request->hasData('title')) |
1183 | || ($val['value'] = !$request->hasData('value')) |
1184 | ) { |
1185 | return $val; |
1186 | } |
1187 | |
1188 | return []; |
1189 | } |
1190 | } |