prefix . self::TABLE; } public static function maybe_install() { if (get_option(self::DB_OPTION) === self::DB_VERSION) { return; } global $wpdb; $table = self::table_name(); $charset = $wpdb->get_charset_collate(); $sql = "CREATE TABLE {$table} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(191) NOT NULL DEFAULT '', status VARCHAR(20) NOT NULL DEFAULT 'draft', roles TEXT NULL, items LONGTEXT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, PRIMARY KEY (id), KEY idx_status (status) ) {$charset};"; require_once ABSPATH . "wp-admin/includes/upgrade.php"; dbDelta($sql); update_option(self::DB_OPTION, self::DB_VERSION); } /** * Applies every published config whose target roles match the current * user: hides items marked hidden and relabels items with a custom label. */ public static function apply() { global $menu, $wpdb; if (!is_admin() || !function_exists("wp_get_current_user")) { return; } $user = wp_get_current_user(); if (!$user || !$user->exists()) { return; } $table = self::table_name(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- {$table} is an internal, hardcoded {$wpdb->prefix}-based table name, not user input; no user-supplied values in this query. Runs on every admin page load to build the sidebar menu, so a per-request cache wouldn't help; a persistent cache would need its own invalidation on every config save, more complexity than this read is worth. $configs = $wpdb->get_results("SELECT roles, items FROM {$table} WHERE status = 'published'"); if (!$configs) { return; } foreach ($configs as $config) { $roles = json_decode($config->roles ?: "[]", true); if (!is_array($roles) || !$roles) { continue; } if (!array_intersect((array) $user->roles, $roles)) { continue; } $items = json_decode($config->items ?: "[]", true); if (!is_array($items)) { continue; } foreach ($items as $item) { $native_id = isset($item["native_id"]) ? $item["native_id"] : ""; if ($native_id === "") { continue; } foreach ($menu as $key => $entry) { if (!isset($entry[2]) || $entry[2] !== $native_id) { continue; } if (!empty($item["hidden"])) { unset($menu[$key]); continue 2; } if (!empty($item["label"])) { $menu[$key][0] = sanitize_text_field($item["label"]); } } } } } }