From ba1e1ccf869b818f7a7a897264dfea05563a7796 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 07 Jun 2026 20:10:20 +0000
Subject: [PATCH] =Major overhaul of Integrations. Playing around with adding fields to post types through Registrar from an integrations' class file.

---
 inc/managers/AdminPages.php |  486 +++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 405 insertions(+), 81 deletions(-)

diff --git a/inc/managers/AdminPages.php b/inc/managers/AdminPages.php
index d0a1f8c..5878e17 100644
--- a/inc/managers/AdminPages.php
+++ b/inc/managers/AdminPages.php
@@ -1,7 +1,9 @@
 <?php
 namespace JVBase\managers;
 
-use JVBase\utility\Features;
+use JVBase\registrar\Registrar;
+use JVBase\base\Site;
+use WP_REST_Response;
 
 if (!defined('ABSPATH')) {
     exit; // Exit if accessed directly
@@ -30,10 +32,10 @@
             'menu_title' => 'JakeVan',
             'capability' => 'manage_options',
             'menu_slug' => BASE . 'settings',
-            'icon' => jvbCSSIcon('settings'),
+            'icon' => jvbCSSIcon('gear-six'),
             'position' => 0
         ];
-		$this->subpages = apply_filters('jvbAdminSubpages', []);
+		$this->subpages = get_option(BASE.'adminSubpage', []);
 //        delete_option(BASE.'admin_actions');
 //        delete_option(BASE.'admin_subpages');
 //        $this->getSubpages();
@@ -43,6 +45,12 @@
         // 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);
+
+		// Handle form submissions
+		add_action('admin_init', [$this, 'handleAdminPageSubmission']);
+		add_action('admin_notices', [$this, 'displayAdminNotices']);
     }
 
     /**
@@ -179,6 +187,14 @@
             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
@@ -202,6 +218,11 @@
         }
     }
 
+	public function getMainConfig():array
+	{
+		return $this->main_page;
+	}
+
     /**
      * Render the main settings page
      */
@@ -270,7 +291,7 @@
     protected function renderStatusItems():void
     {
         // Get queue stats
-        $queue_status = JVB()->queue()->getQueueStatus();
+        $queue_status = JVB()->queue()->getStatus();
 		error_log('Queue Status: '.print_r($queue_status, true));
 
         // Other system checks
@@ -297,11 +318,11 @@
         </li>
         <li>
             <span class="status-label">Content Types:</span>
-            <span class="status-value"><?= count(JVB_CONTENT); ?> registered</span>
+            <span class="status-value"><?= count(Registrar::getRegistered('post')); ?> registered</span>
         </li>
         <li>
             <span class="status-label">Taxonomies:</span>
-            <span class="status-value"><?= count(JVB_TAXONOMY); ?> registered</span>
+            <span class="status-value"><?= count(Registrar::getRegistered('term')); ?> registered</span>
         </li>
         <?php
     }
@@ -333,10 +354,8 @@
         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'];
-		}
+		$content_types = array_map(function ($type) { return jvbCheckBase($type); },
+			Registrar::getRegistered('post'));
 
         ?>
         <table class="jvb-content-table">
@@ -415,9 +434,9 @@
             if (current_user_can($action['capability'])) {
                 ?>
                 <a data-action="<?=$action['slug']?>" class="jvb-action">
-                    <?= jvbIcon($action['icon']); ?>
+                    <?= jvbDashIcon($action['icon']); ?>
                     <span class="jvb-link-title"><?= esc_html($action['label'])?></span>
-                    <span class="loader"><?=jvbIcon('arrows-clockwise')?><?=jvbIcon('check')?></span>
+                    <span class="loader"><?=jvbDashIcon('arrows-clockwise')?><?=jvbDashIcon('check')?></span>
                 </a>
                 <?php
             }
@@ -429,40 +448,41 @@
      *
      * @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;
-        }
+	public function enqueueAdminAssets(string $hook):void
+	{
+		// More robust check for JVB admin pages
+		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 styles
+		wp_enqueue_style(
+			'jvb-admin-styles',
+			JVB_URL . 'assets/css/admin.css',
+			[],
+			'1.1'
+		);
 
-        // Enqueue admin scripts
-        wp_enqueue_script(
-            'jvb-admin-scripts',
-            JVB_URL . 'assets/js/admin.js',
-            [],
-            '1.0.0',
-            true
-        );
+		// Enqueue admin scripts - make sure jvb-auth is loaded first
+		wp_enqueue_script(
+			'jvb-admin-scripts',
+			JVB_URL . 'assets/js/admin.js',
+			['jvb-auth'],
+			'1.1',
+			['strategy' => 'defer', 'in_footer' => 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'),
-            ]
-        );
-    }
+		// Localize to jvb-admin-scripts as well for redundancy
+		wp_localize_script(
+			'jvb-auth',
+			'jvbSettings',
+			[
+				'api'    => rest_url('jvb/v1/'),
+				'nonce'  => wp_create_nonce('wp_rest'),
+				'action' => wp_create_nonce('itsme'),
+			]
+		);
+	}
 
     /**
      * Create a custom SVG icon for the admin menu
@@ -472,7 +492,7 @@
      */
     protected function getIcon(string $icon = 'logo', bool $css = false): string
     {
-        $svg = jvbIcon($icon, ['wrap' => false]);
+        $svg = jvbDashIcon($icon, ['wrap' => false]);
         if ($css) {
             // For CSS, replace currentColor with brand color
             $svg = str_replace('currentColor', '#FF0080', $svg);
@@ -489,42 +509,346 @@
         return 'data:image/svg+xml;base64,' . base64_encode($svg);
     }
 
-    public function renderCachePage()
-    {
-        $groups = get_option(BASE.'all_cache_groups', []);
+	public function renderCachePage():void
+	{
+		$groups = Cache::getAllGroups();
 
-        ?>
-        <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
-        }
-    }
+		// Separate by type
+		$generic = [];
+		$specific = [];
+
+		foreach ($groups as $group => $data) {
+			if ($this->isBoundToContentOrTaxonomy($group)) {
+				$specific[$group] = $data;
+			} else {
+				$generic[$group] = $data;
+			}
+		}
+
+		?>
+		<div class="wrap jvb-admin-wrap">
+			<h1>Cache Management</h1>
+
+			<div class="jvb-cache-actions">
+				<button type="button"
+						class="button button-primary"
+						data-cache-action="flush-all">
+					<?= jvbDashIcon('arrows-clockwise'); ?>
+					Flush All Caches
+				</button>
+			</div>
+
+			<div class="jvb-cache-section">
+				<h2>Generic Caches &amp; Connections</h2>
+				<table class="wp-list-table widefat fixed striped">
+					<thead>
+					<tr>
+						<th class="manage-column">Cache Group</th>
+						<th class="manage-column">Connected To</th>
+						<th class="manage-column">Actions</th>
+					</tr>
+					</thead>
+					<tbody>
+					<?php if (empty($generic)): ?>
+						<tr><td colspan="3">No generic caches registered</td></tr>
+					<?php else: ?>
+						<?php foreach ($generic as $group => $data): ?>
+							<tr>
+								<td><strong><?= esc_html($group); ?></strong></td>
+								<td><?= $this->formatConnections($data); ?></td>
+								<td>
+									<button type="button"
+											class="button"
+											data-cache-action="flush-cache"
+											data-group="<?= esc_attr($group); ?>">
+										<?= jvbDashIcon('trash'); ?> Flush
+									</button>
+								</td>
+							</tr>
+						<?php endforeach; ?>
+					<?php endif; ?>
+					</tbody>
+				</table>
+			</div>
+
+			<details class="jvb-cache-section">
+				<summary><h2>Content-Specific Caches</h2></summary>
+				<table class="wp-list-table widefat fixed striped">
+					<thead>
+					<tr>
+						<th>Cache Group</th>
+						<th>Connected To</th>
+						<th>Actions</th>
+					</tr>
+					</thead>
+					<tbody>
+					<?php foreach ($specific as $group => $data): ?>
+						<tr>
+							<td><strong><?= esc_html($group); ?></strong></td>
+							<td><?= $this->formatConnections($data); ?></td>
+							<td>
+								<button type="button"
+										class="button"
+										data-cache-action="flush-cache"
+										data-group="<?= esc_attr($group); ?>">
+									<?= jvbDashIcon('trash'); ?> Flush
+								</button>
+							</td>
+						</tr>
+					<?php endforeach; ?>
+					</tbody>
+				</table>
+			</details>
+		</div>
+		<?php
+	}
+
+	protected function isBoundToContentOrTaxonomy(string $group): bool
+	{
+		$group = jvbNoBase($group);
+
+		$registered = Registrar::getRegistered();
+		foreach ($registered as $r) {
+			if ($r === $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[] = '<strong>Invalidates:</strong> ' . implode(', ', $targets);
+		}
+
+		if (!empty($data['connected_from'])) {
+			$sources = array_map(fn($conn) => $conn['group'], $data['connected_from']);
+			$parts[] = '<strong>Invalidated by:</strong> ' . implode(', ', $sources);
+		}
+
+		return $parts ? implode('<br>', $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'];
+
+		$icons = IconsManager::for($current_source);
+		$versions = $icons->getVersionHistory();
+
+		?>
+		<div class="wrap jvb-admin-wrap">
+			<h1>Icon Management</h1>
+
+			<!-- Source Selector -->
+			<div class="jvb-icon-source-selector">
+				<label for="icon-source-select">Icon Source:</label>
+				<select id="icon-source-select"
+						onchange="window.location.href='<?= admin_url('admin.php?page=' . BASE . 'icons&icon_source='); ?>' + this.value">
+					<?php foreach ($all_sources as $source): ?>
+						<option value="<?= esc_attr($source); ?>"
+							<?= selected($current_source, $source, false); ?>>
+							<?= esc_html(ucfirst($source)); ?>
+						</option>
+					<?php endforeach; ?>
+				</select>
+			</div>
+
+			<div class="jvb-icon-actions">
+				<button type="button"
+						class="button button-primary"
+						data-icon-action="refresh-icons"
+						data-source="<?= esc_attr($current_source); ?>">
+					<?= jvbDashIcon('arrows-clockwise'); ?>
+					Force Refresh CSS
+				</button>
+				<button type="button"
+						class="button"
+						data-icon-action="merge-icon-versions"
+						data-source="<?= esc_attr($current_source); ?>"
+						id="merge-versions-btn"
+						disabled>
+					<?= jvbDashIcon('git-merge'); ?>
+					Merge Selected Versions
+				</button>
+			</div>
+
+			<h2>Version History for <?= esc_html(ucfirst($current_source)); ?></h2>
+			<table class="wp-list-table widefat fixed striped">
+				<thead>
+				<tr>
+					<th class="check-column">
+						<input type="checkbox" id="select-all-versions">
+						<label for="select-all-versions" class="screen-reader-text">Select All</label>
+					</th>
+					<th>Date/Time</th>
+					<th>Icon Count</th>
+					<th>File Size</th>
+					<th>Actions</th>
+				</tr>
+				</thead>
+				<tbody>
+				<?php if (empty($versions)): ?>
+					<tr><td colspan="5">No version history available</td></tr>
+				<?php else: ?>
+					<?php foreach (array_reverse($versions) as $index => $version): ?>
+						<tr>
+							<th class="check-column">
+								<input type="checkbox"
+									   name="version-select"
+									   class="version-checkbox"
+									   value="<?= esc_attr($version['timestamp']); ?>">
+							</th>
+							<td><?= esc_html(date('Y-m-d H:i:s', $version['timestamp'])); ?></td>
+							<td>
+								<?= esc_html($version['icon_count']); ?> icons
+								<button type="button"
+										class="button-link view-icon-list-btn"
+										data-timestamp="<?= esc_attr($version['timestamp']); ?>">
+									(view)
+								</button>
+							</td>
+							<td><?= esc_html($version['size_formatted']); ?></td>
+							<td>
+								<button type="button"
+										class="button restore-version-btn"
+										data-icon-action="restore-icon-version"
+										data-source="<?= esc_attr($current_source); ?>"
+										data-timestamp="<?= esc_attr($version['timestamp']); ?>">
+									<?= jvbDashIcon('arrow-counter-clockwise'); ?> Restore
+								</button>
+							</td>
+						</tr>
+						<tr id="icon-list-<?= esc_attr($version['timestamp']); ?>"
+							class="icon-list-row"
+							style="display: none;">
+							<td colspan="5">
+								<div class="icon-list-content">
+									<?php foreach ($version['iconList'] as $style => $icons): ?>
+										<strong><?= esc_html(ucfirst($style)); ?>:</strong>
+										<?= esc_html(implode(', ', $icons)); ?><br>
+									<?php endforeach; ?>
+								</div>
+							</td>
+						</tr>
+					<?php endforeach; ?>
+				<?php endif; ?>
+				</tbody>
+			</table>
+		</div>
+		<?php
+	}
+
+	public static function addSubpage(string $key, array $value):void
+	{
+		$option = get_option(BASE.'adminSubpage', []);
+		if (empty($option) || !array_key_exists($key, $option)) {
+			$option[$key] = $value;
+			update_option(BASE.'adminSubpage', $option);
+		}
+	}
+
+	/**
+	 * Handle admin page form submissions
+	 * Fires after WordPress admin_init
+	 */
+	public function handleAdminPageSubmission(): void
+	{
+		// Only process on our admin pages
+		if (!isset($_GET['page']) || strpos($_GET['page'], BASE) !== 0) {
+			return;
+		}
+
+		// Check for form submission
+		if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['submit'])) {
+			return;
+		}
+
+		// Verify nonce
+		$nonce_field = BASE . 'admin_page_nonce';
+		if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], $nonce_field)) {
+			add_action('admin_notices', function() {
+				echo '<div class="notice notice-error"><p>Security check failed. Please try again.</p></div>';
+			});
+			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 '<div class="notice notice-' . esc_attr($type) . ' is-dismissible"><p>' . esc_html($message) . '</p></div>';
+		}
+	}
 }

--
Gitblit v1.10.0