main_page = [ 'page_title' => 'JakeVan Settings', 'menu_title' => 'JakeVan', 'capability' => 'manage_options', 'menu_slug' => BASE . 'settings', 'icon' => jvbCSSIcon('settings'), 'position' => 0 ]; $this->subpages = get_option(BASE.'adminSubpage', []); // 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']); add_filter(BASE.'admin_action_filter', [$this, 'handleCacheActions'], 10, 3); add_action('rest_api_init', [$this, 'registerRestRoutes']); // Handle form submissions add_action('admin_init', [$this, 'handleAdminPageSubmission']); add_action('admin_notices', [$this, 'displayAdminNotices']); } /** * Register REST API routes for admin actions */ public function registerRestRoutes(): void { register_rest_route('jvb/v1', '/admin-cache', [ 'methods' => 'POST', 'callback' => [$this, 'handleCacheAction'], 'permission_callback' => [$this, 'checkAdminPermission'] ]); register_rest_route('jvb/v1', '/admin-icons', [ 'methods' => 'POST', 'callback' => [$this, 'handleIconAction'], 'permission_callback' => [$this, 'checkAdminPermission'] ]); } /** * Check if user has admin permissions */ public function checkAdminPermission(\WP_REST_Request $request): bool { if (!current_user_can('manage_options')) { return false; } // Verify nonce $nonce = $request->get_header('X-WP-Nonce'); if (!wp_verify_nonce($nonce, 'wp_rest')) { return false; } return true; } /** * Handle cache-related actions */ public function handleCacheAction(\WP_REST_Request $request): \WP_REST_Response { $action = sanitize_text_field($request->get_param('action')); switch ($action) { case 'flush-all': $total = Cache::flushAll(); return new \WP_REST_Response([ 'success' => true, 'message' => $total.' caches flushed successfully' ]); case 'flush-cache': $group = sanitize_text_field($request->get_param('group')); if (empty($group)) { return new \WP_REST_Response([ 'success' => false, 'message' => 'No cache group specified' ], 400); } Cache::for($group)?->flush(); return new \WP_REST_Response([ 'success' => true, 'message' => "Cache group '{$group}' flushed successfully" ]); default: return new \WP_REST_Response([ 'success' => false, 'message' => 'Invalid action' ], 400); } } /** * Handle icon-related actions */ public function handleIconAction(\WP_REST_Request $request): \WP_REST_Response { $action = sanitize_text_field($request->get_param('action')); $source = sanitize_text_field($request->get_param('source') ?? 'icons'); $icons = IconsManager::for($source); switch ($action) { case 'refresh-icons': // Force regenerate CSS immediately $icons->forceRefresh(); IconsManager::regenerateAllCSS([$source => true]); return new \WP_REST_Response([ 'success' => true, 'message' => "Icon CSS regenerated successfully for '{$source}'" ]); case 'refresh-all-icons': // Regenerate all icon sources foreach (['icons', 'forms', 'dash'] as $src) { IconsManager::for($src)->forceRefresh(); } IconsManager::regenerateAllCSS(); return new \WP_REST_Response([ 'success' => true, 'message' => 'All icon CSS files regenerated successfully' ]); case 'restore-icon-version': $timestamp = (int)$request->get_param('timestamp'); if (empty($timestamp)) { return new \WP_REST_Response([ 'success' => false, 'message' => 'No timestamp provided' ], 400); } if ($icons->restoreVersion($timestamp)) { return new \WP_REST_Response([ 'success' => true, 'message' => 'Icon version restored successfully' ]); } return new \WP_REST_Response([ 'success' => false, 'message' => 'Failed to restore icon version' ], 500); case 'merge-icon-versions': $timestamps = $request->get_param('timestamps'); if (empty($timestamps) || !is_array($timestamps)) { return new \WP_REST_Response([ 'success' => false, 'message' => 'No versions selected for merging' ], 400); } $timestamps = array_map('intval', $timestamps); if (count($timestamps) < 2) { return new \WP_REST_Response([ 'success' => false, 'message' => 'Please select at least 2 versions to merge' ], 400); } if ($icons->mergeVersions($timestamps)) { // Regenerate CSS after merge IconsManager::regenerateAllCSS([$source => true]); return new \WP_REST_Response([ 'success' => true, 'message' => 'Icon versions merged successfully' ]); } return new \WP_REST_Response([ 'success' => false, 'message' => 'Failed to merge icon versions' ], 500); default: return new \WP_REST_Response([ 'success' => false, 'message' => 'Invalid action' ], 400); } } /** * 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'] ); add_submenu_page( $this->main_page['menu_slug'], 'Icon Management', 'Icons', 'manage_options', BASE.'icons', [$this, 'renderIconsPage'] ); // $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 { ?>

main_page['page_title']); ?>

renderDashboard(); ?>

System Status

Active Users

renderUserStats(); ?>

Recent Content

renderRecentContent(); ?>
queue()->getStatus(); error_log('Queue Status: '.print_r($queue_status, true)); // Other system checks $memory_usage = memory_get_usage(true) / 1024 / 1024; // Display status items ?>
  • Queue Status: pending, processing
  • Memory Usage: MB
  • Plugin Version: 1.0.0
  • Content Types: registered
  • Taxonomies: registered
  • $config) { $count = count_users()['avail_roles'][$role]??0; ?>
    $config) { $content_types[jvbCheckBase($content)] = $config['plural']; } ?> $label) : ?> 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 )); ?>
    Content Type Last 7 Days Total
    '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) { ?> actions as $action) { if (current_user_can($action['capability'])) { ?> 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 = jvbDashIcon($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():void { $groups = Cache::getAllGroups(); // Separate generic vs. specific caches $generic_groups = []; $content_specific = []; $nonce = wp_create_nonce('wp_rest'); // Separate by type $generic = []; $specific = []; foreach ($groups as $group => $data) { if ($this->isBoundToContentOrTaxonomy($group)) { $specific[$group] = $data; } else { $generic[$group] = $data; } } ?>

    Cache Management

    Generic Caches & Connections

    $configs): ?>
    Cache Group Connected To Actions
    No generic caches registered
    formatConnections($configs); ?>

    Content-Specific Caches

    $configs): ?> $data): ?>
    Cache Group Connected To Actions
    formatConnections($configs); ?>
    formatConnections($data); ?>
    $config) { if (jvbNoBase($key) === $group) { return true; } } } if (defined('JVB_TAXONOMY')) { foreach (JVB_TAXONOMY as $key => $config) { if (jvbNoBase($key) === $group) { return true; } } } return false; } protected function formatConnections(array $data): string { $parts = []; if (!empty($data['connects_to'])) { $targets = array_map(function($conn) { $flush_text = $conn['flush'] ? ' (flush all)' : ''; return $conn['group'] . $flush_text; }, $data['connects_to']); $parts[] = 'Invalidates: ' . implode(', ', $targets); } if (!empty($data['connected_from'])) { $sources = array_map(fn($conn) => $conn['group'], $data['connected_from']); $parts[] = 'Invalidated by: ' . implode(', ', $sources); } return $parts ? implode('
    ', $parts) : 'No connections'; } public function handleCacheActions($response, $request, $action):WP_REST_Response { if (!str_starts_with($action, 'flush-')) { return $response; } if ($action === 'flush-all') { wp_cache_flush(); return new WP_REST_Response([ 'success' => true, 'message' => 'All caches flushed successfully' ]); } if (str_starts_with($action, 'flush-cache')) { $group = $request->get_param('group'); if (empty($group)) { return new WP_REST_Response([ 'success' => false, 'message' => 'No cache group specified' ], 400); } $group = sanitize_text_field($request->get_param('group')); Cache::invalidateGroup($group); return new WP_REST_Response([ 'success' => true, 'message' => "Cache group '{$group}' flushed successfully" ]); } return $response; } public function renderIconsPage():void { // Get current source from query param or default to 'icons' $current_source = $_GET['icon_source'] ?? 'icons'; $current_source = sanitize_text_field($current_source); // Get all registered icon sources $all_sources = ['icons', 'forms', 'dash']; // You could get this dynamically if needed $icons = IconsManager::for($current_source); $versions = $icons->getVersionHistory(); $nonce = wp_create_nonce('wp_rest'); ?>

    Icon Management

    Version History for

    $version): ?>
    Date/Time Icon Count File Size Actions
    No version history available
    icons

    Security check failed. Please try again.

    '; }); return; } $page_slug = sanitize_text_field($_GET['page']); // Allow other classes to handle their page submissions $result = apply_filters('jvb_admin_page_submission', null, $page_slug, $_POST); // Store result in transient for after redirect if (is_array($result)) { set_transient(BASE . 'admin_notice_' . get_current_user_id(), $result, 30); } // Redirect to prevent form resubmission (POST-Redirect-GET pattern) wp_safe_redirect(add_query_arg(['page' => $page_slug], admin_url('admin.php'))); exit; } /** * Display admin notices from form submissions */ public function displayAdminNotices(): void { $notice = get_transient(BASE . 'admin_notice_' . get_current_user_id()); if ($notice && is_array($notice)) { delete_transient(BASE . 'admin_notice_' . get_current_user_id()); $type = $notice['success'] ? 'success' : 'error'; $message = $notice['message'] ?? 'Settings saved.'; echo '

    ' . esc_html($message) . '

    '; } } }