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

450 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 ];
129 if (!Arr::get($stageData, 'position')) {
130 $lastStagePosition = $this->getLastPositionOfStagesOfBoard($boardId);
131 $stage->position = $lastStagePosition ? $lastStagePosition->position + 1 : 1;
132 $stage->save();
133 } else {
134 $stage->position = (int)$stageData['position'];
135 $stage->save();
136 $this->moveOtherStages($stage);
137 }
138
139 return $stage;
140 }
141
142 public function getLastPositionOfStagesOfBoard($boardId)
143 {
144 // return last position of stages of board
145 return Stage::where('board_id', $boardId)
146 ->whereNull('archived_at')
147 ->orderBy('position', 'desc')
148 ->first();
149 }
150
151 protected function moveOtherStages($stage)
152 {
153
154 $stages = Stage::where('board_id', $stage->board_id)
155 ->where('position', '>=', $stage->position)
156 ->whereNotIn('id', [$stage->id])
157 ->whereNull('archived_at')->get();
158
159 foreach ($stages as $stage) {
160 $stage->position = $stage->position + 1;
161 $stage->save();
162 }
163 }
164
165 public function copyStagesOfBoard($board, $fromBoardId)
166 {
167 $stages = Stage::where('board_id', $fromBoardId)->where('type', 'stage')->whereNull('archived_at')->get();
168 $stageMapForCopyingTask = array();
169 foreach($stages as $key => $stage)
170 {
171 $stageToSave = array();
172 $stageToSave['title'] = $stage['title'];
173 $stageToSave['board_id'] = $board->id;
174 $stageToSave['slug'] = str_replace(' ', '-', strtolower($stage['title']));
175 $stageToSave['type'] = 'stage';
176 $stageToSave['position'] = $key + 1;
177 $stageToSave['settings'] = [
178 'default_task_status' => $stage->settings['default_task_status']
179 ];
180 $newStage = Stage::create($stageToSave);
181 $stageMapForCopyingTask[$stage['id']] = $newStage->id;
182 }
183 return $stageMapForCopyingTask;
184 }
185
186 public function importStagesFromBoard($board_id, $selectedStages)
187 {
188 $targetStages = Stage::whereIn('id', $selectedStages)->get();
189 $stageMapForCopyingTask = array();
190 $stageIdsToCopy = array();
191
192 //need to define position of stage
193 $numberOfStages = Stage::where('board_id', $board_id)->whereNull('archived_at')->count();
194
195 foreach($targetStages as $key => $stage)
196 {
197 $stageToSave = array();
198 $stageToSave['title'] = $stage['title'] . ' - imported';
199 $stageToSave['board_id'] = $board_id;
200 $stageToSave['slug'] = str_replace(' ', '-', strtolower($stage['title']));
201 $stageToSave['type'] = 'stage';
202 $stageToSave['position'] = $numberOfStages + $key + 1;
203 $stageToSave['settings'] = [
204 'default_task_status' => $stage->settings['default_task_status']
205 ];
206 $newStage = Stage::create($stageToSave);
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 public function checkStageEditable($stages, $toBeUpdatedStage)
424 {
425 $stageEditable = true;
426 if (empty(sanitize_text_field($toBeUpdatedStage['title']))) {
427 $stageEditable = false;
428 } else {
429 foreach ($stages as $stage) { // check if stage label is similar to any other stage label
430 if (strtolower($stage->title) == strtolower(trim($toBeUpdatedStage['title']))) {
431 $stageEditable = false;
432 break;
433 }
434 }
435 }
436
437 return $stageEditable;
438 }
439
440 public function updateStage($updatedStage, $board_id, $oldStage)
441 {
442 $stageBeforeUpdate = clone $oldStage;
443 $oldStage->title = Arr::get($updatedStage, 'title');
444 $oldStage->bg_color = Arr::get($updatedStage, 'cover_bg');
445 $oldStage->save();
446 do_action('fluent_boards/stage_updated', $board_id, $updatedStage, $stageBeforeUpdate);
447 return $oldStage;
448 }
449 }
450