'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;
}
}