PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.95
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.95
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 / BoardMenuHandler.php

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

223 lines 7.0 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\Services\PermissionManager;
7
8 class BoardMenuHandler
9 {
10 public static function getMenuItems()
11 {
12 // Get default menu items with positions
13 $defaultMenuItems = self::getDefaultMenuItems();
14
15 /**
16 * Menu Item Structure:
17 *
18 * Required: key, label, type, icon, html (not for default menu items)
19 * Optional: position, width, render_in, requires_*
20 *
21 * Example:
22 * [
23 * 'key' => 'my_item',
24 * 'label' => 'My Item',
25 * 'type' => 'default',
26 * 'position' => 1,
27 * 'width' => '500px',
28 * 'html' => '<div>Content</div>'
29 * ]
30 */
31
32 // Apply filter to modify all menu items (default + custom)
33 $allMenuItems = apply_filters('fluent_boards/board_menu_items', $defaultMenuItems);
34
35 // Sort by position
36 /**
37 * This ensures that when you add custom menu items with
38 * decimal positions (like 10.5 to insert after duplicate_board at position 10),
39 * they'll be sorted correctly in the final menu order.
40 */
41 usort($allMenuItems, function($a, $b) {
42 $posA = isset($a['position']) ? (float)$a['position'] : 999;
43 $posB = isset($b['position']) ? (float)$b['position'] : 999;
44
45 if ($posA == $posB) {
46 return 0;
47 }
48 return ($posA < $posB) ? -1 : 1;
49 });
50
51 // Apply server-side permission validation to prevent bypass
52 $allMenuItems = self::validateMenuPermissions($allMenuItems);
53
54 return $allMenuItems;
55 }
56
57 private static function getDefaultMenuItems()
58 {
59 return [
60 [
61 'key' => 'about_this_board',
62 'label' => __('About this Board', 'fluent-boards'),
63 'type' => 'default',
64 'position' => 1,
65 'role' => ''
66 ],
67 [
68 'key' => 'board_activity',
69 'label' => __('Board Activity', 'fluent-boards'),
70 'type' => 'default',
71 'position' => 2,
72 'role' => ''
73 ],
74 [
75 'key' => 'change_background',
76 'label' => __('Change Background', 'fluent-boards'),
77 'type' => 'default',
78 'position' => 3,
79 'role' => 'manager'
80 ],
81 [
82 'key' => 'notification_settings',
83 'label' => __('Notification Settings', 'fluent-boards'),
84 'type' => 'default',
85 'position' => 4,
86 'role' => ''
87 ],
88 [
89 'key' => 'board_labels',
90 'label' => __('Board Labels', 'fluent-boards'),
91 'type' => 'default',
92 'position' => 5,
93 'role' => ''
94 ],
95 [
96 'key' => 'custom_fields',
97 'label' => __('Custom Fields', 'fluent-boards'),
98 'type' => 'default',
99 'position' => 6,
100 'role' => ''
101 ],
102 [
103 'key' => 'board_members',
104 'label' => __('Board Members', 'fluent-boards'),
105 'type' => 'default',
106 'position' => 7,
107 'role' => ''
108 ],
109 [
110 'key' => 'archived_items',
111 'label' => __('Archived Items', 'fluent-boards'),
112 'type' => 'default',
113 'position' => 8,
114 'role' => ''
115 ],
116 [
117 'key' => 'webhooks',
118 'label' => __('Webhooks', 'fluent-boards'),
119 'type' => 'default',
120 'position' => 9,
121 'role' => ''
122 ],
123 [
124 'key' => 'associated_crm_contacts',
125 'label' => __('Associated CRM Contacts', 'fluent-boards'),
126 'type' => 'default',
127 'position' => 9,
128 'role' => ''
129 ],
130 [
131 'key' => 'duplicate_board',
132 'label' => __('Duplicate Board', 'fluent-boards'),
133 'type' => 'default',
134 'position' => 10,
135 'role' => 'manager'
136 ],
137 [
138 'key' => 'restore_board',
139 'label' => __('Restore Board', 'fluent-boards'),
140 'type' => 'default',
141 'position' => 10,
142 'role' => 'admin'
143 ],
144 [
145 'key' => 'export',
146 'label' => __('Export', 'fluent-boards'),
147 'type' => 'default',
148 'position' => 10.5,
149 'role' => 'manager',
150 'pro' => true
151 ],
152 [
153 'key' => 'archive_board',
154 'label' => __('Archive Board', 'fluent-boards'),
155 'type' => 'default',
156 'position' => 11,
157 'role' => 'admin'
158 ],
159 [
160 'key' => 'delete_board',
161 'label' => __('Delete Board', 'fluent-boards'),
162 'type' => 'default',
163 'position' => 11,
164 'role' => 'admin'
165 ]
166 ];
167 }
168
169 /**
170 * Validate menu permissions server-side to prevent bypass
171 * Uses key-based validation with optimized permission checking
172 */
173 private static function validateMenuPermissions($menuItems)
174 {
175 $validatedItems = [];
176
177 $isAdmin = PermissionManager::isAdmin();
178
179 foreach ($menuItems as $item) {
180 if (empty($item['key']) || empty($item['label'])) {
181 continue;
182 }
183
184 $key = $item['key'];
185
186 if($key === 'associated_crm_contacts' && !defined('FLUENTCRM')) {
187 continue;
188 }
189
190 // Enforce default item policy if in whitelist
191 if (isset($item['type']) && $item['type'] === 'default') {
192 $requiredRole = $item['role'];
193
194 // Enforce role - only check admin role, remove manager role checks
195 if ($requiredRole === 'admin' && !$isAdmin) {
196 continue;
197 }
198
199 // Enforce pro restriction
200 if (isset($item['pro']) && $item['pro'] === true) {
201 $item['requires_pro'] = true;
202 }
203 } else {
204 // Custom item
205 $item['type'] = 'custom';
206 $requiredRole = $item['role'] ?? '';
207
208 if ($requiredRole === 'admin' && !$isAdmin) {
209 continue;
210 }
211
212 // Optional: validate width
213 if (isset($item['width']) && (!is_string($item['width']) || empty($item['width']))) {
214 continue;
215 }
216 }
217
218 $validatedItems[] = $item;
219 }
220
221 return $validatedItems;
222 }
223 }