<?php
|
namespace JVBase\managers;
|
|
use JVBase\utility\Features;
|
|
if (!defined('ABSPATH')) {
|
exit; // Exit if accessed directly
|
}
|
|
/**
|
* AdminManager - Handles Edmonton Ink admin pages in the WordPress dashboard
|
*
|
* Creates a main settings page with the ability for other classes to register subpages
|
*/
|
class AdminPages
|
{
|
protected array $subpages;
|
protected array $actions;
|
protected string $page_hook;
|
protected string $prefix;
|
protected array $main_page;
|
/**
|
* Constructor
|
*/
|
public function __construct()
|
{
|
// Set up main page details
|
$this->main_page = [
|
'page_title' => 'JakeVan Settings',
|
'menu_title' => 'JakeVan',
|
'capability' => 'manage_options',
|
'menu_slug' => BASE . 'settings',
|
'icon' => jvbCSSIcon('settings'),
|
'position' => 0
|
];
|
$this->subpages = apply_filters('jvbAdminSubpages', []);
|
// delete_option(BASE.'admin_actions');
|
// delete_option(BASE.'admin_subpages');
|
// $this->getSubpages();
|
|
$this->getActions();
|
|
// Hook into WordPress admin
|
add_action('admin_menu', [$this, 'registerAdminPages']);
|
add_action('admin_enqueue_scripts', [$this, 'enqueueAdminAssets']);
|
}
|
|
/**
|
* Register a subpage to appear under the main settings page
|
*
|
* @param string $page_title The text to be displayed in the title tags of the page
|
* @param string $menu_title The text to be used for the menu
|
* @param string $capability The capability required to access the page
|
* @param string $menu_slug The slug name to refer to this menu by
|
* @param callable $callback The function to be called to output the content for this page
|
* @param string|null $icon
|
* @param int|null $position The position in the menu order (optional)
|
* @return bool Success or failure
|
*/
|
public function registerSubpage(
|
string $page_title,
|
string $menu_title,
|
string $capability,
|
string $menu_slug,
|
callable $callback,
|
string|null $icon = null,
|
int|null $position = null
|
):bool {
|
return true;
|
// if (empty($page_title) ||
|
// empty($menu_title) ||
|
// empty($capability) ||
|
// empty($menu_slug) ||
|
// !is_callable($callback)) {
|
// error_log('This wasn\'t registered: '.print_r([
|
// 'page_title' => $page_title,
|
// 'menu_title' => $menu_title,
|
// 'capability' => $capability,
|
// 'menu_slug' => $menu_slug,
|
// 'callback' => $callback
|
// ], true));
|
// return false;
|
// }
|
//
|
// $this->getSubpages();
|
//
|
// $subpage = [
|
// 'page_title' => $page_title,
|
// 'menu_title' => $menu_title,
|
// 'capability' => $capability,
|
// 'menu_slug' => BASE . $menu_slug,
|
// 'callback' => $callback,
|
// 'position' => $position
|
// ];
|
//
|
// if (!is_null($position)) {
|
// $subpage['position'] = $position;
|
// }
|
// if (!is_null($icon)) {
|
// $subpage['icon'] = $this->getIcon($icon);
|
// }
|
//
|
// $this->subpages[] = $subpage;
|
//
|
// $this->setSubpages();
|
//
|
// return true;
|
}
|
|
protected function getSubpages()
|
{
|
$this->subpages = get_option(BASE.'admin_subpages', []);
|
}
|
|
protected function setSubpages()
|
{
|
update_option(BASE.'admin_subpages', $this->subpages);
|
}
|
|
public function registerAction(
|
string $label,
|
string $slug,
|
string $capability,
|
string|null $icon = null
|
):bool {
|
$this->getActions();
|
$action = [
|
'label' => $label,
|
'slug' => sanitize_title($slug),
|
'capability'=> $capability,
|
];
|
if (!is_null($icon)) {
|
$action['icon'] = $icon;
|
}
|
$this->actions[sanitize_title($slug)] = $action;
|
$this->setActions();
|
return true;
|
}
|
|
protected function getActions()
|
{
|
$this->actions = get_option(BASE.'admin_actions', []);
|
}
|
|
protected function setActions()
|
{
|
update_option(BASE.'admin_actions', $this->actions);
|
}
|
/**
|
* Register admin pages with WordPress
|
*/
|
public function registerAdminPages():void
|
{
|
// Add main settings page
|
$this->page_hook = add_menu_page(
|
$this->main_page['page_title'],
|
$this->main_page['menu_title'],
|
$this->main_page['capability'],
|
$this->main_page['menu_slug'],
|
[$this, 'renderMainPage'],
|
$this->main_page['icon'],
|
$this->main_page['position']
|
);
|
|
// Add main page as a subpage to make it appear in submenu
|
add_submenu_page(
|
$this->main_page['menu_slug'],
|
$this->main_page['page_title'],
|
'Overview',
|
$this->main_page['capability'],
|
$this->main_page['menu_slug']
|
);
|
|
add_submenu_page(
|
$this->main_page['menu_slug'],
|
'Cache',
|
'Cache',
|
'manage_options',
|
BASE.'cache',
|
[$this, 'renderCachePage']
|
);
|
|
// $this->getSubpages();
|
// Add registered subpages
|
foreach ($this->subpages as $page) {
|
if (!array_key_exists('page_title', $page) ||
|
!array_key_exists('menu_title', $page) ||
|
!array_key_exists('capability', $page) ||
|
!array_key_exists('menu_slug', $page) ||
|
!array_key_exists('callback', $page)) {
|
error_log('Invalid admin page config for '.print_r($page, true));
|
continue;
|
}
|
add_submenu_page(
|
$this->main_page['menu_slug'],
|
$page['page_title'],
|
$page['menu_title'],
|
$page['capability'],
|
$page['menu_slug'],
|
$page['callback']
|
);
|
}
|
}
|
|
/**
|
* Render the main settings page
|
*/
|
public function renderMainPage():void
|
{
|
?>
|
<div class="wrap jvb-admin-wrap">
|
<h1><?= esc_html($this->main_page['page_title']); ?></h1>
|
|
<div class="jvb-admin-content">
|
<?php $this->renderDashboard(); ?>
|
</div>
|
</div>
|
<?php
|
}
|
|
/**
|
* Render the main dashboard overview
|
*/
|
protected function renderDashboard():void
|
{
|
?>
|
<div class="jvb-dashboard-grid">
|
<div class="jvb-dashboard-card">
|
<h2>System Status</h2>
|
<ul class="jvb-status-list">
|
<?php $this->renderStatusItems(); ?>
|
</ul>
|
</div>
|
|
<div class="jvb-dashboard-card">
|
<h2>Active Users</h2>
|
<div class="jvb-active-users">
|
<?php $this->renderUserStats(); ?>
|
</div>
|
</div>
|
|
<div class="jvb-dashboard-card">
|
<h2>Recent Content</h2>
|
<div class="jvb-recent-content">
|
<?php $this->renderRecentContent(); ?>
|
</div>
|
</div>
|
|
<div class="jvb-dashboard-card jvb-quick-links">
|
<h2>Quick Actions</h2>
|
<div class="jvb-link-grid">
|
<?php $this->renderActions(); ?>
|
</div>
|
</div>
|
|
<div class="jvb-dashboard-card jvb-quick-links">
|
<h2>Quick Links</h2>
|
<div class="jvb-link-grid">
|
<?php $this->renderQuickLinks(); ?>
|
</div>
|
</div>
|
</div>
|
<?php
|
|
}
|
|
/**
|
* Render system status information
|
*/
|
protected function renderStatusItems():void
|
{
|
// Get queue stats
|
$queue_status = JVB()->queue()->getQueueStatus();
|
error_log('Queue Status: '.print_r($queue_status, true));
|
|
// Other system checks
|
$memory_usage = memory_get_usage(true) / 1024 / 1024;
|
|
// Display status items
|
?>
|
<li>
|
<span class="status-label">Queue Status:</span>
|
<span class="status-value">
|
<?= (isset($queue_status['pending']) ? esc_html($queue_status['pending']) : '0'); ?> pending,
|
<?= (isset($queue_status['processing']) ?
|
esc_html($queue_status['processing']) :
|
'0'); ?> processing
|
</span>
|
</li>
|
<li>
|
<span class="status-label">Memory Usage:</span>
|
<span class="status-value"><?= round($memory_usage, 2); ?> MB</span>
|
</li>
|
<li>
|
<span class="status-label">Plugin Version:</span>
|
<span class="status-value">1.0.0</span>
|
</li>
|
<li>
|
<span class="status-label">Content Types:</span>
|
<span class="status-value"><?= count(JVB_CONTENT); ?> registered</span>
|
</li>
|
<li>
|
<span class="status-label">Taxonomies:</span>
|
<span class="status-value"><?= count(JVB_TAXONOMY); ?> registered</span>
|
</li>
|
<?php
|
}
|
|
/**
|
* Render user statistics
|
*/
|
protected function renderUserStats():void
|
{
|
$roles = get_editable_roles();
|
|
foreach ($roles as $role => $config) {
|
$count = count_users()['avail_roles'][$role]??0;
|
?>
|
<div class="jvb-user-stat">
|
<span class="jvb-stat-number"><?= esc_html($count); ?></span>
|
<span class="jvb-stat-label"><?=$config['name']?></span>
|
</div>
|
<?php
|
}
|
}
|
|
/**
|
* Render recent content statistics
|
*/
|
protected function renderRecentContent():void
|
{
|
// Get content counts from the last 7 days
|
global $wpdb;
|
|
$week_ago = date('Y-m-d H:i:s', strtotime('-7 days'));
|
$content_types = [];
|
foreach (JVB_CONTENT as $content => $config) {
|
$content_types[jvbCheckBase($content)] = $config['plural'];
|
}
|
|
?>
|
<table class="jvb-content-table">
|
<tr>
|
<th>Content Type</th>
|
<th>Last 7 Days</th>
|
<th>Total</th>
|
</tr>
|
<?php foreach ($content_types as $type => $label) : ?>
|
<?php
|
$recent_count = $wpdb->get_var($wpdb->prepare(
|
"SELECT COUNT(*) FROM {$wpdb->posts}
|
WHERE post_type = %s
|
AND post_date > %s
|
AND post_status = 'publish'",
|
$type,
|
$week_ago
|
));
|
|
$total_count = $wpdb->get_var($wpdb->prepare(
|
"SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'publish'",
|
$type
|
));
|
?>
|
<tr>
|
<td><?= esc_html($label); ?></td>
|
<td><?= esc_html($recent_count); ?></td>
|
<td><?= esc_html($total_count); ?></td>
|
</tr>
|
<?php endforeach; ?>
|
</table>
|
<?php
|
}
|
|
/**
|
* Render quick links for common admin tasks
|
*/
|
protected function renderQuickLinks():void
|
{
|
$links = [
|
[
|
'title' => 'Moderate Artist Requests',
|
'url' => admin_url('admin.php?page=' . BASE . 'artist_approval'),
|
'icon' => 'dashicons-id'
|
],
|
[
|
'title' => 'Manage Shops',
|
'url' => admin_url('edit-tags.php?taxonomy=' . BASE . 'shop'),
|
'icon' => 'dashicons-store'
|
],
|
[
|
'title' => 'Queue Status',
|
'url' => admin_url('admin.php?page=' . BASE . 'queue_status'),
|
'icon' => 'dashicons-list-view'
|
],
|
[
|
'title' => 'Manage Styles',
|
'url' => admin_url('edit-tags.php?taxonomy=' . BASE . 'style'),
|
'icon' => 'dashicons-admin-customizer'
|
]
|
];
|
|
foreach ($links as $link) {
|
?>
|
<a href="<?= esc_url($link['url']); ?>" class="jvb-quick-link">
|
<span class="dashicons <?= esc_attr($link['icon']); ?>"></span>
|
<span class="jvb-link-title"><?= esc_html($link['title']); ?></span>
|
</a>
|
<?php
|
}
|
}
|
|
protected function renderActions()
|
{
|
foreach ($this->actions as $action) {
|
if (current_user_can($action['capability'])) {
|
?>
|
<a data-action="<?=$action['slug']?>" class="jvb-action">
|
<?= jvbIcon($action['icon']); ?>
|
<span class="jvb-link-title"><?= esc_html($action['label'])?></span>
|
<span class="loader"><?=jvbIcon('arrows-clockwise')?><?=jvbIcon('check')?></span>
|
</a>
|
<?php
|
}
|
}
|
}
|
|
/**
|
* Enqueue admin assets (CSS and JS)
|
*
|
* @param string $hook Current admin page
|
*/
|
public function enqueueAdminAssets(string $hook):void
|
{
|
// Check if we're on an Edmonton Ink admin page
|
if (strpos($hook, BASE) === false) {
|
return;
|
}
|
|
// Enqueue admin styles
|
wp_enqueue_style(
|
'jvb-admin-styles',
|
JVB_URL . 'assets/css/admin.css',
|
[],
|
'1.0.0'
|
);
|
|
// Enqueue admin scripts
|
wp_enqueue_script(
|
'jvb-admin-scripts',
|
JVB_URL . 'assets/js/admin.js',
|
[],
|
'1.0.0',
|
true
|
);
|
|
wp_localize_script(
|
'jvb-admin-scripts',
|
'jvbSettings',
|
[
|
'api' => rest_url('jvb/v1/admin-action'),
|
'nonce' => wp_create_nonce('wp_rest'),
|
'action' => wp_create_nonce('itsme'),
|
]
|
);
|
}
|
|
/**
|
* Create a custom SVG icon for the admin menu
|
* @param string $icon Icon name
|
* @param bool $css Whether to convert to CSS
|
* @return string Base64 encoded SVG icon
|
*/
|
protected function getIcon(string $icon = 'logo', bool $css = false): string
|
{
|
$svg = jvbIcon($icon, ['wrap' => false]);
|
if ($css) {
|
// For CSS, replace currentColor with brand color
|
$svg = str_replace('currentColor', '#FF0080', $svg);
|
|
// Clean and prepare the SVG for CSS
|
$svg = preg_replace('/[\r\n\t]+/', ' ', $svg); // Remove newlines and tabs
|
$svg = preg_replace('/\s{2,}/', ' ', $svg); // Minimize spaces
|
$svg = trim($svg); // Trim whitespace
|
|
// Base64 encoding is the most reliable approach for CSS background images
|
return 'url(data:image/svg+xml;base64,' . base64_encode($svg) . ')';
|
}
|
// Convert the SVG to a data URI
|
return 'data:image/svg+xml;base64,' . base64_encode($svg);
|
}
|
|
public function renderCachePage()
|
{
|
$groups = get_option(BASE.'all_cache_groups', []);
|
|
?>
|
<h1>Manage Cache</h1>
|
<?php
|
foreach ($groups as $group => $caches) {
|
?>
|
<details>
|
<summary class="row btw"><h2><?=$group?></h2></summary>
|
<table>
|
<thead>
|
<tr>
|
<th scope="col"><input type="checkbox" name="select-all-<?=$group?>" id="select-all-<?=$group?>">
|
<label for="select-all-<?=$group?>">All</label></th>
|
<th scope="col">Cache Key</th>
|
<th scope="col">Actions</th>
|
</tr>
|
</thead>
|
<tbody>
|
<?php
|
foreach ($caches as $key) {
|
?>
|
<tr>
|
<td><input type="checkbox" name="select-<?=$group?>-<?=$key?>" id="select-<?=$group?>-<?=$key?>"><label for="select-<?=$group?>-<?=$key?>"></label></td>
|
<td><?= $key ?></td>
|
<td><button type="button" data-action="flush-<?=$group?>-<?=$key?>"><?= jvbIcon('trash')?></button></td>
|
</tr>
|
<?php
|
}
|
?>
|
</tbody>
|
</table>
|
</details>
|
<?php
|
}
|
}
|
}
|