Add theme presets, gradient backgrounds, and macOS-style window chrome
Replaces the old light/dark toggle with 10 fixed color themes (plus Black & Grey and White) that drive a coordinated gradient palette across the whole admin UI, a Google Fonts picker, and a native-page dark-mode stylesheet that now covers modals and plugin/theme card grids. Fixes several background/gradient seams where the sidebar, toolbar, and content frame were each painting their own independent copy of the page gradient instead of sharing one continuous plane. Also adds a macOS-style traffic-light title bar to every page (close, minimize, maximize) and a persistent bottom taskbar so a minimized page can be restored from anywhere, plus a Plugins management page. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
db07e21f9e
commit
4a547d2c50
@@ -163,9 +163,24 @@ html.aurora-dark.aurora-ready #wpbody-content .button-primary:hover {
|
||||
/* Nav tabs -------------------------------------------------------------- */
|
||||
html.aurora-dark.aurora-ready #wpbody-content .nav-tab-wrapper,
|
||||
html.aurora-dark.aurora-ready #wpbody-content .wp-filter {
|
||||
background: var(--aurora-card-bg);
|
||||
border-color: var(--aurora-border);
|
||||
}
|
||||
|
||||
html.aurora-dark.aurora-ready #wpbody-content .wp-filter .filter-links a {
|
||||
color: var(--aurora-accent);
|
||||
}
|
||||
|
||||
html.aurora-dark.aurora-ready #wpbody-content .wp-filter .filter-links .current {
|
||||
color: var(--aurora-text);
|
||||
}
|
||||
|
||||
html.aurora-dark.aurora-ready #wpbody-content .wp-filter .search-form input[type='search'] {
|
||||
background: var(--aurora-bg-subtle);
|
||||
border-color: var(--aurora-border);
|
||||
color: var(--aurora-text);
|
||||
}
|
||||
|
||||
html.aurora-dark.aurora-ready #wpbody-content .nav-tab {
|
||||
background: var(--aurora-bg-subtle);
|
||||
border-color: var(--aurora-border);
|
||||
@@ -251,14 +266,14 @@ html.aurora-dark.aurora-ready body.update-core-php #wpbody-content .wrap > p {
|
||||
html.aurora-dark.aurora-ready body.update-core-php #wpbody-content .aurora-native-card {
|
||||
background: var(--aurora-card-bg);
|
||||
border: 1px solid var(--aurora-card-border);
|
||||
border-radius: 16px;
|
||||
padding: 20px 24px;
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
html.aurora-dark.aurora-ready body.update-core-php #wpbody-content .aurora-native-card h2 {
|
||||
font-size: 1.05rem;
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
margin: 4px 0 8px;
|
||||
margin: 0 0 18px;
|
||||
color: var(--aurora-text);
|
||||
border: 0;
|
||||
padding-top: 0;
|
||||
@@ -280,3 +295,85 @@ html.aurora-dark.aurora-ready body.update-core-php #wpbody-content .aurora-nativ
|
||||
html.aurora-dark.aurora-ready body.update-core-php #wpbody-content .aurora-native-card .button:hover {
|
||||
background: var(--aurora-accent-soft);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Modals & popups (media library, plugin/theme install details) — these are
|
||||
* Backbone/thickbox views appended to <body>, outside #wpbody-content, so
|
||||
* every rule above misses them entirely. Scoped to html.aurora-dark only
|
||||
* (no #wpbody-content ancestor requirement) since that's the only anchor
|
||||
* available at body level.
|
||||
* ------------------------------------------------------------------------ */
|
||||
html.aurora-dark.aurora-ready .media-modal,
|
||||
html.aurora-dark.aurora-ready .media-frame,
|
||||
html.aurora-dark.aurora-ready .media-frame-title,
|
||||
html.aurora-dark.aurora-ready .media-frame-router,
|
||||
html.aurora-dark.aurora-ready .media-frame-content,
|
||||
html.aurora-dark.aurora-ready .media-frame-toolbar,
|
||||
html.aurora-dark.aurora-ready .media-sidebar,
|
||||
html.aurora-dark.aurora-ready #TB_window,
|
||||
html.aurora-dark.aurora-ready #TB_ajaxContent,
|
||||
html.aurora-dark.aurora-ready .plugin-details-modal,
|
||||
html.aurora-dark.aurora-ready .plugin-install-php .plugin-card {
|
||||
background: var(--aurora-card-bg);
|
||||
color: var(--aurora-text);
|
||||
border-color: var(--aurora-card-border);
|
||||
}
|
||||
|
||||
html.aurora-dark.aurora-ready .media-modal h1,
|
||||
html.aurora-dark.aurora-ready .media-modal h2,
|
||||
html.aurora-dark.aurora-ready .media-modal h3,
|
||||
html.aurora-dark.aurora-ready .media-modal label,
|
||||
html.aurora-dark.aurora-ready .media-frame-title h1,
|
||||
html.aurora-dark.aurora-ready #TB_ajaxContent,
|
||||
html.aurora-dark.aurora-ready #TB_ajaxContent p,
|
||||
html.aurora-dark.aurora-ready #TB_ajaxContent h1,
|
||||
html.aurora-dark.aurora-ready .plugin-card-top,
|
||||
html.aurora-dark.aurora-ready .plugin-card .name,
|
||||
html.aurora-dark.aurora-ready .plugin-card .desc {
|
||||
color: var(--aurora-text);
|
||||
}
|
||||
|
||||
html.aurora-dark.aurora-ready .media-frame-menu,
|
||||
html.aurora-dark.aurora-ready .media-menu,
|
||||
html.aurora-dark.aurora-ready .attachments-browser .media-toolbar,
|
||||
html.aurora-dark.aurora-ready .media-sidebar,
|
||||
html.aurora-dark.aurora-ready .plugin-card-bottom {
|
||||
background: var(--aurora-bg-subtle);
|
||||
border-color: var(--aurora-border);
|
||||
}
|
||||
|
||||
html.aurora-dark.aurora-ready .attachments-browser .attachments,
|
||||
html.aurora-dark.aurora-ready .media-frame-content {
|
||||
background: var(--aurora-app-bg-solid);
|
||||
}
|
||||
|
||||
html.aurora-dark.aurora-ready .media-modal input,
|
||||
html.aurora-dark.aurora-ready .media-modal textarea,
|
||||
html.aurora-dark.aurora-ready .media-modal select,
|
||||
html.aurora-dark.aurora-ready #TB_ajaxContent input,
|
||||
html.aurora-dark.aurora-ready #TB_ajaxContent textarea,
|
||||
html.aurora-dark.aurora-ready #TB_ajaxContent select {
|
||||
background: var(--aurora-bg-subtle);
|
||||
border-color: var(--aurora-border);
|
||||
color: var(--aurora-text);
|
||||
}
|
||||
|
||||
html.aurora-dark.aurora-ready .media-modal-backdrop,
|
||||
html.aurora-dark.aurora-ready #TB_overlay {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
/* Plugin/theme card grids on plugins.php / plugin-install.php / themes.php */
|
||||
html.aurora-dark.aurora-ready #wpbody-content .theme-browser .theme,
|
||||
html.aurora-dark.aurora-ready #wpbody-content .plugin-card {
|
||||
background: var(--aurora-card-bg);
|
||||
border-color: var(--aurora-card-border);
|
||||
color: var(--aurora-text);
|
||||
}
|
||||
|
||||
html.aurora-dark.aurora-ready #wpbody-content .theme-browser .theme .theme-name,
|
||||
html.aurora-dark.aurora-ready #wpbody-content .theme-browser .theme .theme-actions {
|
||||
background: var(--aurora-card-bg);
|
||||
color: var(--aurora-text);
|
||||
border-color: var(--aurora-card-border);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use AuroraAdmin\Pages\SettingsPage;
|
||||
use AuroraAdmin\Rest\DashboardData;
|
||||
use AuroraAdmin\Analytics\Analytics;
|
||||
use AuroraAdmin\Rest\AnalyticsData;
|
||||
use AuroraAdmin\Rest\PluginsData;
|
||||
use AuroraAdmin\Posts\ScheduledDayFilter;
|
||||
use AuroraAdmin\WhiteLabel\WhiteLabel;
|
||||
use AuroraAdmin\Pages\SubApps;
|
||||
@@ -39,6 +40,7 @@ class AuroraAdmin
|
||||
new DashboardData();
|
||||
new Analytics();
|
||||
new AnalyticsData();
|
||||
new PluginsData();
|
||||
new ScheduledDayFilter();
|
||||
new WhiteLabel();
|
||||
new SubApps();
|
||||
|
||||
@@ -35,7 +35,12 @@ class Settings
|
||||
"schema" => [
|
||||
"type" => "object",
|
||||
"properties" => [
|
||||
"dark_mode" => ["type" => "string"],
|
||||
// Two producers write this key with different types: the
|
||||
// toolbar's tri-state switcher sends 'light'/'dark'/'system'
|
||||
// (string), the settings page's Dark Mode toggle sends a plain
|
||||
// boolean. Leaving it untyped (falls through to
|
||||
// additionalProperties) lets both save instead of one
|
||||
// rejecting the other with a 400.
|
||||
"dashboard_card_order" => ["type" => "object"],
|
||||
],
|
||||
"additionalProperties" => true,
|
||||
|
||||
@@ -24,6 +24,7 @@ class SubApps
|
||||
"aurora-admin-roles" => ["title" => "Role Editor", "entry" => "role-editor"],
|
||||
"aurora-admin-activity-log" => ["title" => "Activity Log", "entry" => "activity-log"],
|
||||
"aurora-admin-database" => ["title" => "Database Explorer", "entry" => "database-explorer"],
|
||||
"aurora-admin-plugins" => ["title" => "Installed Plugins", "entry" => "plugins"],
|
||||
];
|
||||
|
||||
private static $hook_suffixes = [];
|
||||
@@ -38,11 +39,12 @@ class SubApps
|
||||
public static function register_menus()
|
||||
{
|
||||
foreach (self::PAGES as $slug => $page) {
|
||||
$parent = $slug === "aurora-admin-plugins" ? "plugins.php" : "aurora-admin";
|
||||
$hook = add_submenu_page(
|
||||
"aurora-admin",
|
||||
$parent,
|
||||
__($page["title"], "aurora-admin"),
|
||||
__($page["title"], "aurora-admin"),
|
||||
"manage_options",
|
||||
$slug === "aurora-admin-plugins" ? "activate_plugins" : "manage_options",
|
||||
$slug,
|
||||
[self::class, "render"]
|
||||
);
|
||||
@@ -53,7 +55,7 @@ class SubApps
|
||||
public static function hide_from_native_submenu()
|
||||
{
|
||||
foreach (array_keys(self::PAGES) as $slug) {
|
||||
remove_submenu_page("aurora-admin", $slug);
|
||||
remove_submenu_page($slug === "aurora-admin-plugins" ? "plugins.php" : "aurora-admin", $slug);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
namespace AuroraAdmin\Rest;
|
||||
|
||||
defined("ABSPATH") || exit();
|
||||
|
||||
/**
|
||||
* REST endpoints for Aurora's custom Installed Plugins page.
|
||||
*/
|
||||
class PluginsData
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
add_action("rest_api_init", [self::class, "register"]);
|
||||
}
|
||||
|
||||
public static function register()
|
||||
{
|
||||
register_rest_route("aurora-admin/v1", "/plugins", [
|
||||
"methods" => "GET",
|
||||
"callback" => [self::class, "list_plugins"],
|
||||
"permission_callback" => function () {
|
||||
return current_user_can("activate_plugins");
|
||||
},
|
||||
]);
|
||||
|
||||
register_rest_route("aurora-admin/v1", "/plugins/activate", [
|
||||
"methods" => "POST",
|
||||
"callback" => [self::class, "activate_plugin"],
|
||||
"permission_callback" => function () {
|
||||
return current_user_can("activate_plugins");
|
||||
},
|
||||
"args" => [
|
||||
"file" => ["type" => "string", "required" => true],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route("aurora-admin/v1", "/plugins/deactivate", [
|
||||
"methods" => "POST",
|
||||
"callback" => [self::class, "deactivate_plugin"],
|
||||
"permission_callback" => function () {
|
||||
return current_user_can("activate_plugins");
|
||||
},
|
||||
"args" => [
|
||||
"file" => ["type" => "string", "required" => true],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route("aurora-admin/v1", "/plugins/delete", [
|
||||
"methods" => "DELETE",
|
||||
"callback" => [self::class, "delete_plugin"],
|
||||
"permission_callback" => function () {
|
||||
return current_user_can("delete_plugins");
|
||||
},
|
||||
"args" => [
|
||||
"file" => ["type" => "string", "required" => true],
|
||||
],
|
||||
]);
|
||||
|
||||
register_rest_route("aurora-admin/v1", "/plugins/update", [
|
||||
"methods" => "POST",
|
||||
"callback" => [self::class, "update_plugin"],
|
||||
"permission_callback" => function () {
|
||||
return current_user_can("update_plugins");
|
||||
},
|
||||
"args" => [
|
||||
"file" => ["type" => "string", "required" => true],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public static function list_plugins()
|
||||
{
|
||||
self::load_plugin_admin_functions();
|
||||
self::load_update_functions();
|
||||
|
||||
$updates = get_site_transient("update_plugins");
|
||||
if (!is_object($updates) && function_exists("wp_update_plugins")) {
|
||||
wp_update_plugins();
|
||||
$updates = get_site_transient("update_plugins");
|
||||
}
|
||||
|
||||
$update_response = is_object($updates) && isset($updates->response) && is_array($updates->response)
|
||||
? $updates->response
|
||||
: [];
|
||||
|
||||
$items = [];
|
||||
foreach (get_plugins() as $file => $plugin) {
|
||||
$update = isset($update_response[$file]) ? $update_response[$file] : null;
|
||||
$items[] = [
|
||||
"file" => $file,
|
||||
"slug" => self::plugin_slug($file),
|
||||
"name" => isset($plugin["Name"]) ? $plugin["Name"] : self::plugin_slug($file),
|
||||
"description" => isset($plugin["Description"]) ? wp_strip_all_tags($plugin["Description"]) : "",
|
||||
"version" => isset($plugin["Version"]) ? $plugin["Version"] : "",
|
||||
"author" => isset($plugin["Author"]) ? wp_strip_all_tags($plugin["Author"]) : "",
|
||||
"pluginUri" => isset($plugin["PluginURI"]) ? esc_url_raw($plugin["PluginURI"]) : "",
|
||||
"authorUri" => isset($plugin["AuthorURI"]) ? esc_url_raw($plugin["AuthorURI"]) : "",
|
||||
"active" => is_plugin_active($file),
|
||||
"networkActive" => is_multisite() && is_plugin_active_for_network($file),
|
||||
"updateAvailable" => $update !== null,
|
||||
"newVersion" => $update && isset($update->new_version) ? $update->new_version : "",
|
||||
"canActivate" => current_user_can("activate_plugin", $file),
|
||||
"canDelete" => current_user_can("delete_plugins") && !is_plugin_active($file),
|
||||
"canUpdate" => current_user_can("update_plugins") && $update !== null,
|
||||
];
|
||||
}
|
||||
|
||||
usort($items, function ($a, $b) {
|
||||
if ($a["active"] !== $b["active"]) {
|
||||
return $a["active"] ? -1 : 1;
|
||||
}
|
||||
return strcasecmp($a["name"], $b["name"]);
|
||||
});
|
||||
|
||||
return new \WP_REST_Response([
|
||||
"items" => $items,
|
||||
"stats" => self::stats($items),
|
||||
"addUrl" => admin_url("plugin-install.php"),
|
||||
], 200);
|
||||
}
|
||||
|
||||
public static function activate_plugin($request)
|
||||
{
|
||||
self::load_plugin_admin_functions();
|
||||
$file = self::sanitize_file($request->get_param("file"));
|
||||
$result = activate_plugin($file);
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
return new \WP_Error("aurora_plugin_activate_failed", $result->get_error_message(), ["status" => 500]);
|
||||
}
|
||||
|
||||
self::clean_plugin_cache();
|
||||
return new \WP_REST_Response(["success" => true, "message" => __("Plugin activated.", "aurora-admin")], 200);
|
||||
}
|
||||
|
||||
public static function deactivate_plugin($request)
|
||||
{
|
||||
self::load_plugin_admin_functions();
|
||||
$file = self::sanitize_file($request->get_param("file"));
|
||||
deactivate_plugins($file);
|
||||
|
||||
self::clean_plugin_cache();
|
||||
return new \WP_REST_Response([
|
||||
"success" => true,
|
||||
"message" => __("Plugin deactivated.", "aurora-admin"),
|
||||
"redirect" => $file === "aurora-admin/aurora-admin.php" ? admin_url("plugins.php") : "",
|
||||
], 200);
|
||||
}
|
||||
|
||||
public static function delete_plugin($request)
|
||||
{
|
||||
self::load_plugin_admin_functions();
|
||||
if (!function_exists("delete_plugins")) {
|
||||
require_once ABSPATH . "wp-admin/includes/plugin.php";
|
||||
}
|
||||
|
||||
$file = self::sanitize_file($request->get_param("file"));
|
||||
if (is_plugin_active($file)) {
|
||||
return new \WP_Error("aurora_plugin_active_delete", __("Deactivate the plugin before deleting it.", "aurora-admin"), ["status" => 400]);
|
||||
}
|
||||
|
||||
$result = delete_plugins([$file]);
|
||||
if (is_wp_error($result)) {
|
||||
return new \WP_Error("aurora_plugin_delete_failed", $result->get_error_message(), ["status" => 500]);
|
||||
}
|
||||
|
||||
self::clean_plugin_cache();
|
||||
return new \WP_REST_Response(["success" => true, "message" => __("Plugin deleted.", "aurora-admin")], 200);
|
||||
}
|
||||
|
||||
public static function update_plugin($request)
|
||||
{
|
||||
self::load_plugin_admin_functions();
|
||||
self::load_update_functions();
|
||||
|
||||
require_once ABSPATH . "wp-admin/includes/class-wp-upgrader.php";
|
||||
require_once ABSPATH . "wp-admin/includes/file.php";
|
||||
require_once ABSPATH . "wp-admin/includes/misc.php";
|
||||
|
||||
$file = self::sanitize_file($request->get_param("file"));
|
||||
$skin = new \Automatic_Upgrader_Skin();
|
||||
$upgrader = new \Plugin_Upgrader($skin);
|
||||
$result = $upgrader->upgrade($file);
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
return new \WP_Error("aurora_plugin_update_failed", $result->get_error_message(), ["status" => 500]);
|
||||
}
|
||||
if (!$result) {
|
||||
$error = $skin->get_errors();
|
||||
$message = is_wp_error($error) && $error->has_errors()
|
||||
? $error->get_error_message()
|
||||
: __("Plugin update failed.", "aurora-admin");
|
||||
return new \WP_Error("aurora_plugin_update_failed", $message, ["status" => 500]);
|
||||
}
|
||||
|
||||
self::clean_plugin_cache();
|
||||
return new \WP_REST_Response(["success" => true, "message" => __("Plugin updated.", "aurora-admin")], 200);
|
||||
}
|
||||
|
||||
private static function load_plugin_admin_functions()
|
||||
{
|
||||
if (!function_exists("get_plugins") || !function_exists("is_plugin_active")) {
|
||||
require_once ABSPATH . "wp-admin/includes/plugin.php";
|
||||
}
|
||||
}
|
||||
|
||||
private static function load_update_functions()
|
||||
{
|
||||
if (!function_exists("wp_update_plugins")) {
|
||||
require_once ABSPATH . "wp-admin/includes/update.php";
|
||||
}
|
||||
}
|
||||
|
||||
private static function sanitize_file($file)
|
||||
{
|
||||
return plugin_basename(sanitize_text_field((string) $file));
|
||||
}
|
||||
|
||||
private static function plugin_slug($file)
|
||||
{
|
||||
$parts = explode("/", $file);
|
||||
return $parts[0];
|
||||
}
|
||||
|
||||
private static function clean_plugin_cache()
|
||||
{
|
||||
if (function_exists("wp_clean_plugins_cache")) {
|
||||
wp_clean_plugins_cache(true);
|
||||
}
|
||||
wp_cache_delete("plugins", "plugins");
|
||||
}
|
||||
|
||||
private static function stats($items)
|
||||
{
|
||||
$stats = ["total" => count($items), "active" => 0, "inactive" => 0, "updates" => 0];
|
||||
foreach ($items as $item) {
|
||||
if ($item["active"]) {
|
||||
$stats["active"]++;
|
||||
} else {
|
||||
$stats["inactive"]++;
|
||||
}
|
||||
if ($item["updateAvailable"]) {
|
||||
$stats["updates"]++;
|
||||
}
|
||||
}
|
||||
return $stats;
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,10 @@ class MenuSerializer
|
||||
return "";
|
||||
}
|
||||
|
||||
if ($slug === "plugins.php") {
|
||||
return admin_url("plugins.php?page=aurora-admin-plugins");
|
||||
}
|
||||
|
||||
// Already an absolute URL.
|
||||
if (preg_match('#^https?://#', $slug)) {
|
||||
return $slug;
|
||||
|
||||
Reference in New Issue
Block a user