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 / TaskHandler.php

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

276 lines 8.7 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 DateTimeImmutable;
6 use Exception;
7 use FluentBoards\App\App;
8 use FluentBoards\App\Models\Activity;
9 use FluentBoards\App\Models\Meta;
10 use FluentBoards\App\Models\TaskMeta;
11 use FluentBoards\App\Models\Task;
12 use FluentBoards\App\Models\User;
13 use FluentBoards\App\Services\Constant;
14 use FluentBoards\App\Services\NotificationService;
15 use FluentCrm\App\Models\Subscriber;
16 use FluentBoards\App\Models\Relation;
17 use FluentCrm\App\Services\ContactsQuery;
18 use FluentCrm\App\Services\PermissionManager as CRMPermissionManager;
19
20 class TaskHandler
21 {
22 private $fileHandler;
23
24 public function __construct(FileHandler $fileHandler)
25 {
26 $this->fileHandler = $fileHandler;
27 }
28 public function taskDeleted($task)
29 {
30 // Delete all activities and comments related to this task
31 Activity::where('object_id', $task->id)->where('object_type', Constant::ACTIVITY_TASK)->delete();
32 Meta::where('object_id', $task->id)->where('object_type', Constant::REPEAT_TASK_META)->delete();
33 }
34
35 public function searchAssignees($options, $search, $includedIds)
36 {
37 $users = get_users([
38 'number' => 20,
39 'search' => $search,
40 ]);
41
42 $formattedUsers = [];
43
44 $pushedIds = [];
45
46 foreach ($users as $user) {
47 $pushedIds[] = $user->ID;
48
49 $formattedUsers[] = [
50 'id' => $user->ID,
51 'title' => $user->display_name . ' (' . $user->user_email . ')',
52 'photo' => get_avatar_url($user->user_email),
53 'left_side_value' => $user->display_name,
54 'right_side_value' => $user->user_email,
55 ];
56 }
57
58 if (!$includedIds) {
59 return $formattedUsers;
60 }
61
62 if (!is_array($includedIds)) {
63 $includedIds = [$includedIds];
64 }
65
66 $includedIds = array_diff(array_map('intval', $includedIds), $pushedIds);
67
68 if ($includedIds) {
69 $users = get_users([
70 'include' => $includedIds,
71 ]);
72
73 foreach ($users as $user) {
74 $formattedUsers[] = [
75 'id' => $user->ID,
76 'title' => $user->display_name . ' (' . $user->user_email . ')',
77 'photo' => get_avatar_url($user->user_email),
78 'left_side_value' => $user->display_name,
79 'right_side_value' => $user->user_email,
80 ];
81 }
82 }
83
84 return $formattedUsers;
85 }
86
87 public function searchNonBoardWordpressUsers()
88 {
89 // This enumerates every WordPress user with their email; only user managers may see it.
90 if (!current_user_can('list_users')) {
91 return [];
92 }
93
94 $superAdmin = Relation::select('user_id')->distinct()->pluck('user_id');
95 $users = User::whereDoesntHave('boards')->whereNotIn('ID', $superAdmin)->get();
96
97 $formattedUsers = [];
98
99 foreach ($users as $user) {
100 $formattedUsers[] = [
101 'id' => $user->ID,
102 'photo' => $user->photo,
103 'title' => $user->display_name . ' (' . $user->user_email . ')',
104 'left_side_value' => $user->display_name,
105 'right_side_value' => $user->user_email,
106 ];
107 }
108
109 return $formattedUsers;
110 }
111
112 public function searchContact($options, $search, $includedIds)
113 {
114 if (!defined('FLUENTCRM')) {
115 return [];
116 }
117
118 // check if the use has permission to access contacts
119 $contactPermission = CRMPermissionManager::currentUserCan('fcrm_read_contacts');
120 if(!$contactPermission) {
121 return [];
122 }
123
124 $contactsQuery = new ContactsQuery([
125 'search' => $search,
126 'with' => [],
127 'limit' => 20,
128 'offset' => 0,
129 ]);
130 $subscribers = $contactsQuery->get();
131
132 $formattedSubscribers = [];
133
134 $pushedIs = [];
135 foreach ($subscribers as $subscriber) {
136 $pushedIs[] = $subscriber->id;
137 $formattedSubscribers[] = [
138 'id' => $subscriber->id,
139 'title' => $subscriber->full_name . ' (' . $subscriber->email . ')',
140 'photo' => $subscriber->photo,
141 'name' => $subscriber->full_name,
142 'email' => $subscriber->email,
143 ];
144 }
145
146 if (!is_array($includedIds)) {
147 $includedIds = [$includedIds];
148 }
149
150 if (!$includedIds) {
151 return $formattedSubscribers;
152 }
153
154 $remainingIds = array_diff($includedIds, $pushedIs);
155
156 if ($remainingIds) {
157 $remainingContacts = Subscriber::whereIn('id', $remainingIds)->get();
158 foreach ($remainingContacts as $subscriber) {
159 $formattedSubscribers[] = [
160 'id' => $subscriber->id,
161 'title' => $subscriber->full_name . ' (' . $subscriber->email . ')',
162 'photo' => $subscriber->photo,
163 'name' => $subscriber->full_name,
164 'email' => $subscriber->email,
165 ];
166 }
167 }
168
169 return $formattedSubscribers;
170 }
171
172
173
174 /**
175 * Delete an attachment file only after the owning database transaction commits.
176 *
177 * @param mixed $deletedAttachment
178 * @param int|null $boardId
179 * @return void
180 */
181 public function taskAttachmentDeleted($deletedAttachment, $boardId = null)
182 {
183 if ($deletedAttachment->attachment_type === 'url') {
184 return;
185 }
186
187 if ($boardId === null) {
188 $task = Task::find(absint($deletedAttachment->object_id));
189 $boardId = $task ? $task->board_id : null;
190 }
191
192 $dbInstance = App::getInstance('db');
193
194 if ($dbInstance->inTransaction()) {
195 $attachmentSnapshot = clone $deletedAttachment;
196 $dbInstance->afterCommit(function () use ($attachmentSnapshot, $boardId) {
197 try {
198 $this->fileHandler->deleteAttachmentFile($attachmentSnapshot, $boardId);
199 } catch (\Throwable $e) {
200 error_log(sprintf(
201 'FluentBoards: Failed to delete committed task attachment file: %s',
202 sanitize_text_field($e->getMessage())
203 ));
204 }
205 });
206
207 return;
208 }
209
210 try {
211 $this->fileHandler->deleteAttachmentFile($deletedAttachment, $boardId);
212 } catch (Exception $e) {
213 wp_send_json_error($e->getMessage());
214 }
215 }
216
217 /**
218 * Auto-watch newly created tasks when the board preference allows it.
219 */
220 public function onTaskCreated($task)
221 {
222 $userId = get_current_user_id();
223
224 if (!$userId) {
225 return;
226 }
227
228 if ($this->shouldAutoWatchForBoard($task->board_id, Constant::GLOBAL_EMAIL_NOTIFICATION_CREATING_TASK)) {
229 $task->watchers()->syncWithoutDetaching([$userId => ['object_type' => Constant::OBJECT_TYPE_USER_TASK_WATCH]]);
230 }
231 }
232
233 /**
234 * Auto-watch commented tasks when the board preference allows it.
235 */
236 public function onCommentCreated($comment)
237 {
238 $task = $comment->task;
239 $userId = get_current_user_id();
240
241 if (!$userId) {
242 return;
243 }
244
245 if ($this->shouldAutoWatchForBoard($task->board_id, Constant::GLOBAL_EMAIL_NOTIFICATION_COMMENTING)) {
246 $task->watchers()->syncWithoutDetaching([$userId => ['object_type' => Constant::OBJECT_TYPE_USER_TASK_WATCH]]);
247 }
248 }
249
250 /**
251 * Auto-watch assigned tasks when the board preference allows it.
252 */
253 public function onAssignAnotherUser($task, $assigneeId)
254 {
255 if ($this->shouldAutoWatchForBoard($task->board_id, Constant::GLOBAL_EMAIL_NOTIFICATION_ASSIGNING)) {
256 $task->watchers()->syncWithoutDetaching([$assigneeId => ['object_type' => Constant::OBJECT_TYPE_USER_TASK_WATCH]]);
257 }
258 }
259
260 /**
261 * Determine auto-watch behavior from the current user's board preference.
262 */
263 private function shouldAutoWatchForBoard($boardId, $preferenceKey)
264 {
265 return (new NotificationService())->isBoardAutoWatchEnabled($boardId, get_current_user_id(), $preferenceKey);
266 }
267
268 public function taskCloned($originalTask, $clonedTask)
269 {
270 Activity::where('object_id', $clonedTask->id)
271 ->where('object_type', Constant::ACTIVITY_TASK)
272 ->delete();
273 do_action('fluent_boards/task_cloned_activity', $originalTask, $clonedTask);
274 }
275 }
276