PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.1.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.1.0
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 / Hooks / Handlers / ActivityHandler.php

ActivityHandler.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 2.1.0, at app/Hooks/Handlers/ActivityHandler.php

479 lines 16.3 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\Hooks\Handlers;
4
5 use FluentBoards\App\Models\Board;
6 use FluentBoards\App\Models\Stage;
7 use FluentBoards\App\Models\Task;
8 use FluentBoards\App\Models\User;
9 use FluentBoards\App\Services\Constant;
10 use FluentCrm\App\Models\Subscriber;
11 use FluentBoards\App\Models\Comment;
12 use FluentBoards\App\Services\Helper;
13
14 class ActivityHandler
15 {
16 public function createLogActivity($taskId, $action, $column, $oldValue = null, $newValue = null, $description = null, $settings = null )
17 {
18 $data = [
19 'object_type' => Constant::ACTIVITY_TASK,
20 'object_id' => $taskId,
21 'action' => $action, // action type: changed, updated, added, removed
22 'column' => $column,
23 'old_value' => $oldValue,
24 'new_value' => $newValue,
25 'description' => $description,
26 'settings' => $settings
27 ];
28 $userId = get_current_user_id();
29 if($userId == 0){
30 $task = Task::find($taskId);
31 $data['created_by'] = $task->created_by;
32 }
33
34 Helper::createActivity($data);
35 }
36 public function logMoveTaskToAnotherBoardActivity($task, $oldTask)
37 {
38 $old = $this->getBoardTitleById($oldTask->board_id);
39 $new = $this->getBoardTitleById($task->board_id);
40 $this->createLogActivity( $task->id,'changed', 'board', $old, $new);
41 }
42
43 public function logAssigneeAddedActivity($task, $newAssigneeId)
44 {
45 $currentUserId = get_current_user_id();
46 if($newAssigneeId == $currentUserId) {
47 $this->taskAssigneeJoin($task->id);
48 return;
49 }
50 $user = User::findOrFail($newAssigneeId);
51 $currentUserId = get_current_user_id();
52 if($currentUserId === $user->ID){
53 $this->taskAssigneeJoin($task->id);
54 } elseif ($user) {
55 $this->createLogActivity($task->id, 'added', 'assignee', null, $user->display_name);
56 }
57 }
58
59 public function logAssigneeRemovedActivity($task, $newAssigneeId)
60 {
61 $currentUserId = get_current_user_id();
62 if($newAssigneeId == $currentUserId) {
63 $this->taskAssigneeLeave($task->id);
64 return;
65 }
66 $user = User::findOrFail($newAssigneeId);
67 $currentUserId = get_current_user_id();
68 if($currentUserId === $user->ID){
69 $this->taskAssigneeLeave($task->id);
70 } else if($user) {
71 $this->createLogActivity($task->id, 'removed', 'assignee', null, $user->display_name);
72 }
73 }
74
75 public function logTaskCreationActivity($task)
76 {
77 $this->createLogActivity( $task->id,'created', 'task', null, $task->title, null );
78 }
79
80 public function logTaskContentUpdatedActivity($task, $col, $oldTask = null)
81 {
82 // Description autosaves are intentionally excluded to avoid flooding the activity feed.
83 if ($col === 'description') {
84 return;
85 }
86
87 $action = 'updated';
88 if($task->parent_id){
89 $this->createLogActivity( $task->parent_id, $action, 'subtask', $oldTask->title, $task->title );
90 }else{
91 $this->createLogActivity( $task->id, $action, $col, $oldTask->title, $task->title );
92 }
93 }
94
95 public function taskLabelActivity($task, $label, $action)
96 {
97 $column = 'label';
98 $settings = [
99 'bg_color' => $label->bg_color,
100 'color' => $label->color,
101 'title' => $label->title,
102 ];
103
104 $this->createLogActivity($task->id, $action, $column, null, null, null, $settings);
105 }
106
107 public function logDueDateActivity($task, $oldDate)
108 {
109 $column = 'Due Date';
110 $action = 'changed';
111
112 if($oldDate){
113 $new = gmdate("F j, Y, g:i a", strtotime($task->due_at));
114 $old = gmdate("F j, Y, g:i a", strtotime($oldDate));
115 if($new == $old)
116 return;
117 }
118 $newDueDate = $task->due_at;
119 $new = null;
120 if ($newDueDate) {
121 $new = gmdate("F j, Y, g:i a", strtotime($newDueDate));
122 if(str_contains($new, '12:00 am')){
123 $new = gmdate("F j, Y", strtotime($newDueDate));
124 }
125 }
126 $old = null;
127 if(!$oldDate){
128 $action = 'added';
129 }else{
130 $old = gmdate("F j, Y, g:i a", strtotime($oldDate));
131 if(str_contains($old, '12:00 am')){
132 $old = gmdate("F j, Y", strtotime($oldDate));
133 }
134 }
135
136 $this->createLogActivity($task->id, $action, $column, $old, $new);
137 }
138 public function logDueDateRemoveActivity($task)
139 {
140 $this->createLogActivity( $task->id, 'removed', 'Due Date', null);
141 }
142
143 public function logStartDateActivity($task, $oldDate)
144 {
145 $column = 'Start Date';
146 $action = 'changed';
147
148 if($oldDate){
149 $new = gmdate("F j, Y", strtotime($task->started_at));
150 $old = gmdate("F j, Y", strtotime($oldDate));
151 if($new == $old)
152 return;
153 }
154 $newStartDate = $task->started_at;
155 $new = null;
156 if ($newStartDate) {
157 $new = gmdate("F j, Y", strtotime($newStartDate));
158 }
159 $old = null;
160 if(!$oldDate){
161 $action = 'added';
162 }else{
163 $old = gmdate("F j, Y", strtotime($oldDate));
164 }
165
166 $this->createLogActivity($task->id, $action, $column, $old, $new);
167 }
168
169
170 public function logPriorityChangeActivity($task, $old)
171 {
172 $new = $task->priority;
173 $this->createLogActivity($task->id, 'changed', 'priority', $old, $new);
174 }
175
176 public function logCommentCreateActivity($comment)
177 {
178 if($comment->parent_id) {
179 // dd($comment);
180 $parent = Comment::findOrFail($comment->parent_id);
181 $parentDescription = $parent->description;
182 $this->createLogActivity($comment->task_id, 'added', 'a reply');
183 }else{
184 $commentPlainText = wp_strip_all_tags($comment->description);
185 $this->createLogActivity($comment->task_id, 'added', 'comment', null, $commentPlainText, null);
186 }
187 }
188 public function logCommentUpdateActivity($comment, $oldComment)
189 {
190 $taskId = $comment->task_id;
191 $newComment = $comment->settings['raw_description'] ?? $comment->description;
192
193 // Activity previews are plain text, while comment drafts now contain inline HTML.
194 $plainText = static function ($content) {
195 $content = preg_replace('~<br\s*/?>|</p\s*>~i', "\n", $content);
196 return trim(html_entity_decode(wp_strip_all_tags($content), ENT_QUOTES | ENT_HTML5, 'UTF-8'));
197 };
198
199 $this->createLogActivity($taskId, 'updated', 'comment', $plainText($oldComment), $plainText($newComment));
200 }
201 public function logCommentDeleteActivity($comment)
202 {
203 $commentDescription = wp_strip_all_tags($comment->settings['raw_description'] ?? $comment->description );
204 $taskId = $comment->task_id;
205
206 $this->createLogActivity($taskId, 'removed', 'comment', null, $commentDescription, null);
207 }
208
209 public function logSubtaskAddedActivity($parentTask, $subTask)
210 {
211 $this->createLogActivity($parentTask->id, 'added', 'subtask', null, $subTask->title);
212 }
213
214 public function logSubtaskCloneActivity($parentTask, $subTask)
215 {
216 $this->createLogActivity($parentTask->id, 'cloned', 'subtask', null, $subTask->title);
217 }
218 public function logSubtaskGroupAddedActivity($task_id, $subTaskGroup)
219 {
220 $this->createLogActivity($task_id, 'added', 'subtask group', null, $subTaskGroup->value);
221 }
222
223 public function logSubtaskDeletedActivity($id, $subTaskTitle)
224 {
225 $this->createLogActivity($id, 'removed', 'subtask', null, $subTaskTitle);
226 }
227
228 public function logSubtaskGroupDeletedActivity($task_id, $subTaskGroup)
229 {
230 $this->createLogActivity($task_id, 'removed', 'subtask group', null, $subTaskGroup->value);
231 }
232 public function logSubtaskGroupTitleUpdatedActivity($oldTitle, $group)
233 {
234 $this->createLogActivity($group->task_id, 'changed', 'subtask group title', $oldTitle, $group->value);
235 }
236 public function logTaskCompletedOrReopenActivity($task, $status)
237 {
238 if($status == 'closed'){
239 if($task->parent_id){
240 $this->createLogActivity($task->parent_id, 'closed', 'subtask', $task->title);
241 }else{
242 $this->createLogActivity($task->id, 'closed', 'task', $task->title);
243 }
244 }else{
245 if($task->parent_id){
246 $this->createLogActivity($task->parent_id, 'reopened', 'subtask', $task->title);
247 }else{
248 $this->createLogActivity($task->id, 'reopened', 'task', $task->title);
249 }
250 }
251 }
252
253 public function logTaskStageUpdatedActivity($task, $oldStageId, $stageContext = null)
254 {
255 $oldStage = $stageContext['source'] ?? null;
256 $newStage = $stageContext['target'] ?? null;
257
258 if (!$oldStage instanceof Stage || !$newStage instanceof Stage) {
259 $oldStage = Stage::findOrFail($oldStageId);
260 $newStage = Stage::findOrFail($task->stage_id);
261 }
262
263 if($oldStage && $newStage){
264 $this->createLogActivity($task->id, 'changed', 'stage', $oldStage->title, $newStage->title);
265 }
266 }
267
268 public function associateUserAddChangeRemoveActivity($oldAssociateId, $newAssociateId, $taskId)
269 {
270 $subsciber = new Subscriber();
271 $oldAssociateUser = $subsciber->find($oldAssociateId);
272 $newAssociateUser = $subsciber->find($newAssociateId);
273 $action = 'added';
274 $column = 'the associate email';
275 $old = null; $new = null;
276
277 if ($oldAssociateUser && $newAssociateUser) {
278 $old = $oldAssociateUser->email;
279 $new = $newAssociateUser->email;
280 $action = 'changed';
281 } elseif ($oldAssociateUser && !$newAssociateUser) {
282 $old = $oldAssociateUser->email;
283 $action = 'removed';
284 } else {
285 $new = $newAssociateUser->email;
286 }
287 $this->createLogActivity($taskId, $action, $column, $old, $new);
288 }
289
290 public function taskAssigneeJoin($id)
291 {
292 $this->createLogActivity($id, 'joined', 'task', null, null);
293 }
294
295 public function taskAssigneeLeave($id)
296 {
297 $this->createLogActivity($id, 'left', 'task', null, null);
298 }
299
300 public function labelManageForTaskActivity($id, $action)
301 {
302 $this->createLogActivity($id, $action, 'label', null, null);
303 }
304
305 private function getBoardTitleById($boardId)
306 {
307 return Board::findOrFail($boardId)->title; // it can be further optimized , we may limit multiple query here.
308 }
309
310 public function taskAddedFromFluentForms($task)
311 {
312 $taskActivity = [
313 'object_type' => Constant::ACTIVITY_TASK,
314 'object_id' => $task->id,
315 'action' => 'created', // action type: changed, updated, added, removed
316 'column' => 'task',
317 'old_value' => $task->title,
318 'new_value' => null,
319 'description' => " from Fluent Forms",
320 ];
321
322 $board = Board::find($task->board_id);
323 $settings = $board->settings ?? [];
324
325 if (isset($settings['tasks_count'])) {
326 if ($settings['tasks_count'] != 0) $settings['tasks_count'] += 1;
327 } else {
328 $settings['tasks_count'] = 1;
329 }
330 $board->settings = $settings;
331 $board->save();
332 $taskStage = $task->stage;
333 $settings = ['task_id' => $task->id];
334
335 $boardActivity = [
336 'object_type' => Constant::ACTIVITY_BOARD,
337 'object_id' => $task->board_id,
338 'action' => 'created', // action type: changed, updated, added, removed, created
339 'column' => 'task',
340 'old_value' => null,
341 'new_value' => $task->title,
342 'description' => 'on stage '. $taskStage->title . ' from Fluent Forms ',
343 'settings' => $settings
344 ];
345 if (intval($task->created_by) > 0) {
346 $taskActivity['created_by'] = intval($task->created_by);
347 $boardActivity['created_by'] = intval($task->created_by);
348
349 } else {
350 $taskActivity['description'] .= ' by '. $task['settings']['author']['name'] . " (" . $task['settings']['author']['email'] . ")";
351 $boardActivity['description'] .= ' by '. $task['settings']['author']['name'] . " (" . $task['settings']['author']['email'] . ")";
352 }
353 Helper::createActivity($taskActivity);
354 Helper::createActivity($boardActivity);
355
356 }
357 public function taskAttachmentAdded($attachment)
358 {
359 $this->createLogActivity($attachment->object_id , 'added', 'attachment', null, strlen($attachment->title) > 0 ? $attachment->title : $attachment->full_url);
360 }
361 public function taskAttachmentDeleted($attachment)
362 {
363 $this->createLogActivity($attachment->object_id , 'deleted', 'attachment', null, strlen($attachment->title) > 0 ? $attachment->title : $attachment->full_url);
364 }
365
366 public function taskArchived($task)
367 {
368 if(!$task->archived_at){
369 $this->createLogActivity($task->id, 'restored', 'task', $task->title);
370 }else{
371 $this->createLogActivity($task->id, 'archived', 'task', $task->title);
372 }
373 }
374
375 public function logRepeatTaskCreatedActivity($newTask, $parentTask)
376 {
377 $this->createLogActivity(
378 $newTask->id,
379 'created',
380 'repeat task',
381 $newTask->title,
382 $parentTask->title,
383 null,
384 $parentTask
385 );
386 }
387
388 public function logRepeatTaskSet($newTask)
389 {
390 $this->createLogActivity(
391 $newTask->id,
392 'set',
393 'Repeat Task',
394 null,
395 null,
396 );
397 }
398
399 public function logRepeatTaskUpdated($newTask)
400 {
401 $this->createLogActivity(
402 $newTask->id,
403 'updated',
404 'Repeat Task',
405 null,
406 null
407 );
408 }
409 public function taskMovedFromBoard($task, $oldBoard, $newBoard)
410 {
411 $this->createLogActivity($task->id, 'moved', 'board', $oldBoard->title, $newBoard->title);
412 }
413
414 public function logCustomFieldActivity($taskId, $customField, $oldValue, $newValue, $isNewCustomField)
415 {
416 // Format values for display in activity
417 $displayNewValue = $this->formatValueForDisplay($newValue, $customField->settings['custom_field_type']);
418
419 $action = 'changed';
420 $column = $customField->title;
421
422 // Ensure we have a value to display
423 if (empty($displayNewValue)) {
424 $displayNewValue = 'empty';
425 }
426
427 // Handle old value display logic
428 $displayOldValue = null;
429 if (!$isNewCustomField && $oldValue !== null && $oldValue !== '') {
430 // Format old value for display if it exists
431 $displayOldValue = $this->formatValueForDisplay($oldValue, $customField->settings['custom_field_type']);
432 }
433
434 $settings = [
435 'custom_field_id' => $customField->id,
436 'custom_field_title' => $customField->title,
437 'custom_field_type' => $customField->settings['custom_field_type']
438 ];
439
440 $this->createLogActivity($taskId, $action, $column, $displayOldValue, $displayNewValue, null, $settings);
441 }
442
443 /**
444 * Format custom field value for display in activity logs
445 */
446 private function formatValueForDisplay($value, $fieldType)
447 {
448 if ($value === null || $value === '') {
449 return 'empty';
450 }
451
452 switch ($fieldType) {
453 case 'checkbox':
454 return $value ? 'checked' : 'unchecked';
455 case 'date':
456 // If it's already formatted as Y-m-d H:i:s, convert to readable format
457 if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $value)) {
458 return gmdate('M j, Y g:i A', strtotime($value));
459 }
460 return $value;
461 case 'select':
462 case 'text':
463 case 'textarea':
464 case 'number':
465 default:
466 return (string) $value;
467 }
468 }
469 public function taskCloned($originalTask, $clonedTask)
470 {
471 $this->createLogActivity(
472 $clonedTask->id,
473 'cloned',
474 'task',
475 'from ' . $originalTask->title,
476 );
477 }
478 }
479