Machine-sync snapshot: the working tree carried the full 3.0.0-3.6.1 arc (licensing/FileManager/DatabaseExplorer removal, all new replacement screens and REST controllers) that had not been committed on this machine. Captured here as a safety baseline before starting the Adminify-parity revamp. Co-Authored-By: Claude Opus 4.8 <[email protected]>
121 lines
3.5 KiB
PHP
121 lines
3.5 KiB
PHP
<?php
|
|
namespace AuroraAdmin\MenuCreator;
|
|
|
|
defined("ABSPATH") || exit();
|
|
|
|
/**
|
|
* Custom admin-menu configurations: hide/rename/reorder WP's native menu
|
|
* items per role. Storage + the runtime hook that actually applies a
|
|
* published config live here; REST CRUD lives in MenuCreatorData.php.
|
|
*/
|
|
class MenuCreator
|
|
{
|
|
const TABLE = "aurora_admin_menus";
|
|
const DB_VERSION = "1";
|
|
const DB_OPTION = "aurora_admin_menus_db_version";
|
|
|
|
public function __construct()
|
|
{
|
|
add_action("init", [self::class, "maybe_install"]);
|
|
// Late priority so it runs after all other plugins have registered
|
|
// their own menu items — there's nothing left to reorder/hide otherwise.
|
|
add_action("admin_menu", [self::class, "apply"], PHP_INT_MAX);
|
|
}
|
|
|
|
public static function table_name()
|
|
{
|
|
global $wpdb;
|
|
return $wpdb->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"]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|