admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('wdt_permissions_nonce'), )); } } /** * Register wpDataTables capabilities * Makes them available in role management interfaces */ public static function registerCapabilities() { // Add the capabilities directly to administrator role // This makes them discoverable by role management plugins $adminRole = get_role('administrator'); if ($adminRole) { $adminRole->add_cap(self::CAPABILITY_VIEW_TABLES); $adminRole->add_cap(self::CAPABILITY_VIEW_CHARTS); } // Add filter to include our custom capabilities in role editors add_filter('members_get_capabilities', function ($capabilities) { $capabilities[] = self::CAPABILITY_VIEW_TABLES; $capabilities[] = self::CAPABILITY_VIEW_CHARTS; return array_unique($capabilities); }); // For other role editors, filter the available capabilities add_filter('editable_roles', function ($roles) { return $roles; }); } public static function registerMenu() { // Only admins can access if (!current_user_can('manage_options')) return; add_submenu_page( 'wpdatatables', __('Permissions', 'wpdatatables'), __('Permissions', 'wpdatatables'), 'manage_options', 'wpdatatables_permissions', [self::class, 'renderPage'] ); } public static function renderPage() { $activeTab = isset($_GET['tab']) && $_GET['tab'] === 'charts' ? 'charts' : 'tables'; require_once(WDT_ROOT_PATH . '/source/class.wdtpermissionslisttable.php'); $permissionsTable = new WDTPermissionsListTable(['type' => $activeTab]); $permissionsTable->prepare_items(); include(WDT_ROOT_PATH . '/templates/admin/permissions/permissions.inc.php'); } public static function loadPermissionsAjax() { check_ajax_referer('wdt_permissions_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Unauthorized']); } $tab = isset($_POST['tab']) ? sanitize_text_field($_POST['tab']) : 'tables'; // Load assigned permissions for tables or charts if ($tab === 'tables') { $html = self::getTablePermissionsListHtml(); } else { $html = self::getChartPermissionsListHtml(); } wp_send_json_success(['html' => $html]); } private static function getTablePermissionsListHtml() { // Get optional search term $search = isset($_POST['s']) ? sanitize_text_field($_POST['s']) : ''; // Get all users with the wpdt_view_tables capability $users = get_users(); if (!is_array($users) || empty($users)) { return '' . esc_html__('No users found.', 'wpdatatables') . ''; } $html = ''; $foundAny = false; foreach ($users as $user) { // Skip administrators if (in_array('administrator', (array)$user->roles, true)) { continue; } // Apply search filter if present if ($search) { $match = false; if (is_numeric($search) && intval($search) === intval($user->ID)) $match = true; if (stripos($user->user_login, $search) !== false) $match = true; if (stripos($user->user_email, $search) !== false) $match = true; if (!$match) continue; } // Check if user has the capability by checking their actual capabilities // This respects role hierarchy and capability grants/denials if (!isset($user->allcaps[self::CAPABILITY_VIEW_TABLES]) || !$user->allcaps[self::CAPABILITY_VIEW_TABLES]) { continue; } $foundAny = true; // Get user roles $roles = implode(', ', $user->roles); // Get table access metadata for this user $userTableAccess = get_user_meta($user->ID, 'wpdt_table_access', true); $allTables = empty($userTableAccess) || isset($userTableAccess['all_tables']) && $userTableAccess['all_tables']; // Get tables list if ($allTables) { $tablesDisplay = esc_html__('All', 'wpdatatables'); } else { $tableIds = isset($userTableAccess['table_ids']) ? $userTableAccess['table_ids'] : array(); if (empty($tableIds)) { $tablesDisplay = '—'; } else { // Get table titles global $wpdb; $placeholders = implode(',', array_fill(0, count($tableIds), '%d')); $query = $wpdb->prepare("SELECT GROUP_CONCAT(title) as titles FROM {$wpdb->prefix}wpdatatables WHERE id IN ($placeholders)", ...$tableIds); $result = $wpdb->get_var($query); $tablesDisplay = $result ?: '—'; } } $permissionsDisplay = esc_html__('View Tables', 'wpdatatables'); $html .= ''; $html .= '' . intval($user->ID) . ''; $html .= '' . esc_html($user->user_login) . ''; $html .= '' . esc_html($roles) . ''; $html .= '' . esc_html($user->user_email) . ''; $html .= '' . esc_html($tablesDisplay) . ''; $html .= '' . esc_html($permissionsDisplay) . ''; $html .= ''; $html .= '
'; $html .= ''; $html .= ''; $html .= '
'; $html .= ''; $html .= ''; } if (!$foundAny) { return '' . esc_html__('No permissions assigned yet.', 'wpdatatables') . ''; } return $html; } private static function getChartPermissionsListHtml() { // Get optional search term $search = isset($_POST['s']) ? sanitize_text_field($_POST['s']) : ''; // Get all users with the wpdt_view_charts capability $users = get_users(); if (!is_array($users) || empty($users)) { return '' . esc_html__('No users found.', 'wpdatatables') . ''; } $html = ''; $foundAny = false; foreach ($users as $user) { // Skip administrators if (in_array('administrator', (array)$user->roles, true)) { continue; } // Apply search filter if present if ($search) { $match = false; if (is_numeric($search) && intval($search) === intval($user->ID)) $match = true; if (stripos($user->user_login, $search) !== false) $match = true; if (stripos($user->user_email, $search) !== false) $match = true; if (!$match) continue; } // Check if user has the capability by checking their actual capabilities // This respects role hierarchy and capability grants/denials if (!isset($user->allcaps[self::CAPABILITY_VIEW_CHARTS]) || !$user->allcaps[self::CAPABILITY_VIEW_CHARTS]) { continue; } $foundAny = true; // Get user roles $roles = implode(', ', $user->roles); // Get chart access metadata for this user $userChartAccess = get_user_meta($user->ID, 'wpdt_chart_access', true); $allCharts = empty($userChartAccess) || isset($userChartAccess['all_charts']) && $userChartAccess['all_charts']; // Get charts list if ($allCharts) { $chartsDisplay = esc_html__('All', 'wpdatatables'); } else { $chartIds = isset($userChartAccess['chart_ids']) ? $userChartAccess['chart_ids'] : array(); if (empty($chartIds)) { $chartsDisplay = '—'; } else { // Get chart titles global $wpdb; $placeholders = implode(',', array_fill(0, count($chartIds), '%d')); $query = $wpdb->prepare("SELECT GROUP_CONCAT(title) as titles FROM {$wpdb->prefix}wpdatacharts WHERE id IN ($placeholders)", ...$chartIds); $result = $wpdb->get_var($query); $chartsDisplay = $result ?: '—'; } } $permissionsDisplay = esc_html__('View Charts', 'wpdatatables'); $html .= ''; $html .= '' . intval($user->ID) . ''; $html .= '' . esc_html($user->user_login) . ''; $html .= '' . esc_html($roles) . ''; $html .= '' . esc_html($user->user_email) . ''; $html .= '' . esc_html($chartsDisplay) . ''; $html .= '' . esc_html($permissionsDisplay) . ''; $html .= ''; $html .= '
'; $html .= ''; $html .= ''; $html .= '
'; $html .= ''; $html .= ''; } if (!$foundAny) { return '' . esc_html__('No permissions assigned yet.', 'wpdatatables') . ''; } return $html; } public static function savePermissionAjax() { check_ajax_referer('wdt_permissions_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Unauthorized']); } $tab = isset($_POST['tab']) ? sanitize_text_field($_POST['tab']) : 'tables'; $userId = isset($_POST['user_id']) ? intval($_POST['user_id']) : 0; $enableSpecific = isset($_POST['enable_specific']) ? intval($_POST['enable_specific']) : 0; $itemIds = isset($_POST['item_ids']) ? (array)$_POST['item_ids'] : array(); if (!$userId) { wp_send_json_error(['message' => 'Invalid data']); } $user = get_userdata($userId); if (!$user) { wp_send_json_error(['message' => 'User not found']); } // Determine capability to grant $capability = $tab === 'tables' ? self::CAPABILITY_VIEW_TABLES : self::CAPABILITY_VIEW_CHARTS; // Grant capability to user $user->add_cap($capability); // Store table/chart access metadata $metaKey = $tab === 'tables' ? 'wpdt_table_access' : 'wpdt_chart_access'; $accessData = array(); if ($enableSpecific && !empty($itemIds)) { $itemIdKey = $tab === 'tables' ? 'table_ids' : 'chart_ids'; $accessData[$itemIdKey] = array_map('intval', $itemIds); } else { $allKey = $tab === 'tables' ? 'all_tables' : 'all_charts'; $accessData[$allKey] = true; } update_user_meta($userId, $metaKey, $accessData); wp_send_json_success(['message' => 'Permission saved successfully']); } public static function updatePermissionAjax() { check_ajax_referer('wdt_permissions_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Unauthorized']); } $tab = isset($_POST['tab']) ? sanitize_text_field($_POST['tab']) : 'tables'; $userId = isset($_POST['user_id']) ? intval($_POST['user_id']) : 0; $enableSpecific = isset($_POST['enable_specific']) ? intval($_POST['enable_specific']) : 0; $itemIds = isset($_POST['item_ids']) ? (array)$_POST['item_ids'] : array(); if (!$userId) { wp_send_json_error(['message' => 'Invalid data']); } $user = get_userdata($userId); if (!$user) { wp_send_json_error(['message' => 'User not found']); } // Determine capability $capability = $tab === 'tables' ? self::CAPABILITY_VIEW_TABLES : self::CAPABILITY_VIEW_CHARTS; // Ensure user has capability $user->add_cap($capability); // Update table/chart access metadata $metaKey = $tab === 'tables' ? 'wpdt_table_access' : 'wpdt_chart_access'; $accessData = array(); if ($enableSpecific && !empty($itemIds)) { $itemIdKey = $tab === 'tables' ? 'table_ids' : 'chart_ids'; $accessData[$itemIdKey] = array_map('intval', $itemIds); } else { $allKey = $tab === 'tables' ? 'all_tables' : 'all_charts'; $accessData[$allKey] = true; } update_user_meta($userId, $metaKey, $accessData); wp_send_json_success(['message' => 'Permission updated successfully']); } public static function deletePermissionAjax() { check_ajax_referer('wdt_permissions_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Unauthorized']); } $tab = isset($_POST['tab']) ? sanitize_text_field($_POST['tab']) : 'tables'; $userId = isset($_POST['user_id']) ? intval($_POST['user_id']) : 0; if (!$userId) { wp_send_json_error(['message' => 'Invalid data']); } $user = get_userdata($userId); if (!$user) { wp_send_json_error(['message' => 'User not found']); } // Determine capability to remove $capability = $tab === 'tables' ? self::CAPABILITY_VIEW_TABLES : self::CAPABILITY_VIEW_CHARTS; // Remove capability from user $user->remove_cap($capability); // Delete associated metadata $metaKey = $tab === 'tables' ? 'wpdt_table_access' : 'wpdt_chart_access'; delete_user_meta($userId, $metaKey); wp_send_json_success(['message' => 'Permission deleted successfully']); } public static function getPermissionAjax() { check_ajax_referer('wdt_permissions_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'Unauthorized']); } $tab = isset($_POST['tab']) ? sanitize_text_field($_POST['tab']) : 'tables'; $userId = isset($_POST['user_id']) ? intval($_POST['user_id']) : 0; if (!$userId) { wp_send_json_error(['message' => 'Invalid data']); } $user = get_userdata($userId); if (!$user) { wp_send_json_error(['message' => 'User not found']); } // Get capability $capability = $tab === 'tables' ? self::CAPABILITY_VIEW_TABLES : self::CAPABILITY_VIEW_CHARTS; $hasCapability = $user->has_cap($capability); // Get table/chart access metadata $metaKey = $tab === 'tables' ? 'wpdt_table_access' : 'wpdt_chart_access'; $accessData = get_user_meta($userId, $metaKey, true); $result = array( 'user_id' => $userId, 'has_capability' => $hasCapability, ); // Determine access type if (empty($accessData)) { $result['all_items'] = true; $result['item_ids'] = array(); } else { $allKey = $tab === 'tables' ? 'all_tables' : 'all_charts'; $itemIdKey = $tab === 'tables' ? 'table_ids' : 'chart_ids'; $result['all_items'] = isset($accessData[$allKey]) && $accessData[$allKey]; $result['item_ids'] = isset($accessData[$itemIdKey]) ? $accessData[$itemIdKey] : array(); } wp_send_json_success($result); } } add_action('admin_menu', ['WDTPermissionsAdmin', 'registerMenu']); add_action('admin_enqueue_scripts', ['WDTPermissionsAdmin', 'enqueueAssets']); add_action('init', ['WDTPermissionsAdmin', 'registerCapabilities'], 1); add_action('wp_ajax_wpdatatables_load_permissions', ['WDTPermissionsAdmin', 'loadPermissionsAjax']); add_action('wp_ajax_wpdatatables_save_permission', ['WDTPermissionsAdmin', 'savePermissionAjax']); add_action('wp_ajax_wpdatatables_update_permission', ['WDTPermissionsAdmin', 'updatePermissionAjax']); add_action('wp_ajax_wpdatatables_delete_permission', ['WDTPermissionsAdmin', 'deletePermissionAjax']); add_action('wp_ajax_wpdatatables_get_permission', ['WDTPermissionsAdmin', 'getPermissionAjax']);