PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.0.0
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.0.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 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.0, at app/Services/UserService.php

498 lines 18.8 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()->with($taskRelations)->whereNull('archived_at')->whereNull('parent_id')->whereIn('board_id', $allowedBoardIds);
249 } else if($taskType == 'mentioned') {
250 $tasksQuery = $user->mentionedTasks()->with($taskRelations)->whereNull('archived_at')->whereNull('parent_id')->whereIn('board_id', $allowedBoardIds);
251 } else {
252 // Get the task assigned to the user
253 $tasksQuery = $user->tasks()->with($taskRelations)->whereNull('archived_at') ->whereIn('board_id', $allowedBoardIds);
254
255 switch ($taskType) {
256 case 'upcoming':
257 $tasksQuery->upcoming()
258 ->whereIn('board_id', $allowedBoardIds);
259 break;
260 case 'due_today':
261 $tasksQuery->dueToday()
262 ->whereIn('board_id', $allowedBoardIds);
263 break;
264 case 'overdue':
265 $tasksQuery->overdue()
266 ->whereIn('board_id', $allowedBoardIds);
267 break;
268 case 'completed':
269 $tasksQuery->where('status', 'closed')
270 ->whereIn('board_id', $allowedBoardIds);
271 break;
272 default:
273 $tasksQuery->whereNull('due_at')
274 ->whereIn('board_id', $allowedBoardIds);
275 break;
276 }
277 }
278
279
280
281 $currentUserId = get_current_user_id();
282 if ($currentUserId != $user->ID && !PermissionManager::isAdmin()) {
283 $currentUser = User::find($currentUserId);
284 $currentUserBoardIds = $currentUser->boards->pluck('id')->toArray();
285
286 if (empty($boardIds)) {
287 $boardIds = $currentUserBoardIds;
288 } else {
289 // Get the intersection of the two arrays
290 $boardIds = array_intersect($boardIds, $currentUserBoardIds);
291 }
292 }
293
294 if (!empty($boardIds)) {
295 $tasksQuery->whereIn('board_id', $boardIds);
296 }
297
298 $sortOptions = ['priority', 'due_at', 'position', 'created_at', 'title'];
299 $orderOptions = ['ASC', 'DESC'];
300
301 // Validate order and orderBy parameters
302 if (!in_array($order, $orderOptions) || !in_array($orderBy, $sortOptions)) {
303 throw new \Exception(esc_html__('Invalid sort or orderBy parameter', 'fluent-boards'));
304 }
305
306 // Apply ordering based on the specified order and orderBy
307 if ($orderBy === 'priority') {
308 $tasksQuery->orderByRaw("FIELD(priority, 'urgent', 'high', 'medium', 'low') {$order}");
309 } else if ($orderBy === 'due_at') {
310 $tasksQuery->orderByRaw("ISNULL(due_at), due_at {$order}");
311 } else {
312 $tasksQuery->orderBy($orderBy, $order);
313 }
314
315 $tasks = $tasksQuery->paginate($per_page, ['*'], 'page', $page);
316
317 return [
318 'tasks' => $tasks->values()->toArray(),
319 'paginationInfo' => [
320 'current_page' => $tasks->currentPage(),
321 'last_page' => $tasks->lastPage(),
322 'per_page' => (int) $tasks->perPage(),
323 'total' => $tasks->total(),
324 ],
325 ];
326 }
327
328 /**
329 * Get totals for each task category available in the profile task tabs.
330 */
331 public function getMemberTaskCounts($userId, $boardIds = [])
332 {
333 $user = User::find($userId);
334 $currentUserId = get_current_user_id();
335 $allowedBoardIds = PermissionManager::getBoardIdsForUser($currentUserId);
336
337 if (!$user || empty($allowedBoardIds)) {
338 return array_fill_keys(
339 ['due_today', 'assigned', 'upcoming', 'overdue', 'mentioned', 'completed', 'others'],
340 0
341 );
342 }
343
344 if ($currentUserId != $user->ID && !PermissionManager::isAdmin()) {
345 $currentUser = User::find($currentUserId);
346 $currentUserBoardIds = $currentUser->boards->pluck('id')->toArray();
347 $boardIds = empty($boardIds)
348 ? $currentUserBoardIds
349 : array_intersect($boardIds, $currentUserBoardIds);
350 }
351
352 // Keep the count queries aligned with the profile list without loading task models or relations.
353 $applyTaskScope = function ($query) use ($allowedBoardIds, $boardIds) {
354 $query->whereNull('archived_at')
355 ->whereNull('parent_id')
356 ->whereIn('board_id', $allowedBoardIds);
357
358 if (!empty($boardIds)) {
359 $query->whereIn('board_id', $boardIds);
360 }
361
362 return $query;
363 };
364 $watchedTasks = function () use ($user, $applyTaskScope) {
365 return $applyTaskScope($user->tasks());
366 };
367
368 return [
369 'due_today' => (int) $watchedTasks()->dueToday()->count(),
370 'assigned' => (int) $applyTaskScope($user->assignedTasks())->count(),
371 'upcoming' => (int) $watchedTasks()->upcoming()->count(),
372 'overdue' => (int) $watchedTasks()->overdue()->count(),
373 'mentioned' => (int) $applyTaskScope($user->mentionedTasks())->count(),
374 'completed' => (int) $watchedTasks()->where('status', 'closed')->count(),
375 'others' => (int) $watchedTasks()->whereNull('due_at')->count(),
376 ];
377 }
378
379
380 public function getMemberRelatedAcitivies($user_id, $page)
381 {
382 $activities = Activity::query()->where('created_by', $user_id)
383 ->orderBy('created_at', 'desc')
384 ->with('user')->paginate(40, ['*'], 'page', $page);
385
386 $activitiesToShow = array();
387
388 foreach ($activities as $activity) {
389 if ($activity->object_type == Constant::ACTIVITY_BOARD) {
390 $activity->load('board');
391 // if($activity->settings && $activity->settings['task_id']){
392 // $activity->task = Task::findOrFail($activity->settings['task_id']);
393 // }
394 if (PermissionManager::userHasPermission($activity->board_id, get_current_user_id())) {
395 $activitiesToShow[] = $activity;
396 }
397 } elseif ($activity->object_type == Constant::ACTIVITY_TASK) {
398 $activity->load('task');
399 if ($activity->task && PermissionManager::userHasPermission($activity->task->board_id, get_current_user_id())) {
400 $activitiesToShow[] = $activity;
401 }
402 }
403 }
404 return [
405 'activities' => $activitiesToShow,
406 'pagination' => $activities->toArray(),
407 ];
408 }
409
410 private function getTaskById($taskId)
411 {
412 return Task::findOrFail($taskId);
413 }
414
415 public function getMemberBoards($user_id)
416 {
417 if(!$user_id) {
418 return [];
419 }
420 $boardIds = [];
421 $boards = [];
422 $user = User::find($user_id);
423 $currentUserId = get_current_user_id();
424 if($currentUserId != $user->ID) {
425 if(!PermissionManager::isAdmin()){
426 $currentUser = User::find($currentUserId);
427 $currentUserBoardIds = $currentUser->whichBoards->pluck('id')->toArray();
428 $boardIds = $currentUserBoardIds;
429 }
430 }
431 if (!empty($boardIds)) {
432 $boards = $user->whichBoards()->whereIn('fbs_boards.id', $boardIds)->get();
433 } else {
434 $boards = $user->whichBoards;
435 }
436 return $boards;
437 }
438
439 /**
440 * Returns four aggregate counts for a member's stats widget.
441 *
442 * Cross-user access is scoped to boards shared with the requesting user,
443 * matching the contract of getMemberBoards() and getMemberAssociatedTasks().
444 * Unread notifications are personal and only returned for self or admin.
445 */
446 public function getMemberStats(int $user_id): array
447 {
448 $empty = ['assigned_tasks' => 0, 'completed_tasks' => 0, 'total_boards' => 0, 'unread_notifications' => 0];
449
450 $user = User::find($user_id);
451 if (!$user) {
452 return $empty;
453 }
454
455 $currentUserId = get_current_user_id();
456 $isSelfOrAdmin = ($currentUserId === $user_id) || PermissionManager::isAdmin($currentUserId);
457
458 // For cross-user access, restrict counts to boards the requesting user can also see.
459 $allowedBoardIds = null;
460 if (!$isSelfOrAdmin) {
461 $currentUser = User::find($currentUserId);
462 $allowedBoardIds = $currentUser ? $currentUser->whichBoards->pluck('id')->toArray() : [];
463 if (empty($allowedBoardIds)) {
464 return $empty;
465 }
466 }
467
468 $assignedBase = $user->assignedTasks()->whereNull('archived_at')->whereNull('parent_id');
469 if ($allowedBoardIds !== null) {
470 $assignedBase->whereIn('board_id', $allowedBoardIds);
471 }
472
473 $boardsQuery = $user->whichBoards();
474 if ($allowedBoardIds !== null) {
475 $boardsQuery->whereIn('fbs_boards.id', $allowedBoardIds);
476 }
477
478 // Use Notification model with object_type guard — matches NotificationService::newNotificationNumber().
479 // Notifications are personal; return 0 when a non-admin views another member's profile.
480 $unreadNotifications = 0;
481 if ($isSelfOrAdmin) {
482 $unreadNotifications = Notification::query()
483 ->where('object_type', Constant::OBJECT_TYPE_BOARD_NOTIFICATION)
484 ->whereHas('users', function ($q) use ($user_id) {
485 $q->where('user_id', $user_id)->whereNull('marked_read_at');
486 })
487 ->count();
488 }
489
490 return [
491 'assigned_tasks' => (int) (clone $assignedBase)->where('status', '!=', 'closed')->count(),
492 'completed_tasks' => (int) (clone $assignedBase)->where('status', 'closed')->count(),
493 'total_boards' => (int) $boardsQuery->count(),
494 'unread_notifications' => (int) $unreadNotifications,
495 ];
496 }
497 }
498