'my_item', * 'label' => 'My Item', * 'type' => 'default', // or 'custom' * 'position' => 1, * 'width' => '500px', * 'html' => '
Content
' * ] */ // Apply filter to modify all menu items (default + custom) $allMenuItems = apply_filters('fluent_boards/board_menu_items', $defaultMenuItems); // Sort by position /** * This ensures that when you add custom menu items with * decimal positions (like 10.5 to insert after duplicate_board at position 10), * they'll be sorted correctly in the final menu order. */ usort($allMenuItems, function($a, $b) { $posA = isset($a['position']) ? (float)$a['position'] : 999; $posB = isset($b['position']) ? (float)$b['position'] : 999; if ($posA == $posB) { return 0; } return ($posA < $posB) ? -1 : 1; }); // Apply server-side permission validation to prevent bypass $allMenuItems = self::validateMenuPermissions($allMenuItems); return $allMenuItems; } private static function getDefaultMenuItems() { return [ [ 'key' => 'about_this_board', 'icon_name' => 'info', 'label' => __('About this Board', 'fluent-boards'), 'type' => 'default', 'position' => 1, 'role' => '' ], [ 'key' => 'board_activity', 'icon_name' => 'activity', 'label' => __('Board Activity', 'fluent-boards'), 'type' => 'default', 'position' => 2, 'role' => '' ], [ 'key' => 'change_background', 'icon_name' => 'image', 'label' => __('Change Background', 'fluent-boards'), 'type' => 'default', 'position' => 3, 'role' => 'manager' ], [ 'key' => 'notification_settings', 'icon_name' => 'notification', 'label' => __('Notification Settings', 'fluent-boards'), 'type' => 'default', 'position' => 4, 'role' => '' ], [ 'key' => 'board_labels', 'icon_name' => 'label', 'label' => __('Board Labels', 'fluent-boards'), 'type' => 'default', 'position' => 5, 'role' => '' ], [ 'key' => 'custom_fields', 'icon_name' => 'custom_fields', 'label' => __('Custom Fields', 'fluent-boards'), 'type' => 'default', 'position' => 6, 'role' => '' ], [ 'key' => 'board_members', 'icon_name' => 'members', 'label' => __('Board Members', 'fluent-boards'), 'type' => 'default', 'position' => 7, 'role' => '' ], [ 'key' => 'archived_items', 'icon_name' => 'archived_items', 'label' => __('Archived Items', 'fluent-boards'), 'type' => 'default', 'position' => 8, 'role' => '' ], [ 'key' => 'webhooks', 'icon_name' => 'webhooks', 'label' => __('Webhooks', 'fluent-boards'), 'type' => 'default', 'position' => 9, 'role' => '' ], [ 'key' => 'associated_crm_contacts', 'icon_name' => 'contacts-book', 'label' => __('Associated CRM Contacts', 'fluent-boards'), 'type' => 'default', 'position' => 9, 'role' => '' ], [ 'key' => 'duplicate_board', 'icon_name' => 'copy', 'label' => __('Duplicate Board', 'fluent-boards'), 'type' => 'default', 'position' => 10, 'role' => 'manager' ], [ 'key' => 'restore_board', 'icon_name' => 'refresh', 'label' => __('Restore Board', 'fluent-boards'), 'type' => 'default', 'position' => 10, 'role' => 'admin' ], [ 'key' => 'export', 'icon_name' => 'export', 'label' => __('Export', 'fluent-boards'), 'type' => 'default', 'position' => 10.5, 'role' => 'manager', 'pro' => true ], [ 'key' => 'archive_board', 'icon_name' => 'archive', 'label' => __('Archive Board', 'fluent-boards'), 'type' => 'default', 'position' => 11, 'role' => 'admin' ], [ 'key' => 'delete_board', 'icon_name' => 'delete', 'label' => __('Delete Board', 'fluent-boards'), 'type' => 'default', 'position' => 11, 'role' => 'admin' ] ]; } /** * Validate menu permissions server-side to prevent bypass * Uses key-based validation with optimized permission checking */ private static function validateMenuPermissions($menuItems) { $validatedItems = []; $isAdmin = PermissionManager::isAdmin(); foreach ($menuItems as $item) { if (empty($item['key']) || empty($item['label'])) { continue; } $key = $item['key']; if($key === 'associated_crm_contacts' && !defined('FLUENTCRM')) { continue; } // Enforce default item policy if in whitelist if (isset($item['type']) && $item['type'] === 'default') { $requiredRole = $item['role']; // Enforce role - only check admin role, remove manager role checks if ($requiredRole === 'admin' && !$isAdmin) { continue; } // Enforce pro restriction if (isset($item['pro']) && $item['pro'] === true) { $item['requires_pro'] = true; } } else { // Custom item $item['type'] = 'custom'; $requiredRole = $item['role'] ?? ''; if ($requiredRole === 'admin' && !$isAdmin) { continue; } // Optional: validate width if (isset($item['width']) && (!is_string($item['width']) || empty($item['width']))) { continue; } } $validatedItems[] = $item; } return $validatedItems; } }