PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 2.0.10
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v2.0.10
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 2.0.10, at app/Hooks/Handlers/BoardMenuHandler.php

239 lines 8.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', // or 'custom'
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 'icon_name' => 'info',
63 'label' => __('About this Board', 'fluent-boards'),
64 'type' => 'default',
65 'position' => 1,
66 'role' => ''
67 ],
68 [
69 'key' => 'board_activity',
70 'icon_name' => 'activity',
71 'label' => __('Board Activity', 'fluent-boards'),
72 'type' => 'default',
73 'position' => 2,
74 'role' => ''
75 ],
76 [
77 'key' => 'change_background',
78 'icon_name' => 'image',
79 'label' => __('Change Background', 'fluent-boards'),
80 'type' => 'default',
81 'position' => 3,
82 'role' => 'manager'
83 ],
84 [
85 'key' => 'notification_settings',
86 'icon_name' => 'notification',
87 'label' => __('Notification Settings', 'fluent-boards'),
88 'type' => 'default',
89 'position' => 4,
90 'role' => ''
91 ],
92 [
93 'key' => 'board_labels',
94 'icon_name' => 'label',
95 'label' => __('Board Labels', 'fluent-boards'),
96 'type' => 'default',
97 'position' => 5,
98 'role' => ''
99 ],
100 [
101 'key' => 'custom_fields',
102 'icon_name' => 'custom_fields',
103 'label' => __('Custom Fields', 'fluent-boards'),
104 'type' => 'default',
105 'position' => 6,
106 'role' => ''
107 ],
108 [
109 'key' => 'board_members',
110 'icon_name' => 'members',
111 'label' => __('Board Members', 'fluent-boards'),
112 'type' => 'default',
113 'position' => 7,
114 'role' => ''
115 ],
116 [
117 'key' => 'archived_items',
118 'icon_name' => 'archived_items',
119 'label' => __('Archived Items', 'fluent-boards'),
120 'type' => 'default',
121 'position' => 8,
122 'role' => ''
123 ],
124 [
125 'key' => 'webhooks',
126 'icon_name' => 'webhooks',
127 'label' => __('Webhooks', 'fluent-boards'),
128 'type' => 'default',
129 'position' => 9,
130 'role' => ''
131 ],
132 [
133 'key' => 'associated_crm_contacts',
134 'icon_name' => 'contacts-book',
135 'label' => __('Associated CRM Contacts', 'fluent-boards'),
136 'type' => 'default',
137 'position' => 9,
138 'role' => ''
139 ],
140 [
141 'key' => 'duplicate_board',
142 'icon_name' => 'copy',
143 'label' => __('Duplicate Board', 'fluent-boards'),
144 'type' => 'default',
145 'position' => 10,
146 'role' => 'manager'
147 ],
148 [
149 'key' => 'restore_board',
150 'icon_name' => 'refresh',
151 'label' => __('Restore Board', 'fluent-boards'),
152 'type' => 'default',
153 'position' => 10,
154 'role' => 'admin'
155 ],
156 [
157 'key' => 'export',
158 'icon_name' => 'export',
159 'label' => __('Export', 'fluent-boards'),
160 'type' => 'default',
161 'position' => 10.5,
162 'role' => 'manager',
163 'pro' => true
164 ],
165 [
166 'key' => 'archive_board',
167 'icon_name' => 'archive',
168 'label' => __('Archive Board', 'fluent-boards'),
169 'type' => 'default',
170 'position' => 11,
171 'role' => 'admin'
172 ],
173 [
174 'key' => 'delete_board',
175 'icon_name' => 'delete',
176 'label' => __('Delete Board', 'fluent-boards'),
177 'type' => 'default',
178 'position' => 11,
179 'role' => 'admin'
180 ]
181 ];
182 }
183
184 /**
185 * Validate menu permissions server-side to prevent bypass
186 * Uses key-based validation with optimized permission checking
187 */
188 private static function validateMenuPermissions($menuItems)
189 {
190 $validatedItems = [];
191
192 $isAdmin = PermissionManager::isAdmin();
193
194 foreach ($menuItems as $item) {
195 if (empty($item['key']) || empty($item['label'])) {
196 continue;
197 }
198
199 $key = $item['key'];
200
201 if($key === 'associated_crm_contacts' && !defined('FLUENTCRM')) {
202 continue;
203 }
204
205 // Enforce default item policy if in whitelist
206 if (isset($item['type']) && $item['type'] === 'default') {
207 $requiredRole = $item['role'];
208
209 // Enforce role - only check admin role, remove manager role checks
210 if ($requiredRole === 'admin' && !$isAdmin) {
211 continue;
212 }
213
214 // Enforce pro restriction
215 if (isset($item['pro']) && $item['pro'] === true) {
216 $item['requires_pro'] = true;
217 }
218 } else {
219 // Custom item
220 $item['type'] = 'custom';
221 $requiredRole = $item['role'] ?? '';
222
223 if ($requiredRole === 'admin' && !$isAdmin) {
224 continue;
225 }
226
227 // Optional: validate width
228 if (isset($item['width']) && (!is_string($item['width']) || empty($item['width']))) {
229 continue;
230 }
231 }
232
233 $validatedItems[] = $item;
234 }
235
236 return $validatedItems;
237 }
238 }
239