PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.11
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.11
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 1.45 All 41 releases
fluent-boards / app / Http / Controllers / TaskController.php

TaskController.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.11, at app/Http/Controllers/TaskController.php

423 lines 14.2 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\Http\Controllers;
4
5 use DateTimeImmutable;
6 use FluentBoards\App\Models\Stage;
7 use FluentBoards\App\Models\Task;
8 use FluentBoards\App\Models\Board;
9 use FluentBoards\App\Services\Constant;
10 use FluentBoards\App\Services\Helper;
11 use FluentBoards\App\Services\StageService;
12 use FluentBoards\App\Services\TaskService;
13 use FluentBoards\App\Services\NotificationService;
14 use FluentBoards\Framework\Http\Request\Request;
15 use FluentBoards\App\Services\PermissionManager;
16 use FluentBoards\Framework\Support\Arr;
17
18 class TaskController extends Controller
19 {
20 private TaskService $taskService;
21
22 private NotificationService $notificationService;
23
24 public function __construct(TaskService $taskService, NotificationService $notificationService)
25 {
26
27 parent::__construct();
28 $this->taskService = $taskService;
29 $this->notificationService = $notificationService;
30 }
31
32 public function getTopTasksForBoards()
33 {
34 $userId = get_current_user_id();
35 $task_ids = PermissionManager::getTaskIdsWatchByUser($userId);
36 $tasksArray = $this->taskService->getTasksForBoards(['overdue', 'upcoming'], 5, $task_ids);
37
38 return [
39 'data' => $tasksArray,
40 ];
41 }
42
43 public function getTasksByBoard($board_id)
44 {
45 $tasks = Task::with(['assignees', 'labels', 'watchers'])
46 ->where('board_id', $board_id)
47 ->whereNull('archived_at')
48 ->whereNull('parent_id')
49 ->orderBy('due_at', 'ASC')
50 ->get();
51
52 foreach ($tasks as $task) {
53 $task->isOverdue = $task->isOverdue();
54 $task->isUpcoming = $task->upcoming();
55 $task->contact = Helper::crm_contact($task->crm_contact_id);
56 $task->is_watching = $task->isWatching();
57 $task->assignees = Helper::sanitizeUserCollections($task->assignees);
58 $task->watchers = Helper::sanitizeUserCollections($task->watchers);
59 }
60
61 return [
62 'tasks' => $tasks,
63 ];
64 }
65
66 public function create(Request $request, $board_id)
67 {
68 $taskData = $this->taskSanitizeAndValidate($request->get('task'), [
69 'title' => 'required|string',
70 'board_id' => 'required|numeric',
71 'stage_id' => 'required|numeric',
72 'priority' => 'nullable|string',
73 'crm_contact_id' => 'nullable|numeric',
74 'is_template' => 'string',
75 ]);
76
77 try {
78 if ($taskData['board_id'] != $board_id) {
79 throw new \Exception(__('Board id is not valid', 'fluent-boards'));
80 }
81
82 $task = $this->taskService->createTask($taskData, $board_id);
83
84 return $this->sendSuccess([
85 'task' => $task,
86 'message' => __('Task has been successfully created', 'fluent-boards'),
87 'updatedTasks' => $this->taskService->getLastOneMinuteUpdatedTasks($task->board_id)
88 ], 201);
89 } catch (\Exception $e) {
90 return $this->sendError($e->getMessage(), 400);
91 }
92 }
93
94 public function find($board_id, $task_id)
95 {
96 $stageService = new StageService();
97 $task = Task::findOrFail($task_id);
98
99 if (defined('FLUENT_BOARDS_PRO')) {
100 $task->load(['attachments']);
101 }
102
103 $task->load(['board', 'stage', 'labels', 'assignees']);
104
105 $task->assignees = Helper::sanitizeUserCollections($task->assignees);
106
107 $task->isOverdue = $task->isOverdue();
108 $task->contact = Task::lead_contact($task->crm_contact_id);
109 $task->board->stages = $stageService->stagesByBoardId($board_id);
110 $task->is_watching = $this->notificationService->isCurrentUserObservingTask($task);
111
112 $task = $this->taskService->loadNextStage($task);
113
114 if ($task->type == 'roadmap') {
115 $task->vote_statistics = $this->taskService->getIdeaVoteStatistics($task_id);
116 }
117
118 return [
119 'task' => $task
120 ];
121 }
122
123 public function getStageType(Request $request)
124 {
125 $stage = Stage::findOrFail($request->stage_id);
126
127 return [
128 'stage' => $stage,
129 ];
130 }
131
132 public function getActivities(Request $request, $board_id, $task_id)
133 {
134 $filter = $request->getSafe('filter');
135 $per_page = 15; // Apparently, let's use a fixed number of items per page.
136
137 return [
138 'activities' => $this->taskService->getActivities($task_id, $per_page, $filter)
139 ];
140
141 }
142
143 public function getArchivedTasks(Request $request, $board_id)
144 {
145 $tasks = $this->taskService->getArchivedTasks($request->all(), $board_id);
146
147 foreach ($tasks as $task) {
148 $task->assignees = Helper::sanitizeUserCollections($task->assignees);
149 }
150
151 return [
152 'tasks' => $tasks
153 ];
154 }
155
156 public function updateTaskProperties(Request $request, $board_id, $task_id)
157 {
158 $col = $request->getSafe('property', 'sanitize_text_field');
159 $value = $request->get('value');
160
161 $validatedData = $this->updateTaskPropValidationAndSanitation($col, $value);
162 $task = Task::with(['board', 'labels', 'assignees'])->findOrFail($task_id);
163
164 $task = $this->taskService->updateTaskProperty($col, $validatedData[$col], $task);
165 $task->isOverdue = $task->isOverdue();
166 $task->isUpcoming = $task->upcoming();
167 $task->contact = Helper::crm_contact($task->crm_contact_id);
168 $task->is_watching = $task->isWatching();
169 $task->assignees = Helper::sanitizeUserCollections($task->assignees);
170
171 // A recent update to a task might impact other tasks on the board.
172 $updatedTasks = $this->taskService->getLastOneMinuteUpdatedTasks($board_id);
173
174 return [
175 'message' => __('Task has been updated', 'fluent-boards'),
176 'task' => $task,
177 'updatedTasks' => $updatedTasks
178 ];
179 }
180
181 public function updateTaskDates(Request $request, $board_id, $task_id)
182 {
183 $task = Task::findOrFail($task_id);
184
185 $startAt = $request->getSafe('started_at', 'sanitize_text_field', NULL);
186 $dueAt = $request->getSafe('due_at', 'sanitize_text_field', NULL);
187
188 if ($startAt && $dueAt) {
189 if (strtotime($startAt) > strtotime($dueAt)) {
190 $startAt = gmdate('Y-m-d 00:00:00', strtotime($dueAt));
191 }
192 }
193
194 $task = $this->taskService->updateTaskProperty('started_at', $startAt, $task);
195 $task = $this->taskService->updateTaskProperty('due_at', $dueAt, $task);
196
197 return [
198 'task' => $task,
199 'message' => __('Dates has been updated', 'fluent-boards'),
200 'updatedTasks' => $this->taskService->getLastOneMinuteUpdatedTasks($board_id),
201 ];
202 }
203
204 public function updateTaskCoverPhoto(Request $request, $board_id, $task_id)
205 {
206 $imagePath = $request->thumbnail;
207 $task = $this->taskService->taskCoverPhotoUpdate($task_id, $imagePath);
208
209 return [
210 'message' => __('Task cover photo has been updated', 'fluent-boards'),
211 'task' => $task,
212 ];
213
214 }
215
216 public function taskStatusUpdate(Request $request, $board_id, $task_id)
217 {
218 return [
219 'message' => __('Task status has been updated', 'fluent-boards'),
220 'task' => $this->taskService->taskStatusUpdate($task_id, $request->integrationType),
221 ];
222 }
223
224 public function delete($board_id, $task_id)
225 {
226 $task = Task::findOrFail($task_id);
227 $options = null;
228 //if we need to do something before a task is deleted
229 do_action('fluent_boards/before_task_deleted', $task, $options);
230
231 $this->taskService->deleteTask($task);
232
233 return [
234 'updatedTasks' => $this->taskService->getLastOneMinuteUpdatedTasks($board_id),
235 'message' => __('Task has been deleted', 'fluent-boards'),
236 ];
237 }
238
239 private function taskSanitizeAndValidate($data, array $rules = [])
240 {
241 $data = Helper::sanitizeTask($data);
242
243 return $this->validate($data, $rules);
244 }
245
246 private function updateTaskPropValidationAndSanitation($col, $value)
247 {
248 $rules = [
249 'title' => 'required|string',
250 'board_id' => 'required',
251 'parent_id' => 'required',
252 'crm_contact_id' => 'nullable',
253 'task_type' => 'nullable|string',
254 'status' => 'nullable|string',
255 'stage_id' => 'required',
256 'reminder_type' => 'nullable|string',
257 'priority' => 'nullable|string',
258 'lead_value' => 'nullable|numeric|between:0,9999999.99',
259 'remind_at' => 'nullable|string',
260 'scope' => 'nullable|string',
261 'source' => 'nullable|string',
262 'description' => 'nullable|string',
263 'due_at' => 'nullable|string',
264 'started_at' => 'nullable|string',
265 'start_at' => 'nullable|string',
266 'log_minutes' => 'nullable|integer|unsigned',
267 'last_completed' => 'nullable|date',
268 'assignees' => 'nullable|integer',
269 'archived_at' => 'nullable|string',
270 'is_watching' => 'nullable|string',
271 'is_template' => 'string',
272 'last_completed_at' => 'nullable',
273 'settings' => 'nullable|array',
274 ];
275 if (array_key_exists($col, $rules)) {
276 $rule = $rules[$col];
277 if ('assignees' == $col && is_array($value)) {
278 $sanitizedAndValidatedValue = [];
279 foreach ($value as $val) {
280 $sanitizeData = Helper::sanitizeTask([$col => $val]);
281 $validatedData = $this->validate($sanitizeData, [
282 $col => $rule,
283 ]);
284 array_push($sanitizedAndValidatedValue, $validatedData[$col]);
285 }
286
287 return [$col => $sanitizedAndValidatedValue];
288 }
289 $data = Helper::sanitizeTask([$col => $value]);
290
291 return $this->validate($data, [
292 $col => $rule,
293 ]);
294 }
295 }
296
297 public function getLabelsByTask($task_id)
298 {
299 $labels = $this->taskService->getLabelsByTask($task_id);
300
301 return $this->sendSuccess([
302 'labels' => $labels,
303 ], 200);
304 }
305
306 public function getStageByTask($task_id)
307 {
308 $stage = $this->taskService->getStageByTask($task_id);
309
310 return [
311 'stage' => $stage,
312 ];
313 }
314
315 public function assignYourselfInTask($board_id, $task_id)
316 {
317 $task = $this->taskService->assignYourselfInTask($board_id, $task_id);
318
319 return [
320 'task' => $task,
321 ];
322 }
323
324 public function detachYourselfFromTask($board_id, $task_id)
325 {
326 $task = $this->taskService->detachYourselfFromTask($board_id, $task_id);
327 $task->assignees = Helper::sanitizeUserCollections($task->assignees);
328
329 return [
330 'task' => $task,
331 ];
332 }
333
334 private function taskMetaSanitizeAndValidate($data, array $rules = [])
335 {
336 $data = Helper::sanitizeTaskMeta($data);
337
338 return $this->validate($data, $rules);
339 }
340
341 public function moveTaskToNextStage($board_id, $task_id)
342 {
343 $task = $this->taskService->moveTaskToNextStage($task_id);
344
345 return [
346 'task' => $task
347 ];
348 }
349
350 /**
351 * @throws \Exception
352 */
353 public function moveTask(Request $request, $board_id, $task_id)
354 {
355 $task = Task::findOrFail($task_id);
356 $oldStageId = $task->stage_id;
357 $newStageId = $request->getSafe('newStageId', 'intval');
358 $newIndex = $request->getSafe('newIndex', 'intval');
359 $newBoardId = $request->getSafe('newBoardId', 'intval');
360
361 if ((!is_numeric($newStageId) || $newStageId == 0)) {
362 throw new \Exception(__('Invalid Stage', 'fluent-boards'));
363 }
364 if ((!is_numeric($newIndex) || $newIndex == 0)) {
365 throw new \Exception(__('Invalid Value', 'fluent-boards'));
366 }
367 if ($newBoardId) {
368 if ((!is_numeric($newBoardId) || $newBoardId == 0)) {
369 throw new \Exception(__('Invalid Board', 'fluent-boards'));
370 }
371 $task->board_id = $newBoardId;
372 }
373
374 $task->stage_id = $newStageId;
375 $task = $task->moveToNewPosition($newIndex);
376
377 if ($oldStageId != $newStageId) {
378
379 $defaultPosition = $task->stage->defaultTaskStatus();
380
381 if ($defaultPosition == 'closed' && $task->status != 'closed') {
382 $task = $task->close();
383 }
384
385 do_action('fluent_boards/task_moved_to_new_stage', $task, $oldStageId);
386
387 do_action('fluent_boards/task_stage_updated', $task, $oldStageId);
388
389 $usersToSendEmail = $this->notificationService->filterAssigneeToSendEmail($task->id, Constant::BOARD_EMAIL_STAGE_CHANGE);
390 $this->taskService->sendMailAfterTaskModify('stage_change', $usersToSendEmail, $task->id);
391 }
392
393 do_action('fluent_boards/task_updated', $task, 'position');
394
395 $updatedTasks = $this->taskService->getLastOneMinuteUpdatedTasks($task->board_id, $request->get('last_boards_updated'));
396
397 return [
398 'new_position' => $task,
399 'message' => __('Task has been updated', 'fluent-boards'),
400 'task' => $task,
401 'updatedTasks' => $updatedTasks,
402 'last_updated' => current_time('mysql')
403 ];
404 }
405
406 public function sendMailAfterStageChange($usersToSendEmail, $taskId)
407 {
408 $current_user_id = get_current_user_id();
409
410 /* this will run in background as soon as possible */
411 /* sending Model or Model Instance won't work here */
412 as_enqueue_async_action('fluent_boards/one_time_schedule_send_email_for_stage_change', [$taskId, $usersToSendEmail, $current_user_id], 'fluent-boards');
413 }
414
415 public function getAssociatedTasks($associated_id)
416 {
417 return [
418 'tasks' => $this->taskService->getAssociatedTasks($associated_id)
419 ];
420 }
421
422 }
423