2026-07-03 02:53:29 -05:00
<? 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 ();
2026-07-12 08:41:18 -05:00
// 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.
2026-07-03 02:53:29 -05:00
$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" ]);
}
}
}
}
}
}