PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.5
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.5
2.4.5 2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 All 69 releases
← All changes | app/Modules/PermissionManager.php +25 -53 2.3.0 → 2.4.5 View file →
@@ -7,12 +7,9 @@
7 7 use FluentSupport\App\Services\Tickets\AgentTicketAccess;
8 8 use FluentSupport\Framework\Support\Arr;
9 9
10 10 /**
11 - * PermissionManager class is responsible for getting/settings data related to permission
12 - * @package FluentSupport\App\Modules
13 - *
14 - * @version 1.0.0
11 + * Fluent Support's own agent permissions, stored in user meta.
15 12 */
16 13
17 14 class PermissionManager
18 15 {
@@ -23,9 +20,8 @@
23 20 const VISIBILITY_ASSIGNED_AND_UNASSIGNED = 'assigned_and_unassigned';
24 21 const VISIBILITY_ASSIGNED_ONLY = 'assigned_only';
25 22
26 23 /**
27 - * pluginPermissions method will return the list of permissions support by Fluent Support Plugin
28 24 * @return string[]
29 25 */
30 26 public static function pluginPermissions()
31 27 {
@@ -85,10 +81,9 @@
85 81 return false;
86 82 }
87 83
88 84 /**
89 - * currentUserCan method will return whether a user has the selected permission or not.
90 - * Backward-compatible alias for userCan().
85 + * Alias of userCan().
91 86 *
92 87 * @param $permission
93 88 * @return bool
94 89 */
@@ -97,10 +92,9 @@
97 92 return self::userCan($permission);
98 93 }
99 94
100 95 /**
101 - * attachPermissions method will save selected permissions to user meta.
102 - * Also cleans up any legacy fst_* WordPress capabilities.
96 + * Save permissions to user meta and drop legacy fst_* capabilities.
103 97 *
104 98 * @param $user
105 99 * @param $permissions
106 100 * @return false|mixed
@@ -120,14 +114,16 @@
120 114 }
121 115
122 116 $allPermissions = self::pluginPermissions();
123 117
118 + // Only known permissions. There is no "grant only what you hold" check
119 + // here because AgentPolicy limits agent edits to administrators.
124 120 $permissions = array_values(array_intersect($allPermissions, $permissions));
125 121
126 122 $exclusionRules = self::getExclusionRules();
127 123 $permissions = self::applyExclusionRules($permissions, $exclusionRules);
128 124
129 - // Auto-grant fst_view_tickets when any manage, draft, or approve permission is present
125 + // Manage, draft and approve all imply fst_view_tickets
130 126 $manageOrDraftPermissions = [
131 127 'fst_manage_own_tickets',
132 128 'fst_manage_unassigned_tickets',
133 129 'fst_manage_other_tickets',
@@ -139,12 +135,10 @@
139 135 && !in_array('fst_view_tickets', $permissions)) {
140 136 $permissions[] = 'fst_view_tickets';
141 137 }
142 138
143 - // Store permissions in user meta
144 139 update_user_meta($user->ID, self::META_KEY, array_values($permissions));
145 140
146 - // Clean up legacy WordPress capabilities
147 141 foreach ($allPermissions as $cap) {
148 142 $user->remove_cap($cap);
149 143 }
150 144
@@ -151,9 +145,9 @@
151 145 return $user;
152 146 }
153 147
154 148 /**
155 - * Clean removal of all Fluent Support permissions for a user.
149 + * Remove all Fluent Support permissions from a user.
156 150 *
157 151 * @param int $userId
158 152 * @return void
159 153 */
@@ -160,9 +154,9 @@
160 154 public static function detachPermissions($userId)
161 155 {
162 156 delete_user_meta($userId, self::META_KEY);
163 157
164 - // Clean up any legacy WordPress capabilities
158 + // Legacy capabilities
165 159 $user = get_user_by('ID', $userId);
166 160 if ($user && !user_can($user, 'manage_options')) {
167 161 foreach (self::pluginPermissions() as $cap) {
168 162 $user->remove_cap($cap);
@@ -193,13 +187,10 @@
193 187 * @return array Each key => value pair means: if key is present, remove value.
194 188 */
195 189 public static function getExclusionRules()
196 190 {
197 - // Mutual exclusion rules applied when assigning permissions:
198 - // - If agent has any manage_*_tickets permission, remove fst_draft_reply
199 - // (draft-only mode is for agents who CANNOT manage tickets)
200 - // - If agent has fst_draft_reply, remove fst_approve_draft_reply
201 - // (draft-only agents should not approve their own drafts)
191 + // Draft-only mode is for agents who can't manage tickets, and a
192 + // draft-only agent must not approve their own drafts.
202 193 return [
203 194 'fst_manage_unassigned_tickets' => 'fst_draft_reply',
204 195 'fst_manage_other_tickets' => 'fst_draft_reply',
205 196 'fst_manage_own_tickets' => 'fst_draft_reply',
@@ -228,12 +219,11 @@
228 219 return is_array($permissions) ? $permissions : [];
229 220 }
230 221
231 222 /**
232 - * getUserPermissions method will get all permissions for a user.
233 - * Reads from user meta with legacy wp_capabilities fallback.
223 + * Reads user meta, migrating legacy fst_* capabilities on first read.
234 224 *
235 - * @param false $user
225 + * @param int|\WP_User|false $user
236 226 * @return array|string[]
237 227 */
238 228 public static function getUserPermissions($user = false)
239 229 {
@@ -252,9 +242,8 @@
252 242 $pluginPermission = array_values(array_diff($pluginPermission, ['fst_draft_reply']));
253 243 return $pluginPermission;
254 244 }
255 245
256 - // Read from meta first
257 246 $permissions = self::getMetaPermissions($user->ID);
258 247
259 248 if (!empty($permissions)) {
260 249 return array_values(array_intersect($permissions, $pluginPermission));
@@ -259,16 +248,14 @@
259 248 if (!empty($permissions)) {
260 249 return array_values(array_intersect($permissions, $pluginPermission));
261 250 }
262 251
263 - // Legacy fallback: read from wp_capabilities and migrate
252 + // Legacy: permissions stored as WP capabilities. Move them to meta.
264 253 $legacyPermissions = array_values(array_intersect(array_keys($user->allcaps), $pluginPermission));
265 254
266 255 if (!empty($legacyPermissions)) {
267 - // Migrate to meta
268 256 update_user_meta($user->ID, self::META_KEY, $legacyPermissions);
269 257
270 - // Clean up legacy caps
271 258 foreach ($legacyPermissions as $cap) {
272 259 $user->remove_cap($cap);
273 260 }
274 261 }
@@ -276,9 +263,8 @@
276 263 return $legacyPermissions;
277 264 }
278 265
279 266 /**
280 - * currentUserPermissions method will return the permission of logged-in user
281 267 * @param bool $cached
282 268 * @return array|mixed|string[]
283 269 */
284 270 public static function currentUserPermissions($cached = true)
@@ -294,11 +280,10 @@
294 280 return $permissions;
295 281 }
296 282
297 283 /**
298 - * Determine the WordPress capability string for menu registration.
299 - * Returns 'manage_options' for admins, the user's WP role for agents
300 - * with permissions, or empty string to hide the menu.
284 + * Capability for the admin menu: 'manage_options' for admins, the user's
285 + * role for agents, or '' to hide the menu.
301 286 *
302 287 * @return string
303 288 */
304 289 public static function getMenuPermission()
@@ -314,9 +299,9 @@
314 299 }
315 300
316 301 $metaPermissions = self::getMetaPermissions($userId);
317 302
318 - // Legacy fallback: check wp_capabilities for fst_* caps
303 + // Legacy agents may still have fst_* capabilities instead of meta
319 304 if (empty($metaPermissions)) {
320 305 $user = get_user_by('ID', $userId);
321 306 if ($user) {
322 307 $legacyPermissions = array_intersect(array_keys($user->allcaps), self::pluginPermissions());
@@ -345,10 +330,10 @@
345 330
346 331 }
347 332
348 333 /**
349 - * Whether the current user can perform mutating ticket actions (reply, close, reopen, assign, etc.).
350 - * Draft-only agents return false here — they can view tickets and create drafts but cannot publish.
334 + * Whether the current user can reply, close, assign, etc. False for
335 + * draft-only agents.
351 336 *
352 337 * @return bool
353 338 */
354 339 public static function canManageTickets()
@@ -360,10 +345,9 @@
360 345 ]);
361 346 }
362 347
363 348 /**
364 - * Whether the current user can access ticket API routes at all (read or write).
365 - * Includes manage, merge, draft-only, and view-only agents.
349 + * Whether the current user can reach ticket routes at all, read or write.
366 350 *
367 351 * @return bool
368 352 */
369 353 public static function canAccessTicketRoutes()
@@ -378,19 +362,16 @@
378 362 ]);
379 363 }
380 364
381 365 /**
382 - * Determine ticket visibility level from a permission set.
366 + * Manage permissions win. Otherwise view-only and draft agents see all
367 + * tickets, since they can't modify them.
383 368 *
384 - * Business rule: fst_view_tickets and fst_draft_reply get full visibility because
385 - * read-only and draft agents need to view any ticket, even though they cannot publish.
386 - *
387 369 * @param array $permissions
388 370 * @return string One of the VISIBILITY_* constants.
389 371 */
390 372 private static function resolveTicketVisibility(array $permissions)
391 373 {
392 - // Manage-level permissions take priority for visibility
393 374 if (in_array('fst_manage_other_tickets', $permissions)) {
394 375 return self::VISIBILITY_ALL;
395 376 }
396 377
@@ -401,9 +382,8 @@
401 382 if (in_array('fst_manage_own_tickets', $permissions)) {
402 383 return self::VISIBILITY_ASSIGNED_ONLY;
403 384 }
404 385
405 - // Non-manage roles (draft, view-only) can see all tickets but cannot modify
406 386 if (in_array('fst_draft_reply', $permissions)
407 387 || in_array('fst_view_tickets', $permissions)) {
408 388 return self::VISIBILITY_ALL;
409 389 }
@@ -411,9 +391,8 @@
411 391 return self::VISIBILITY_ASSIGNED_ONLY;
412 392 }
413 393
414 394 /**
415 - * currentTicketVisibility method will return the permission level for a user in tickets
416 395 * @return string
417 396 */
418 397 public static function currentTicketVisibility()
419 398 {
@@ -421,10 +400,9 @@
421 400 return self::resolveTicketVisibility($permissions);
422 401 }
423 402
424 403 /**
425 - * getAgentTicketVisibility method will return the access level of an agent in tickets
426 - * @param false $userId
404 + * @param int|false $userId
427 405 * @return string
428 406 */
429 407 public static function getAgentTicketVisibility($userId = false)
430 408 {
@@ -437,9 +415,10 @@
437 415 return self::resolveTicketVisibility($permissions);
438 416 }
439 417
440 418 /**
441 - * canAccessTicket method will return whether the selected user has permission in selected ticket or not
419 + * Whether the current agent can access this ticket.
420 + *
442 421 * @param $ticket
443 422 * @return bool
444 423 */
445 424 public static function canAccessTicket($ticket)
@@ -447,9 +426,8 @@
447 426 return (new AgentTicketAccess())->currentAgentCanAccess($ticket);
448 427 }
449 428
450 429 /**
451 - * getReadablePermissionGroups method will return the permission group as array
452 430 * @return array[]
453 431 */
454 432 public static function getReadablePermissionGroups()
455 433 {
@@ -500,15 +478,9 @@
500 478 {
501 479 return MailBox::select(['id', 'name'])->get();
502 480 }
503 481
504 - /*
505 - |--------------------------------------------------------------------------
506 - | Deprecated Methods
507 - |--------------------------------------------------------------------------
508 - | These methods are kept for backward compatibility with third-party add-ons.
509 - | They delegate to the renamed replacements and will be removed in a future release.
510 - */
482 + // Deprecated aliases, kept for third-party add-ons.
511 483
512 484 /**
513 485 * @deprecated Use currentTicketVisibility() instead.
514 486 */