Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19831cd2f3 | ||
|
|
faf9f35992 | ||
|
|
f5c48bced9 |
+1
-2
@@ -1053,8 +1053,7 @@ and closed the one remaining documented security gap.
|
||||
- First phase of the "full admin takeover" initiative: Posts, Pages, Media
|
||||
Library, Users, and Comments no longer redirect to a separate
|
||||
`admin.php?page=...` URL — they now hijack their native screens in place
|
||||
(`ScreenHijack.php`, registered via `HijackedNativePages.php`), the same
|
||||
approach the original uixpress reference plugin used. WordPress's
|
||||
(`ScreenHijack.php`, registered via `HijackedNativePages.php`). WordPress's
|
||||
`load-{page}.php` hook fires before the native screen prints anything, so
|
||||
Aurora buffers and discards the native output and mounts its own Vue page
|
||||
in its place — the URL never changes, so there's no separate slug that can
|
||||
|
||||
+2
-2
@@ -287,8 +287,8 @@ URL with no rewrite needed.
|
||||
|
||||
**Plugins is the one exception, deliberately left on the old redirect
|
||||
mechanism** — `plugins.php` prints output in a way that can't be cleanly
|
||||
buffered (confirmed by the old uixpress reference plugin's own abandoned
|
||||
attempt at hijacking it the same way). It still has a real
|
||||
buffered (confirmed via a prior abandoned attempt at hijacking it the same
|
||||
way). It still has a real
|
||||
`aurora-admin-plugins` submenu under `plugins.php` and its own
|
||||
`redirect_native_pages()` branch in `SubApps.php`.
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ use AuroraAdmin\Media\SvgUploads;
|
||||
use AuroraAdmin\Media\MediaFolders;
|
||||
use AuroraAdmin\ImageFormats\ImageFormats;
|
||||
use AuroraAdmin\Security\Security;
|
||||
use AuroraAdmin\Code\CodeInjection;
|
||||
use AuroraAdmin\Rest\MediaFolderData;
|
||||
use AuroraAdmin\Rest\BugReportData;
|
||||
use AuroraAdmin\Rest\CompanionPluginData;
|
||||
@@ -99,6 +100,7 @@ class AuroraAdmin
|
||||
new MediaFolders();
|
||||
new ImageFormats();
|
||||
new Security();
|
||||
new CodeInjection();
|
||||
new MediaFolderData();
|
||||
new BugReportData();
|
||||
new CompanionPluginData();
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
namespace AuroraAdmin\Code;
|
||||
|
||||
use AuroraAdmin\Options\Settings;
|
||||
|
||||
defined("ABSPATH") || exit();
|
||||
|
||||
/**
|
||||
* Custom code injection (Settings > Code).
|
||||
*
|
||||
* Four admin-authored fields, only settable by manage_options users on the
|
||||
* settings page:
|
||||
* - Admin CSS → wrapped in a <style> in the admin <head>
|
||||
* - Admin JS → wrapped in a <script> in the admin footer
|
||||
* - Header scripts → printed raw in the front-end <head>
|
||||
* - Footer scripts → printed raw before the front-end </body>
|
||||
*
|
||||
* Header/footer scripts are printed verbatim (they're meant to hold full
|
||||
* <script>/<meta> tags — analytics, verification, etc.), the same trusted-
|
||||
* admin model as any "insert headers and footers" tool. Admin CSS/JS is the
|
||||
* user's own code too, but is wrapped in the right tag for them and guarded
|
||||
* against accidentally breaking out of that tag.
|
||||
*/
|
||||
class CodeInjection
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
add_action("admin_head", [self::class, "admin_css"], 999);
|
||||
add_action("admin_print_footer_scripts", [self::class, "admin_js"], 999);
|
||||
add_action("wp_head", [self::class, "header_scripts"], 999);
|
||||
add_action("wp_footer", [self::class, "footer_scripts"], 999);
|
||||
}
|
||||
|
||||
public static function admin_css()
|
||||
{
|
||||
$css = trim((string) Settings::get("custom_admin_css", ""));
|
||||
if ($css === "") {
|
||||
return;
|
||||
}
|
||||
// Keep the CSS from breaking out of its own <style> element.
|
||||
$css = str_ireplace("</style>", "", $css);
|
||||
echo "\n<style id=\"aurora-custom-admin-css\">\n" . $css . "\n</style>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin-authored stylesheet, output raw inside a <style> tag by design; </style> stripped above to prevent breakout.
|
||||
}
|
||||
|
||||
public static function admin_js()
|
||||
{
|
||||
$js = trim((string) Settings::get("custom_admin_js", ""));
|
||||
if ($js === "") {
|
||||
return;
|
||||
}
|
||||
$js = str_ireplace("</script>", "<\\/script>", $js);
|
||||
echo "\n<script id=\"aurora-custom-admin-js\">\n" . $js . "\n</script>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin-authored script, output raw inside a <script> tag by design.
|
||||
}
|
||||
|
||||
public static function header_scripts()
|
||||
{
|
||||
$code = trim((string) Settings::get("header_scripts", ""));
|
||||
if ($code === "") {
|
||||
return;
|
||||
}
|
||||
echo "\n" . $code . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin-authored header markup (scripts/meta), printed verbatim by design.
|
||||
}
|
||||
|
||||
public static function footer_scripts()
|
||||
{
|
||||
$code = trim((string) Settings::get("footer_scripts", ""));
|
||||
if ($code === "") {
|
||||
return;
|
||||
}
|
||||
echo "\n" . $code . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin-authored footer markup (scripts), printed verbatim by design.
|
||||
}
|
||||
}
|
||||
@@ -42,10 +42,25 @@ class LoginStyle
|
||||
}
|
||||
|
||||
$logo = trim((string) Settings::get("login_logo", ""));
|
||||
$bg_image = trim((string) Settings::get("login_bg_image", ""));
|
||||
// Colors fall back to the original fixed dark theme when unset; invalid
|
||||
// values are dropped by sanitize_hex_color (returns null) then fall back.
|
||||
$bg_color = sanitize_hex_color((string) Settings::get("login_bg_color", "")) ?: "";
|
||||
$form_bg = sanitize_hex_color((string) Settings::get("login_form_bg", "")) ?: "#1c2333";
|
||||
$accent = sanitize_hex_color((string) Settings::get("login_button_color", "")) ?: "#4f7cff";
|
||||
$custom_css = str_ireplace("</style>", "", (string) Settings::get("login_custom_css", ""));
|
||||
|
||||
if ($bg_image) {
|
||||
$background = "url(" . esc_url($bg_image) . ") center / cover no-repeat";
|
||||
} elseif ($bg_color) {
|
||||
$background = $bg_color;
|
||||
} else {
|
||||
$background = "linear-gradient(135deg, #1b2333 0%, #0f1420 100%)";
|
||||
}
|
||||
?>
|
||||
<style>
|
||||
body.login {
|
||||
background: linear-gradient(135deg, #1b2333 0%, #0f1420 100%);
|
||||
background: <?php echo $background; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- composed from a sanitize_hex_color() color or esc_url()'d image above. ?>;
|
||||
}
|
||||
body.login #login h1 a {
|
||||
<?php if ($logo): ?>
|
||||
@@ -64,7 +79,7 @@ class LoginStyle
|
||||
body.login form#loginform,
|
||||
body.login form#registerform,
|
||||
body.login form#lostpasswordform {
|
||||
background: #1c2333;
|
||||
background: <?php echo esc_attr($form_bg); ?>;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35);
|
||||
@@ -84,8 +99,8 @@ class LoginStyle
|
||||
body.login input[type="text"]:focus,
|
||||
body.login input[type="password"]:focus,
|
||||
body.login input[type="email"]:focus {
|
||||
border-color: #4f7cff;
|
||||
box-shadow: 0 0 0 1px #4f7cff;
|
||||
border-color: <?php echo esc_attr($accent); ?>;
|
||||
box-shadow: 0 0 0 1px <?php echo esc_attr($accent); ?>;
|
||||
}
|
||||
body.login .forgetmenot label {
|
||||
color: #9fb0d0;
|
||||
@@ -99,23 +114,23 @@ class LoginStyle
|
||||
color: #fff;
|
||||
}
|
||||
body.login .wp-core-ui .button-primary {
|
||||
background: #4f7cff;
|
||||
border-color: #4f7cff;
|
||||
background: <?php echo esc_attr($accent); ?>;
|
||||
border-color: <?php echo esc_attr($accent); ?>;
|
||||
box-shadow: none;
|
||||
text-shadow: none;
|
||||
}
|
||||
body.login .wp-core-ui .button-primary:hover,
|
||||
body.login .wp-core-ui .button-primary:focus {
|
||||
background: #3b63e0;
|
||||
border-color: #3b63e0;
|
||||
filter: brightness(0.92);
|
||||
}
|
||||
body.login #login_error,
|
||||
body.login .message,
|
||||
body.login .success {
|
||||
background: #232b40;
|
||||
border-left-color: #4f7cff;
|
||||
border-left-color: <?php echo esc_attr($accent); ?>;
|
||||
color: #e2e8f4;
|
||||
}
|
||||
<?php echo $custom_css; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- admin-authored login CSS, output raw by design; </style> stripped above. ?>
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
@@ -138,6 +138,7 @@ const installCompanion = async () => {
|
||||
<textarea
|
||||
v-else-if="field.type === 'textarea'"
|
||||
class="field__input field__textarea"
|
||||
:class="{ 'field__textarea--mono': field.mono }"
|
||||
:placeholder="field.placeholder || ''"
|
||||
:rows="field.rows || 6"
|
||||
:value="val || ''"
|
||||
@@ -346,6 +347,11 @@ const installCompanion = async () => {
|
||||
}
|
||||
.field__input:focus { outline: none; border-color: var(--aurora-accent); }
|
||||
.field__textarea { resize: vertical; font-family: inherit; line-height: 1.5; }
|
||||
.field__textarea--mono {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
|
||||
font-size: 0.8125rem;
|
||||
tab-size: 2;
|
||||
}
|
||||
|
||||
.field__color { display: flex; align-items: center; gap: 10px; }
|
||||
.field__color input[type='color'] {
|
||||
|
||||
@@ -150,6 +150,48 @@ export const settingsGroups = [
|
||||
type: 'image',
|
||||
label: 'Login logo',
|
||||
description: 'Replace the WordPress logo on the login screen.',
|
||||
dependsOn: 'style_login',
|
||||
},
|
||||
{
|
||||
key: 'login_bg_image',
|
||||
type: 'image',
|
||||
label: 'Background image',
|
||||
description: 'Full-screen background for the login page. Overrides the background color below.',
|
||||
dependsOn: 'style_login',
|
||||
},
|
||||
{
|
||||
key: 'login_bg_color',
|
||||
type: 'color',
|
||||
label: 'Background color',
|
||||
description: 'Solid background color for the login page (used when no background image is set).',
|
||||
default: '#0f1420',
|
||||
dependsOn: 'style_login',
|
||||
},
|
||||
{
|
||||
key: 'login_form_bg',
|
||||
type: 'color',
|
||||
label: 'Form background',
|
||||
description: 'Background color of the login form panel.',
|
||||
default: '#1c2333',
|
||||
dependsOn: 'style_login',
|
||||
},
|
||||
{
|
||||
key: 'login_button_color',
|
||||
type: 'color',
|
||||
label: 'Button color',
|
||||
description: 'Color of the primary “Log In” button and the field focus accent.',
|
||||
default: '#4f7cff',
|
||||
dependsOn: 'style_login',
|
||||
},
|
||||
{
|
||||
key: 'login_custom_css',
|
||||
type: 'textarea',
|
||||
mono: true,
|
||||
rows: 8,
|
||||
label: 'Custom login CSS',
|
||||
description: 'Extra CSS applied to the login screen, after Aurora’s own styles.',
|
||||
placeholder: 'body.login { … }',
|
||||
dependsOn: 'style_login',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -388,6 +430,69 @@ export const settingsGroups = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'code',
|
||||
title: 'Code',
|
||||
icon: 'dashicons-editor-code',
|
||||
categories: [
|
||||
{
|
||||
id: 'custom-code',
|
||||
title: 'Custom CSS & JS',
|
||||
subtitle: 'Inject your own CSS and JavaScript into the WordPress admin',
|
||||
icon: 'dashicons-admin-customizer',
|
||||
fields: [
|
||||
{
|
||||
key: 'custom_admin_css',
|
||||
type: 'textarea',
|
||||
mono: true,
|
||||
rows: 10,
|
||||
label: 'Admin CSS',
|
||||
description:
|
||||
'CSS added to every wp-admin page, inside a <style> tag in the admin <head>. Enter CSS only — no <style> tags needed.',
|
||||
placeholder: '.wrap { max-width: 1200px; }',
|
||||
},
|
||||
{
|
||||
key: 'custom_admin_js',
|
||||
type: 'textarea',
|
||||
mono: true,
|
||||
rows: 10,
|
||||
label: 'Admin JavaScript',
|
||||
description:
|
||||
'JavaScript added to the admin footer, inside a <script> tag. Enter JS only — no <script> tags needed.',
|
||||
placeholder: "console.log('Hello from Aurora');",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'header-footer',
|
||||
title: 'Header & footer scripts',
|
||||
subtitle: 'Inject raw code into your site’s front-end <head> and footer',
|
||||
icon: 'dashicons-editor-code',
|
||||
fields: [
|
||||
{
|
||||
key: 'header_scripts',
|
||||
type: 'textarea',
|
||||
mono: true,
|
||||
rows: 8,
|
||||
label: 'Header scripts',
|
||||
description:
|
||||
'Raw HTML/JS printed in the front-end <head> — analytics snippets, verification meta tags, etc. Include the full <script>/<meta> tags.',
|
||||
placeholder: '<!-- e.g. analytics -->\n<script>…</script>',
|
||||
},
|
||||
{
|
||||
key: 'footer_scripts',
|
||||
type: 'textarea',
|
||||
mono: true,
|
||||
rows: 8,
|
||||
label: 'Footer scripts',
|
||||
description:
|
||||
'Raw HTML/JS printed just before </body> on the front end. Include the full <script> tags.',
|
||||
placeholder: '<script>…</script>',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'add-ons',
|
||||
title: 'Add-ons',
|
||||
|
||||
@@ -61,6 +61,7 @@ require AURORA_ADMIN_PATH . "admin/src/Media/SvgUploads.php";
|
||||
require AURORA_ADMIN_PATH . "admin/src/Media/MediaFolders.php";
|
||||
require AURORA_ADMIN_PATH . "admin/src/ImageFormats/ImageFormats.php";
|
||||
require AURORA_ADMIN_PATH . "admin/src/Security/Security.php";
|
||||
require AURORA_ADMIN_PATH . "admin/src/Code/CodeInjection.php";
|
||||
require AURORA_ADMIN_PATH . "admin/src/Rest/MediaFolderData.php";
|
||||
require AURORA_ADMIN_PATH . "admin/src/Rest/BugReportData.php";
|
||||
require AURORA_ADMIN_PATH . "admin/src/Rest/CompanionPluginData.php";
|
||||
|
||||
Reference in New Issue
Block a user