PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.32
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.32
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / app / Api / Classes / Tasks.php

Tasks.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.32, at app/Api/Classes/Tasks.php

604 lines 16.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBoards\App\Api\Classes;
4
5 defined('ABSPATH') || exit;
6
7 use Exception;
8 use FluentBoards\App\Models\Label;
9 use FluentBoards\App\Models\Stage;
10 use FluentBoards\App\Models\Task;
11 use FluentBoards\App\Models\Board;
12 use FluentBoards\App\Services\BoardService;
13 use FluentBoards\App\Services\Helper;
14 use FluentBoards\App\Services\PermissionManager;
15 use FluentBoards\App\Services\TaskService;
16 use FluentBoardsPro\App\Models\TaskAttachment;
17 use FluentBoardsPro\App\Services\AttachmentService;
18 use FluentBoardsPro\App\Services\SubtaskService;
19 use FluentCrm\App\Models\Subscriber;
20
21 use FluentBoards\App\Services\Constant;
22
23
24
25 /**
26 * Contacts Class - PHP APi Wrapper
27 *
28 * Contacts API Wrapper Class that can be used as <code>FluentBoardsApi('tasks')</code> to get the class instance
29 *
30 * @package FluentBoards\App\Api\Classes
31 * @namespace FluentBoards\App\Api\Classes
32 *
33 * @version 1.0.0
34 */
35 class Tasks
36 {
37 private $instance = null;
38
39 private $allowedInstanceMethods = [
40 'all',
41 'get',
42 'find',
43 'first',
44 'paginate'
45 ];
46
47 public function __construct(Task $instance)
48 {
49 $this->instance = $instance;
50 }
51
52 /**
53 * Get Tasks by board
54 *
55 * Use:
56 * <code>FluentBoardsApi('tasks')->getTasksByBoard();</code>
57 *
58 * @param string|int $board_id
59 * @param array $with
60 * @return array|Task Model
61 */
62 public function getTasksByBoard($board_id, $with = [])
63 {
64 if (!$board_id) {
65 return [];
66 }
67
68 //checking if current user has access to board
69 if (!PermissionManager::userHasPermission($board_id)) {
70 return false;
71 }
72
73 $query = Task::query()
74 ->where('board_id', $board_id)
75 ->whereNull('parent_id')
76 ->whereNull('archived_at')
77 ->orderBy('due_at', 'ASC');
78
79 if (!empty($with)) {
80 $query->with($with);
81 }
82
83 $tasks = $query->get();
84
85 $tasks->transform(function ($task) {
86 $task->isOverdue = $task->isOverdue();
87 $task->isUpcoming = $task->upcoming();
88 $task->contact = Helper::crm_contact($task->crm_contact_id);
89 $task->is_watching = $task->isWatching();
90 return $task;
91 });
92
93 return $tasks;
94 }
95
96
97 /**
98 * Get Task by id
99 *
100 * Use:
101 * <code>FluentBoardsApi('tasks')->getTask($id);</code>
102 *
103 * @param int|string $id Task id
104 * @return false|Task Model
105 */
106 public function getTask($id)
107 {
108 if (!empty($id)) {
109 $task = Task::where('id', $id)->first();
110
111 //checking if current user has access to board
112 if (!PermissionManager::userHasPermission($task->board_id)) {
113 return false;
114 }
115 return $task;
116 }
117 return false;
118 }
119
120
121 /**
122 * Get Task Created by
123 *
124 * Use:
125 * <code>FluentBoardsApi('tasks')->getTasksCreatedBy($userId);</code>
126 *
127 * @param int|string $userId Task id
128 * @param array $with
129 * @return false|Task Model
130 */
131 public function getTasksCreatedBy($userId, $with = [])
132 {
133 if (!empty($userId)) {
134 $query = Task::where('created_by', $userId);
135
136 if (!empty($with)) {
137 $query->with($with);
138 }
139
140 $tasks = $query->get();
141
142 $permittedTasks = [];
143
144 foreach ($tasks as $task) {
145 //checking if current user has access to board
146 if (PermissionManager::userHasPermission($task->board_id)) {
147 $permittedTasks[] = $task;
148 }
149 }
150
151 return $permittedTasks;
152 }
153 return false;
154 }
155
156
157 public function create($data)
158 {
159 if (empty($data)) {
160 return false;
161 }
162
163 if (empty($data['title']) || empty($data['board_id']) || empty($data['stage_id'])) {
164 return false;
165 }
166
167 $taskData = Helper::sanitizeTask($data);
168
169 if(!empty($taskData['priority']) && !in_array($taskData['priority'], ['low', 'medium', 'high'])) {
170 $taskData['priority'] = 'low';
171 }
172 if(!empty($taskData['status']) && !in_array($taskData['status'], ['open', 'closed'])) {
173 $taskData['status'] = 'open';
174 }
175 if(!empty($data['crm_contact_id'])) {
176 $taskData['crm_contact_id'] = $data['crm_contact_id'];
177 }
178
179 if(!empty($data['contact_email']) && empty($data['crm_contact_id'])) {
180 // Find first if the contact exists
181 $contact = FluentCrmApi('contacts')->creteOrUpdate([
182 'email' => $data['contact_email'],
183 'first_name' => $data['contact_first_name'],
184 'last_name' => $data['contact_last_name'],
185 'status' => 'subscribed'
186 ]);
187 $taskData['crm_contact_id'] = $contact->id;
188 }
189
190 if(!empty($data['assignees']) && is_array($data['assignees'])) {
191 // check if the assignees are valid, they have to be wp user ids
192 $users = get_users(['include' => $data['assignees']]);
193 $taskData['assignees'] = array_map(function($user) {
194 return $user->ID;
195 }, $users);
196 // after successful task creation we have to add them as board members
197 }
198
199 if(!empty($data['labels']) && is_array($data['labels'])) {
200 $labelIds = [];
201 // check if the labels are valid, they have to be label ids
202 foreach ($data['labels'] as $label) {
203 if(is_numeric($label)) {
204 $labelModel = Label::where('id', $label)->where('board_id', $data['board_id'])->first();
205 if($labelModel) {
206 $labelId = $labelModel->id;
207 $labelIds[] = $labelId;
208 }
209 continue;
210 }
211 if(is_string($label)) {
212 $labelModel = Label::where('board_id', $data['board_id'])
213 ->where(function($query) use ($label) {
214 $query->where('title', $label)
215 ->orWhere('slug', $label);
216 })->first();
217 if($labelModel) {
218 $labelId = $labelModel->id;
219 $labelIds[] = $labelId;
220 }
221 }
222 }
223 $taskData['labels'] = $labelIds;
224 // we have to map the labels after task creation
225 }
226
227 $task = $this->instance->createTask($taskData);
228 // push assignees to board Members
229 $boardService = new BoardService();
230 if(!empty($taskData['assignees'])) {
231 foreach ($taskData['assignees'] as $assigneeId) {
232 $boardService->addMembersInBoard($data['board_id'], $assigneeId);
233 }
234 }
235
236 return $task;
237 }
238
239
240 public function createTask($data = [])
241 {
242 $defaultData = [
243 'title',
244 'board_id',
245 'stage_id',
246 'parent_id',
247 'status', // open | closed
248 'priority', // low | medium | high
249 'source',
250 'source_id',
251 'description',
252 'due_at',
253 'crm_contact_id',
254 // <or>
255 'contact_email',
256 'contact_first_name', // this is the full name
257 'contact_last_name', // this is the full name
258 'contact_status', // default subscribed
259 // </or>
260 'assignees', // WP User IDs / WP User Emails as an array
261 'labels', // array of label ids or titles [1, 'New']
262 ];
263
264 }
265
266 public function addAssignees($task_id, $assignees = [])
267 {
268 if(empty($task_id) || empty($assignees)) {
269 return false;
270 }
271
272 $task = Task::where('id', $task_id)->first();
273 $board = Board::findOrFail($task->board_id);
274
275 if(!PermissionManager::isBoardManager($task->board_id))
276 {
277 if(!PermissionManager::isAdmin())
278 {
279 return false;
280 }
281 }
282
283 if(!$task) {
284 return false;
285 }
286
287 $boardUsersIds = $board->users->pluck('ID')->toArray();
288
289 foreach($assignees as $assignee)
290 {
291 if(in_array($assignee, $boardUsersIds)) { //check if user is a board member
292 $task->assignees()
293 ->syncWithoutDetaching([
294 $assignee => [
295 'object_type' => Constant::OBJECT_TYPE_TASK_ASSIGNEE
296 ]
297 ]);
298 $task->watchers()
299 ->syncWithoutDetaching([
300 $assignee => [
301 'object_type' => Constant::OBJECT_TYPE_USER_TASK_WATCH
302 ]
303 ]);
304 }
305 }
306
307 return true;
308 }
309
310 public function removeAssignees($task_id, $assignees = [])
311 {
312 if(empty($task_id) || empty($assignees)) {
313 return false;
314 }
315
316 $task = Task::where('id', $task_id)->first();
317 if(!$task) {
318 return false;
319 }
320
321 if(!PermissionManager::isBoardManager($task->board_id))
322 {
323 if(!PermissionManager::isAdmin())
324 {
325 return false;
326 }
327 }
328
329 $task->assignees()->detach($assignees);
330
331 $task->watchers()->detach($assignees);
332
333 return true;
334 }
335
336
337
338 public function attachLabels($taskId, $labelIds = [])
339 {
340 // do code here
341 if(!$taskId)
342 {
343 return false;
344 }
345
346 $task = Task::findOrFail($taskId);
347 $board = Board::findOrFail($task->board_id);
348
349 if(!$task)
350 {
351 return false;
352 }
353
354 //checking if current user has access to board
355 if (!PermissionManager::userHasPermission($task->board_id)) {
356 return false;
357 }
358
359 $boardlabelIds = $board->labels->pluck('id')->toArray();
360
361 foreach ($labelIds as $labelId)
362 {
363 if(in_array($labelId, $boardlabelIds)) { //check if label is a board label
364 $task->labels()
365 ->syncWithoutDetaching([
366 $labelId => [
367 'object_type' => Constant::OBJECT_TYPE_TASK_LABEL
368 ]
369 ]);
370 }
371 }
372
373 return true;
374
375 }
376
377 public function removeLabels($taskId, $labelIds = [])
378 {
379 // do code here
380 if(!$taskId)
381 {
382 return false;
383 }
384
385 $task = Task::findOrFail($taskId);
386
387 if(!$task)
388 {
389 return false;
390 }
391
392 //checking if current user has access to board
393 if (!PermissionManager::userHasPermission($task->board_id)) {
394 return false;
395 }
396
397 $task->labels()->detach($labelIds);
398
399 return true;
400 }
401
402 /*
403 * Change the stage of a task
404 * @param int $taskId
405 * @param int $stageId - the new stage id
406 * @return Task
407 */
408 public function changeStage($task, $stageId)
409 {
410 // param can be either task object or task Id
411 if(is_numeric($task)) {
412 $task = Task::where('id', $task)->first();
413 }
414
415 if(!$task) {
416 return false;
417 }
418
419 //checking if current user has access to board
420 if (!PermissionManager::userHasPermission($task->board_id)) {
421 return false;
422 }
423
424 $stage = Stage::findOrFail($stageId);
425
426 if($stage->board_id != $task->board_id)
427 {
428 return false;
429 }
430
431 $taskService = new TaskService();
432 $position = $taskService->getLastPositionOfTasks($stageId);
433
434 $task->stage_id = $stageId;
435 $task->position = $position;
436 $task->save();
437
438 return $task;
439 }
440
441 public function updateProperty($taskId, $property, $value)
442 {
443 $taskService = new TaskService();
444 $task = Task::where('id', $taskId)->first();
445
446 if(!$task) {
447 return false;
448 }
449
450 $allowedColumns = ['title', 'description', 'due_at', 'priority', 'status', 'source', 'source_id', 'crm_contact_id'];
451
452 if(!in_array($property, $allowedColumns)) {
453 return false;
454 }
455
456 $taskService->updateTaskProperty($task, $property, $value);
457
458 return $task;
459
460 }
461
462 /**
463 * Create a task attachment
464 * @param int $boardId
465 * @param int $taskId
466 * @param array $data - ['title', 'url'] // url is required
467 * @throws Exception
468 */
469 public function createTaskAttachment(int $boardId, int $taskId, array $data = [])
470 {
471 // check if the user has permission to add attachment
472 if (!$this->hasProAndBoardAccess($boardId) || empty($data) || empty($data['url'])) {
473 return false;
474 }
475
476 $task = Task::where('id', $taskId)->where('board_id', $boardId)->first();
477 if (!$task) {
478 return false;
479 }
480
481 $attachmentData = Helper::sanitizeTaskAttachment($data);
482 return (new AttachmentService())->addTaskAttachment($task->id, $attachmentData);
483 }
484
485
486 /**
487 * delete a task attachment
488 * @param $boardId
489 * @param $taskId
490 * @param $attachmentId
491 * @return bool
492 */
493 public function deleteTaskAttachment( int $boardId, int $taskId, int $attachmentId)
494 {
495 // check if the user has permission to delete attachment
496 if (!$this->hasProAndBoardAccess($boardId)) {
497 return false;
498 }
499
500 $task = Task::where('id', $taskId)->where('board_id', $boardId)->first();
501 if (!$task) {
502 return false;
503 }
504
505 (new AttachmentService())->deleteTaskAttachment($task->id, $attachmentId);
506
507 return true;
508 }
509
510
511 /**
512 * Create a subtask
513 * @param int $boardId
514 * @param int $taskId
515 * @param $data
516 * @return bool|Task
517 */
518 public function createSubtask(int $boardId, int $taskId, $data): Task|bool
519 {
520 if (!$this->hasProAndBoardAccess($boardId)) {
521 return false;
522 }
523
524 $task = Task::where('id', $taskId)->where('board_id', $boardId)->first();
525 if (!$task) {
526 return false;
527 }
528
529 $data['board_id'] = $boardId;
530 $data['parent_id'] = $task->id;
531
532
533 return $this->create($data);
534 }
535
536
537 /**
538 * Update a subtask
539 * @param int $boardId
540 * @param int $taskId
541 * @param int $subtaskId
542 * @param string $property
543 * @param mixed $value
544 *
545 */
546 public function updateSubtask($boardId, $taskId, $subtaskId, $property, $value)
547 {
548 if (!$this->hasProAndBoardAccess($boardId)) {
549 return false;
550 }
551 return $this->updateProperty($subtaskId, $property, $value);
552 }
553
554
555 /**
556 * Delete a subtask
557 * @param int $boardId
558 * @param int $taskId
559 * @param int $subtaskId
560 */
561 public function deleteSubtask($boardId, $taskId, $subtaskId)
562 {
563 if (!$this->hasProAndBoardAccess($boardId)) {
564 return false;
565 }
566
567 $subtask = Task::where('id', $subtaskId)->where('parent_id', $taskId)->where('board_id', $boardId)->first();
568 $deletedTask = clone $subtask;
569
570 $options = null;
571 //if we need to do something before a task is deleted
572 do_action('fluent_boards/before_task_deleted', $subtask, $options);
573
574 ( new SubtaskService() )->deleteSubtask($subtask);
575
576 do_action('fluent_boards/subtask_deleted_activity', $deletedTask->parent_id, $deletedTask->title);
577 }
578
579 /**
580 * Check if the user has pro version and has access to the board
581 * @param $boardId
582 * @return bool
583 */
584 private function hasProAndBoardAccess($boardId)
585 {
586 return defined('FLUENT_BOARDS_PRO_VERSION') && PermissionManager::userHasPermission($boardId);
587 }
588
589
590 public function getInstance()
591 {
592 return $this->instance;
593 }
594
595 public function __call($method, $params)
596 {
597 if (in_array($method, $this->allowedInstanceMethods)) {
598 return call_user_func_array([$this->instance, $method], $params);
599 }
600
601 throw new \Exception("Method {$method} does not exist.");
602 }
603 }
604