PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.0.4
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.0.4
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 / UserService.php

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

514 lines 19.4 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\Activity;
6 use FluentBoards\App\Models\Meta;
7 use FluentBoards\App\Models\Notification;
8 use FluentBoards\App\Models\Task;
9 use FluentBoards\App\Models\User;
10 use FluentBoards\App\Models\Board;
11 use FluentBoards\App\Models\Relation;
12 use FluentBoards\Framework\Support\Arr;
13
14
15 class UserService
16 {
17 public function allFluentBoardsUsers($boardId = null)
18 {
19 // Get the global admins first
20 $adminUserIds = Meta::query()->where('object_type', Constant::FLUENT_BOARD_ADMIN)
21 ->get()->pluck('object_id')->toArray();
22
23 $boardObjects = Relation::where('object_type', 'board_user');
24
25 if ($adminUserIds) {
26 $boardObjects = $boardObjects->whereNotIn('foreign_id', $adminUserIds);
27 }
28
29 if ($boardId) {
30 $boardObjects = $boardObjects->where('object_id', $boardId);
31 }
32
33 $boardObjects = $boardObjects->whereNotIn('foreign_id', $adminUserIds)
34 ->get();
35
36 $boardUserMaps = [];
37
38 $accessBoardIds = [];
39
40 foreach ($boardObjects as $boardObject) {
41 if (!isset($boardUserMaps[$boardObject->foreign_id])) {
42 $boardUserMaps[$boardObject->foreign_id] = [];
43 }
44
45 $accessBoardIds[] = $boardObject->object_id;
46
47 $boardUserMaps[$boardObject->foreign_id][] = [
48 'board_id' => $boardObject->object_id,
49 'role' => Arr::get($boardObject->settings, 'is_admin')
50 ? 'admin'
51 : (Arr::has($boardObject->settings, 'is_viewer_only') && Arr::get($boardObject->settings, 'is_viewer_only')
52 ? 'viewer'
53 : 'member')
54 ];
55 }
56
57 $accessBoardIds = array_unique($accessBoardIds);
58 $allUserIds = array_unique(array_merge($adminUserIds, array_keys($boardUserMaps)));
59
60 $users = get_users([
61 'include' => $allUserIds
62 ]);
63
64 $boardUsers = [];
65 $allBoards = Board::query()->whereIn('id', $accessBoardIds)->get()->keyBy('id');
66
67 foreach ($users as $user) {
68
69 $boards = Arr::get($boardUserMaps, $user->ID, []);
70
71 $formattedBoars = [];
72
73 foreach ($boards as $board) {
74 if (empty($allBoards[$board['board_id']])) {
75 continue;
76 }
77
78 $boardModel = $allBoards[$board['board_id']];
79 $formattedBoars[] = [
80 'id' => $boardModel->id,
81 'title' => $boardModel->title,
82 'role' => $board['role']
83 ];
84 }
85
86 $name = trim($user->first_name . ' ' . $user->last_name);
87
88 if (!$name) {
89 $name = $user->display_name;
90 }
91
92 $photo = fluent_boards_user_avatar($user->user_email, $name);
93
94 $boardUsers[] = [
95 'ID' => $user->ID,
96 'display_name' => $user->display_name,
97 'photo' => $photo,
98 'email' => $user->user_email,
99 'boards' => $formattedBoars,
100 'is_super' => in_array($user->ID, $adminUserIds),
101 'is_wpadmin' => $user->has_cap('manage_options')
102 ];
103 }
104
105 usort($boardUsers, function ($a, $b) {
106 return strcmp($a['display_name'], $b['display_name']);
107 });
108
109 return $boardUsers;
110 }
111
112 public function memberAssociatedTaskUsers($userId)
113 {
114 $user = User::find($userId);
115 $boards = $user->whichBoards->pluck('id');
116 $boardUsers = Relation::whereIn('object_id', $boards)
117 ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)
118 ->pluck('foreign_id')->toArray();
119 $uniqueUsersIds = array_unique($boardUsers);
120 $uniqueUsers = User::whereIn('ID', $uniqueUsersIds)->with('whichBoards')->get();
121
122 $userWiseBoardDesignation = Relation::query()->whereIn('foreign_id', $uniqueUsersIds)
123 ->where('object_type', Constant::OBJECT_TYPE_BOARD_USER)->get();
124
125 $data = array();
126 $data['userWiseBoardDesignation'] = $userWiseBoardDesignation;
127
128 foreach ($uniqueUsers as &$uniqueUser) {
129 if (user_can($uniqueUser['ID'], 'manage_options') && PermissionManager::isFluentBoardsAdmin($uniqueUser['ID'])) {
130 $uniqueUser['is_super'] = true;
131 $uniqueUser['is_wpadmin'] = true;
132 } elseif (user_can($uniqueUser['ID'], 'manage_options')) {
133 $uniqueUser['is_wpadmin'] = true;
134 $uniqueUser['is_super'] = false;
135 } elseif (PermissionManager::isFluentBoardsAdmin($uniqueUser['ID'])) {
136 $uniqueUser['is_super'] = true;
137 $uniqueUser['is_wpadmin'] = false;
138 } else {
139 $uniqueUser['all_boards'] = Arr::get($uniqueUser, 'boards');
140 $uniqueUser['is_super'] = false;
141 $uniqueUser['is_wpadmin'] = false;
142 }
143 }
144
145 $data['uniqueUsers'] = $uniqueUsers;
146
147 return $data;
148 }
149
150 public function searchFluentBoardsUser($search_input)
151 {
152
153 $boardUsers = User::whereHas('whichBoards', function ($query) use ($search_input) {
154 $query->where('display_name', 'like', '%' . $search_input . '%');
155 })
156 ->where('display_name', 'like', '%' . $search_input . '%')
157 ->with('whichBoards')
158 ->get()
159 ->toArray();
160
161 foreach ($boardUsers as &$boardUser) {
162 if (user_can($boardUser['ID'], 'manage_options') && PermissionManager::isFluentBoardsAdmin($boardUser['ID'])) {
163 $boardUser['is_super'] = true;
164 $boardUser['is_wpadmin'] = true;
165 } elseif (user_can($boardUser['ID'], 'manage_options')) {
166 $boardUser['is_wpadmin'] = true;
167 $boardUser['is_super'] = false;
168 } elseif (PermissionManager::isFluentBoardsAdmin($boardUser['ID'])) {
169 $boardUser['is_super'] = true;
170 $boardUser['is_wpadmin'] = false;
171 } else {
172 $boardUser['all_boards'] = Arr::get($boardUser, 'boards');
173 $boardUser['is_super'] = false;
174 $boardUser['is_wpadmin'] = false;
175 }
176 }
177
178 return $boardUsers;
179 }
180
181 public function searchMemberUser($search_input, $userId)
182 {
183 $user = User::find($userId);
184 $boards = $user->boards->pluck('id');
185 $boardUsers = Relation::whereIn('board_id', $boards)->orWhere('board_id', null)->where('status', 'ACTIVE')->pluck('user_id')->toArray();
186 $uniqueUsersIds = array_unique($boardUsers);
187 // $uniqueUsers = User::whereIn('ID', $uniqueUsersIds)->with('boards')->get();
188
189 $searchResult = User::query()->whereIn('ID', $uniqueUsersIds)->with('boards')
190 ->where('display_name', 'like', '%' . $search_input . '%')
191 ->take(20)->get();
192
193 foreach ($searchResult as &$uniqueUser) {
194 if (PermissionManager::isAdmin($uniqueUser['ID'])) {
195 $uniqueUser['all_boards'] = Board::query()->where('is_archived', 0)->get();
196 $isFluentBoardsAdmin = Relation::where('user_id', $uniqueUser['ID'])
197 ->whereNull('board_id')
198 ->where('status', 'ACTIVE')
199 ->first();
200 if ($isFluentBoardsAdmin) {
201 $uniqueUser['is_super'] = true;
202 $uniqueUser['is_wpadmin'] = false;
203 } else {
204 $uniqueUser['is_super'] = false;
205 $uniqueUser['is_wpadmin'] = true;
206 }
207 } else {
208 $uniqueUser['all_boards'] = $uniqueUser['boards'];
209 $uniqueUser['is_super'] = false;
210 $uniqueUser['is_wpadmin'] = false;
211 }
212 }
213
214 return $searchResult;
215 }
216
217
218 public function getMemberAssociatedTasks($user_id, $requestData)
219 {
220 $taskType = $requestData['taskType'] ?? null;
221 $boardIds = !empty($requestData['boardIds']) ? $requestData['boardIds'] : [];
222 $orderBy = $requestData['orderBy'] ?? 'created_at';
223 $order = strtoupper($requestData['order'] ?? 'ASC');
224 $per_page = $requestData['per_page'] ?? 15;
225 $page = $requestData['page'] ?? 1;
226 $user = User::find($user_id);
227
228 $loggedInUserId = get_current_user_id();
229
230 $allowedBoardIds = PermissionManager::getBoardIdsForUser($loggedInUserId);
231
232 if (!$user || empty($allowedBoardIds)) {
233 return [
234 'tasks' => [],
235 'paginationInfo' => [
236 'current_page' => 1,
237 'last_page' => 1,
238 'per_page' => (int) $per_page,
239 'total' => 0,
240 ],
241 ];
242 }
243
244 // Table view needs the labels column; list view ignores the extra relation.
245 $taskRelations = ['stage', 'board', 'labels'];
246
247 if ($taskType == 'assigned') {
248 $tasksQuery = $user->assignedTasks()
249 ->with($taskRelations)
250 ->whereNull('archived_at')
251 ->whereNull('parent_id')
252 ->whereIn('board_id', $allowedBoardIds)
253 ->where('status', '!=', 'closed')
254 ->onActiveAvailableBoards();
255 } else if ($taskType == 'mentioned') {
256 $tasksQuery = $user->mentionedTasks()
257 ->with($taskRelations)
258 ->whereNull('archived_at')
259 ->whereNull('parent_id')
260 ->whereIn('board_id', $allowedBoardIds)
261 ->onActiveAvailableBoards();
262 } else {
263 // Watched tasks power the date-based and completed profile tabs.
264 $tasksQuery = $user->tasks()
265 ->with($taskRelations)
266 ->whereNull('archived_at')
267 ->whereIn('board_id', $allowedBoardIds)
268 ->onActiveAvailableBoards();
269
270 switch ($taskType) {
271 case 'upcoming':
272 $tasksQuery->upcoming()
273 ->whereIn('board_id', $allowedBoardIds);
274 break;
275 case 'due_today':
276 $tasksQuery->dueToday()
277 ->whereIn('board_id', $allowedBoardIds);
278 break;
279 case 'overdue':
280 $tasksQuery->overdue()
281 ->whereIn('board_id', $allowedBoardIds);
282 break;
283 case 'completed':
284 $tasksQuery->where('status', 'closed')
285 ->whereIn('board_id', $allowedBoardIds);
286 break;
287 default:
288 $tasksQuery->whereNull('due_at')
289 ->whereIn('board_id', $allowedBoardIds);
290 break;
291 }
292 }
293
294 $currentUserId = get_current_user_id();
295 if ($currentUserId != $user->ID && !PermissionManager::isAdmin()) {
296 $currentUser = User::find($currentUserId);
297 $currentUserBoardIds = $currentUser->boards->pluck('id')->toArray();
298
299 if (empty($boardIds)) {
300 $boardIds = $currentUserBoardIds;
301 } else {
302 // Get the intersection of the two arrays
303 $boardIds = array_intersect($boardIds, $currentUserBoardIds);
304 }
305 }
306
307 if (!empty($boardIds)) {
308 $tasksQuery->whereIn('board_id', $boardIds);
309 }
310
311 $sortOptions = ['priority', 'due_at', 'position', 'created_at', 'title'];
312 $orderOptions = ['ASC', 'DESC'];
313
314 // Validate order and orderBy parameters
315 if (!in_array($order, $orderOptions) || !in_array($orderBy, $sortOptions)) {
316 throw new \Exception(esc_html__('Invalid sort or orderBy parameter', 'fluent-boards'));
317 }
318
319 // Apply ordering based on the specified order and orderBy
320 if ($orderBy === 'priority') {
321 $tasksQuery->orderByRaw("FIELD(priority, 'urgent', 'high', 'medium', 'low') {$order}");
322 } else if ($orderBy === 'due_at') {
323 $tasksQuery->orderByRaw("ISNULL(due_at), due_at {$order}");
324 } else {
325 $tasksQuery->orderBy($orderBy, $order);
326 }
327
328 $tasks = $tasksQuery->paginate($per_page, ['*'], 'page', $page);
329
330 return [
331 'tasks' => $tasks->values()->toArray(),
332 'paginationInfo' => [
333 'current_page' => $tasks->currentPage(),
334 'last_page' => $tasks->lastPage(),
335 'per_page' => (int) $tasks->perPage(),
336 'total' => $tasks->total(),
337 ],
338 ];
339 }
340
341 /**
342 * Get totals for each task category available in the profile task tabs.
343 */
344 public function getMemberTaskCounts($userId, $boardIds = [])
345 {
346 $user = User::find($userId);
347 $currentUserId = get_current_user_id();
348 $allowedBoardIds = PermissionManager::getBoardIdsForUser($currentUserId);
349
350 if (!$user || empty($allowedBoardIds)) {
351 return array_fill_keys(
352 ['due_today', 'assigned', 'upcoming', 'overdue', 'mentioned', 'completed', 'others'],
353 0
354 );
355 }
356
357 if ($currentUserId != $user->ID && !PermissionManager::isAdmin()) {
358 $currentUser = User::find($currentUserId);
359 $currentUserBoardIds = $currentUser->boards->pluck('id')->toArray();
360 $boardIds = empty($boardIds)
361 ? $currentUserBoardIds
362 : array_intersect($boardIds, $currentUserBoardIds);
363 }
364
365 // Keep the count queries aligned with the profile list without loading task models or relations.
366 $applyTaskScope = function ($query) use ($allowedBoardIds, $boardIds) {
367 $query->whereNull('archived_at')
368 ->whereNull('parent_id')
369 ->whereIn('board_id', $allowedBoardIds)
370 ->onActiveAvailableBoards();
371
372 if (!empty($boardIds)) {
373 $query->whereIn('board_id', $boardIds);
374 }
375
376 return $query;
377 };
378 $watchedTasks = function () use ($user, $applyTaskScope) {
379 return $applyTaskScope($user->tasks());
380 };
381
382 return [
383 'due_today' => (int) $watchedTasks()->dueToday()->count(),
384 'assigned' => (int) $applyTaskScope($user->assignedTasks())
385 ->where('status', '!=', 'closed')
386 ->count(),
387 'upcoming' => (int) $watchedTasks()->upcoming()->count(),
388 'overdue' => (int) $watchedTasks()->overdue()->count(),
389 'mentioned' => (int) $applyTaskScope($user->mentionedTasks())->count(),
390 'completed' => (int) $watchedTasks()->where('status', 'closed')->count(),
391 'others' => (int) $watchedTasks()->whereNull('due_at')->count(),
392 ];
393 }
394
395
396 public function getMemberRelatedAcitivies($user_id, $page)
397 {
398 $activities = Activity::query()->where('created_by', $user_id)
399 ->orderBy('created_at', 'desc')
400 ->with('user')->paginate(40, ['*'], 'page', $page);
401
402 $activitiesToShow = array();
403
404 foreach ($activities as $activity) {
405 if ($activity->object_type == Constant::ACTIVITY_BOARD) {
406 $activity->load('board');
407 // if($activity->settings && $activity->settings['task_id']){
408 // $activity->task = Task::findOrFail($activity->settings['task_id']);
409 // }
410 if (PermissionManager::userHasPermission($activity->board_id, get_current_user_id())) {
411 $activitiesToShow[] = $activity;
412 }
413 } elseif ($activity->object_type == Constant::ACTIVITY_TASK) {
414 $activity->load('task');
415 if ($activity->task && PermissionManager::userHasPermission($activity->task->board_id, get_current_user_id())) {
416 $activitiesToShow[] = $activity;
417 }
418 }
419 }
420 return [
421 'activities' => $activitiesToShow,
422 'pagination' => $activities->toArray(),
423 ];
424 }
425
426 private function getTaskById($taskId)
427 {
428 return Task::findOrFail($taskId);
429 }
430
431 public function getMemberBoards($user_id)
432 {
433 if(!$user_id) {
434 return [];
435 }
436 $boardIds = [];
437 $boards = [];
438 $user = User::find($user_id);
439 $currentUserId = get_current_user_id();
440 if($currentUserId != $user->ID) {
441 if(!PermissionManager::isAdmin()){
442 $currentUser = User::find($currentUserId);
443 $currentUserBoardIds = $currentUser->whichBoards->pluck('id')->toArray();
444 $boardIds = $currentUserBoardIds;
445 }
446 }
447 if (!empty($boardIds)) {
448 $boards = $user->whichBoards()->whereIn('fbs_boards.id', $boardIds)->get();
449 } else {
450 $boards = $user->whichBoards;
451 }
452 return $boards;
453 }
454
455 /**
456 * Returns four aggregate counts for a member's stats widget.
457 *
458 * Cross-user access is scoped to boards shared with the requesting user,
459 * matching the contract of getMemberBoards() and getMemberAssociatedTasks().
460 * Unread notifications are personal and only returned for self or admin.
461 */
462 public function getMemberStats(int $user_id): array
463 {
464 $empty = ['assigned_tasks' => 0, 'completed_tasks' => 0, 'total_boards' => 0, 'unread_notifications' => 0];
465
466 $user = User::find($user_id);
467 if (!$user) {
468 return $empty;
469 }
470
471 $currentUserId = get_current_user_id();
472 $isSelfOrAdmin = ($currentUserId === $user_id) || PermissionManager::isAdmin($currentUserId);
473
474 // For cross-user access, restrict counts to boards the requesting user can also see.
475 $allowedBoardIds = null;
476 if (!$isSelfOrAdmin) {
477 $currentUser = User::find($currentUserId);
478 $allowedBoardIds = $currentUser ? $currentUser->whichBoards->pluck('id')->toArray() : [];
479 if (empty($allowedBoardIds)) {
480 return $empty;
481 }
482 }
483
484 $assignedBase = $user->assignedTasks()->whereNull('archived_at')->whereNull('parent_id');
485 if ($allowedBoardIds !== null) {
486 $assignedBase->whereIn('board_id', $allowedBoardIds);
487 }
488
489 $boardsQuery = $user->whichBoards();
490 if ($allowedBoardIds !== null) {
491 $boardsQuery->whereIn('fbs_boards.id', $allowedBoardIds);
492 }
493
494 // Use Notification model with object_type guard — matches NotificationService::newNotificationNumber().
495 // Notifications are personal; return 0 when a non-admin views another member's profile.
496 $unreadNotifications = 0;
497 if ($isSelfOrAdmin) {
498 $unreadNotifications = Notification::query()
499 ->where('object_type', Constant::OBJECT_TYPE_BOARD_NOTIFICATION)
500 ->whereHas('users', function ($q) use ($user_id) {
501 $q->where('user_id', $user_id)->whereNull('marked_read_at');
502 })
503 ->count();
504 }
505
506 return [
507 'assigned_tasks' => (int) (clone $assignedBase)->where('status', '!=', 'closed')->count(),
508 'completed_tasks' => (int) (clone $assignedBase)->where('status', 'closed')->count(),
509 'total_boards' => (int) $boardsQuery->count(),
510 'unread_notifications' => (int) $unreadNotifications,
511 ];
512 }
513 }
514