PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.41
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.41
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 / Services / StageService.php

StageService.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.41, at app/Services/StageService.php

449 lines 14.6 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\Services;
4
5 use FluentBoards\App\Models\Task;
6 use FluentBoards\App\Models\Board;
7 use FluentBoards\App\Models\Stage;
8 use FluentBoards\Framework\Http\Request\Request;
9 use FluentBoards\Framework\Support\Arr;
10
11 class StageService
12 {
13 public function createDefaultStages($board)
14 {
15 $stages = $this->defaultStages($board);
16 foreach ($stages as $stage) {
17 Stage::create($stage);
18 }
19 }
20
21 public function defaultStages($board)
22 {
23 return $this->defaultStagesForTodos($board->id);
24 }
25
26 public function defaultStagesForTodos($boardId)
27 {
28 return [
29 [
30 'board_id' => $boardId,
31 'title' => 'Open',
32 'position' => 1,
33 'slug' => 'open',
34 'settings' => [
35 'default_task_status' => 'open'
36 ]
37 ],
38 [
39 'board_id' => $boardId,
40 'title' => 'In Progress',
41 'position' => 2,
42 'slug' => 'in-progress',
43 'settings' => [
44 'default_task_status' => 'open'
45 ]
46 ],
47 [
48 'board_id' => $boardId,
49 'title' => 'Completed',
50 'position' => 3,
51 'slug' => 'completed',
52 'settings' => [
53 'default_task_status' => 'closed'
54 ]
55 ]
56 ];
57 }
58 public function updateStageProperty($col, $value, $stageId)
59 {
60 $stage = Stage::findOrFail($stageId);
61
62 if ('title' == $col) {
63 $stage = $this->updateTitle($value, $stage);
64 } elseif ('status' == $col) {
65 $stage = $this->updateStatus($value, $stage);
66 } elseif ('color' == $col) {
67 $stage = $this->updateColor($value, $stage);
68 } elseif ('bg_color' == $col) {
69 $stage = $this->updateBackgroundColor($value, $stage);
70 } elseif ('archived_at' == $col) {
71 $stage = $this->updateArchivedAt($value, $stage);
72 }
73 return $stage;
74 }
75
76 public function getLastOneMinuteUpdatedStages($boardId)
77 {
78 $oneMinuteAgoTimestamp = current_time('timestamp') - 60;
79 return Stage::where('board_id', $boardId)
80 ->where('updated_at', '>=', date_i18n('Y-m-d H:i:s', $oneMinuteAgoTimestamp))
81 ->get();
82 }
83
84 private function updateTitle($value, $stage)
85 {
86 $stage->title = $value;
87 $stage->save();
88 return $stage;
89 }
90
91 private function updateStatus($value, $stage)
92 {
93 $oldSettings = $stage->settings;
94 $oldSettings['default_task_status'] = $value;
95 $stage->settings = $oldSettings;
96 $stage->save();
97 return $stage;
98 }
99
100 private function updateColor($value, $stage)
101 {
102 $stage->color = $value;
103 $stage->save();
104 return $stage;
105 }
106
107 private function updateBackgroundColor($value, $stage)
108 {
109 $stage->bg_color = $value;
110 $stage->save();
111 return $stage;
112 }
113
114 private function updateArchivedAt($value, $stage)
115 {
116 $stage->archived_at = $value;
117 $stage->save();
118 return $stage;
119 }
120
121 public function createStage($stageData, $boardId)
122 {
123 $stage = new Stage();
124 $stage->board_id = $boardId;
125 $stage->title = $stageData['title'];
126 $stage->settings = [
127 'default_task_status' => Arr::get($stageData, 'status') ?? 'open',
128 'default_task_assignees' => []
129 ];
130 $providerPosition = Arr::get($stageData, 'position');
131 $lastStagePosition = $this->getLastPositionOfStagesOfBoard($boardId);
132 $stage->position = $lastStagePosition ? $lastStagePosition->position + 1 : 1;
133 $stage->save();
134 if($providerPosition){
135 $stage->moveToNewPosition($providerPosition);
136 }
137 return $stage;
138 }
139
140 public function getLastPositionOfStagesOfBoard($boardId)
141 {
142 // return last position of stages of board
143 return Stage::where('board_id', $boardId)
144 ->whereNull('archived_at')
145 ->orderBy('position', 'desc')
146 ->first();
147 }
148
149 protected function moveOtherStages($stage)
150 {
151
152 $stages = Stage::where('board_id', $stage->board_id)
153 ->where('position', '>=', $stage->position)
154 ->whereNotIn('id', [$stage->id])
155 ->whereNull('archived_at')->get();
156
157 foreach ($stages as $stage) {
158 $stage->position = $stage->position + 1;
159 $stage->save();
160 }
161 }
162
163 public function copyStagesOfBoard($board, $fromBoardId)
164 {
165 $stages = Stage::where('board_id', $fromBoardId)->where('type', 'stage')->whereNull('archived_at')->get();
166 $stageMapForCopyingTask = array();
167 foreach($stages as $key => $stage)
168 {
169 $stageToSave = array();
170 $stageToSave['title'] = $stage['title'];
171 $stageToSave['board_id'] = $board->id;
172 $stageToSave['slug'] = str_replace(' ', '-', strtolower($stage['title']));
173 $stageToSave['type'] = 'stage';
174 $stageToSave['position'] = $key + 1;
175 $stageToSave['settings'] = [
176 'default_task_status' => $stage->settings['default_task_status']
177 ];
178 $newStage = Stage::create($stageToSave);
179 $stageMapForCopyingTask[$stage['id']] = $newStage->id;
180 }
181 return $stageMapForCopyingTask;
182 }
183
184 public function importStagesFromBoard($board_id, $selectedStages, $position = null)
185 {
186 $targetStages = Stage::whereIn('id', $selectedStages)->get();
187 $stageMapForCopyingTask = array();
188 $stageIdsToCopy = array();
189
190 $numberOfStages = Stage::where('board_id', $board_id)->whereNull('archived_at')->count();
191
192 foreach($targetStages as $key => $stage)
193 {
194 $stageToSave = array();
195 $stageToSave['title'] = $stage['title'] . ' - imported';
196 $stageToSave['board_id'] = $board_id;
197 $stageToSave['slug'] = str_replace(' ', '-', strtolower($stage['title']));
198 $stageToSave['type'] = 'stage';
199 $stageToSave['position'] = $numberOfStages + $key + 1;
200 $stageToSave['settings'] = [
201 'default_task_status' => $stage->settings['default_task_status']
202 ];
203 $newStage = Stage::create($stageToSave);
204 if($position) {
205 $newStage->moveToNewPosition( (int) $position + $key );
206 }
207 $stageMapForCopyingTask[$stage['id']] = $newStage->id;
208 $stageIdsToCopy[] = $stage['id'];
209 }
210
211 $this->importTasks($board_id, $stageMapForCopyingTask, $stageIdsToCopy);
212 }
213
214 public function importTasks($boardId, $stageMapper, $stageIds)
215 {
216 $tasksToImport = Task::whereIn('stage_id', $stageIds)->whereNull('archived_at')->get();
217
218 foreach($tasksToImport as $task)
219 {
220 $newTask = array();
221 $newTask['title'] = $task->title;
222 $newTask['description'] = $task->description;
223 $newTask['board_id'] = $boardId;
224 $newTask['stage_id'] = $stageMapper[$task->stage_id];
225 $newTask['status'] = $task->status;
226 $newTask['priority'] = $task->priority;
227 $newTask['position'] = $task->position;
228 $newTask['due_at'] = $task->due_at;
229 Task::create($newTask);
230 }
231
232 //update task count of board
233 $totalTasks = sizeof($tasksToImport);
234 $board = Board::findOrFail($boardId);
235 $settings = $board->settings ?? [];
236
237 if (isset($settings['tasks_count'])) {
238 $settings['tasks_count'] += $totalTasks;
239 } else {
240 $settings['tasks_count'] = $totalTasks;
241 }
242 $board->settings = $settings;
243 $board->save();
244 }
245
246 public function updateStageTemplate($stage_id)
247 {
248 $stage = Stage::findOrFail($stage_id);
249 $stageSettings = $stage->settings;
250 if($stageSettings && array_key_exists('is_template', $stageSettings))
251 {
252 $currentlyIsTemplate = $stageSettings['is_template'];
253 if($currentlyIsTemplate){
254 $stageSettings['is_template'] = false;
255 $stage->settings = $stageSettings;
256 }else{
257 $stageSettings['is_template'] = true;
258 $stage->settings = $stageSettings;
259 }
260 }else {
261 if(!$stageSettings) {
262 $stage->settings = [
263 'is_template' => true
264 ];
265 } else {
266 $stage->settings = array_merge($stage->settings, [
267 'is_template' => true
268 ]);
269 }
270
271 }
272 $stage->save();
273
274 return $stage;
275 }
276
277 public function moveAllTasks($oldStageId, $newStageId)
278 {
279 $tasks = Task::where('stage_id', $oldStageId)->whereNull('parent_id')->whereNull('archived_at')->get();
280
281 // get the last position available of that stage
282 $position = (new TaskService())->getLastPositionOfTasks($newStageId);
283
284 // update tasks stage and position
285 foreach ($tasks as $key => $task) {
286 $task->stage_id = $newStageId;
287 $task->position = $position + $key;
288 $task->save();
289 }
290 return $tasks;
291 }
292 public function archiveAllTasksInStage($stage_id)
293 {
294 $tasks = Task::where('stage_id', $stage_id)->whereNull('parent_id')->whereNull('archived_at')->get();
295 foreach ($tasks as $task) {
296 $task->position = 0;
297 $task->archived_at = current_time('mysql');
298 $task->save();
299 do_action('fluent_boards/task_archived', $task);
300 }
301 return $tasks;
302 }
303
304 public function createRoadmapStages($board, $stagesData)
305 {
306 foreach ($stagesData as $index => $formStageData) {
307 $stage = new Stage();
308 $stage->board_id = $board->id;
309 $stage->title = $formStageData['title'];
310 $stage->slug = $formStageData['slug'];
311 $stage->position = $formStageData['position'] ? $formStageData['position'] : 1;
312 $stage->settings = $this->roadmapStageSetting($index);
313 $stage->save();
314 }
315 return $stagesData;
316 }
317
318 /*
319 * I have no idea what this function does, but I am doing it
320 * to make the code work
321 */
322 public function roadmapStageSetting($index)
323 {
324 return [
325 'is_public' => $index > 0 ? true : false,
326 'default_task_status' => 'open',
327 'is_template' => false,
328 ];
329 }
330
331
332 public function createStages($board, $stageData)
333 {
334 $firstStage = null;
335 foreach ($stageData as $index => $stage) {
336 $stageToPush = array();
337 $stageToPush['title'] = $stage['title'];
338 $stageToPush['board_id'] = $board->id;
339 $stageToPush['position'] = $index + 1;
340 $stageToPush['slug'] = $this->createSlug($stage['title']);
341
342 if (Arr::get($stage, 'title') == 'Completed') {
343 $stageToPush['settings'] = [
344 'default_task_status' => 'closed',
345 'is_template' => false
346 ];
347 }
348
349 $stage = Stage::create($stageToPush);
350 if($index == 0){
351 $firstStage = $stage;
352 }
353 }
354 return $firstStage;
355 }
356
357 private function createSlug($title)
358 {
359 return str_replace(' ', '-', strtolower($title));
360 }
361
362 public function stagesByBoardId($boardId)
363 {
364 return Stage::where('board_id', $boardId)->whereNull('archived_at')->get();
365 }
366
367
368
369 public function sortStageTasks($order, $orderBy, $stage_id)
370 {
371 $sortOptions = ['priority', 'due_at', 'position', 'created_at', 'title'];
372 $orderOptions = ['ASC', 'DESC'];
373
374 // Validate order and orderBy parameters
375 if (!in_array($order, $sortOptions) || !in_array($orderBy, $orderOptions)) {
376 throw new \Exception(__('Invalid sort or orderBy parameter', 'fluent-boards'));
377 }
378
379 $tasksQuery = Task::where('stage_id', $stage_id)
380 ->whereNull('parent_id')
381 ->whereNull('archived_at')
382 ->with(['assignees', 'labels', 'watchers']);
383
384 // Apply ordering based on the specified order and orderBy
385 switch ($order) {
386 case 'priority':
387 $tasksQuery->orderByRaw("FIELD(priority, 'High', 'Medium', 'Low') {$orderBy}");
388 break;
389
390 case 'due_at':
391 if ($orderBy === 'ASC') {
392 // Separate ordering for tasks with and without due dates
393 $tasksWithDueDate = (clone $tasksQuery)->whereNotNull('due_at')->orderBy('due_at')->get();
394 $tasksWithoutDueDate = (clone $tasksQuery)->whereNull('due_at')->get();
395 $tasks = $tasksWithDueDate->merge($tasksWithoutDueDate);
396 } else {
397 $tasksQuery->orderBy('due_at', $orderBy);
398 }
399 break;
400
401 default:
402 $tasksQuery->orderBy($order, $orderBy);
403 break;
404 }
405
406 // Fetch tasks if not already fetched
407 if (!isset($tasks)) {
408 $tasks = $tasksQuery->get();
409 }
410
411 // Update tasks with additional attributes
412 $tasks->each(function ($task, $key) {
413 $task->position = $key + 1;
414 $task->save();
415 $task->isOverdue = $task->isOverdue();
416 $task->isUpcoming = $task->upcoming();
417 $task->is_watching = $task->isWatching();
418 $task->contact = Task::lead_contact($task->crm_contact_id);
419 });
420 return $tasks;
421 }
422
423
424 public function updateStage($updatedStage, $board_id, $oldStage)
425 {
426 $stageBeforeUpdate = clone $oldStage;
427 $oldStage->title = sanitize_text_field(Arr::get($updatedStage, 'title'));
428 $oldStage->bg_color = sanitize_text_field(Arr::get($updatedStage, 'cover_bg'));
429 $oldStage->save();
430 do_action('fluent_boards/stage_updated', $board_id, $updatedStage, $stageBeforeUpdate);
431 return $oldStage;
432 }
433
434 public function setDefaultAssignees($stage_id, $assignees)
435 {
436 $stage = Stage::findOrFail($stage_id);
437 if ($stage) {
438 $oldSettings = $stage->settings;
439 $oldSettings['default_task_assignees'] = $assignees;
440 $stage->settings = $oldSettings;
441 $stage->save();
442
443 do_action('fluent_boards/default_assignees_updated', $stage, $assignees);
444
445 return $stage;
446 }
447 }
448 }
449