From 3baf3d2545ba6ece6b74a64c0def59bd0774cf54 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 10 Jun 2026 16:34:12 +0000
Subject: [PATCH] =Laid the groundwork for an improved DashboardManager.php setup. Have to put it aside so I can get the dang Northeh done though.
---
JVBase.php | 10
inc/managers/Dashboard/Section.php | 55 +++
inc/managers/Dashboard/DashboardManager.php | 659 +++++++++++++++++++++++++++++++++++++++++
inc/managers/DashboardManager.php | 11
inc/managers/RoleManager.php | 16
inc/integrations/Integrations.php | 16
inc/managers/Dashboard/DashboardPage.php | 159 +++++++++
inc/managers/_setup.php | 8
jvb.php | 2
base/options.php | 5
inc/managers/Dashboard/_setup.php | 8
11 files changed, 926 insertions(+), 23 deletions(-)
diff --git a/JVBase.php b/JVBase.php
index bf0db8a..ea4f09c 100644
--- a/JVBase.php
+++ b/JVBase.php
@@ -11,7 +11,7 @@
use JVBase\managers\LoginManager;
use JVBase\managers\MagicLinkManager;
use JVBase\managers\queue\Queue;
-use JVBase\managers\DashboardManager;
+use JVBase\managers\Dashboard\DashboardManager;
use JVBase\managers\DirectoryManager;
use JVBase\managers\ReferralManager;
use JVBase\managers\RoleManager;
@@ -132,9 +132,6 @@
$this->routes['referral'] = new ReferralRoutes();
}
- if (Site::has('dashboard')) {
- $this->managers['dash'] = new DashboardManager();
- }
if (Site::hasIntegration('square')) {
$this->routes['square'] = new IntegrationsSquareRoutes();
@@ -198,6 +195,11 @@
$this->routes['invites'] = new Invitations();
}
+
+ if (Site::has('dashboard')) {
+ $this->managers['dash'] = new DashboardManager();
+ }
+
$this->setupIntegrations();
add_action('wp_footer', [$this, 'additionalActions']);
diff --git a/base/options.php b/base/options.php
index 50f0979..7f7cc3f 100644
--- a/base/options.php
+++ b/base/options.php
@@ -60,8 +60,9 @@
$this->fields->addField(
'open_to_public',
[
- 'type' => 'true_false',
- 'label' => 'Open to Public?'
+ 'type' => 'true_false',
+ 'label' => 'Open to Public?',
+ 'default' => 1,
]
);
diff --git a/inc/integrations/Integrations.php b/inc/integrations/Integrations.php
index ef1a135..cb08b13 100644
--- a/inc/integrations/Integrations.php
+++ b/inc/integrations/Integrations.php
@@ -66,7 +66,7 @@
* Used for UI rendering in admin interfaces
*/
public string $title; // Human-readable service name (e.g., 'Google My Business')
- public string $icon; // Phosphoricons icon slug
+ public string $icon = ''; // Phosphoricons icon slug
/**
* Credentials & State
@@ -2714,6 +2714,15 @@
return $this->title;
}
+ public static function title():string
+ {
+ return (new static())->getTitle();
+ }
+ public static function icon():string
+ {
+ return (new static())->getIcon();
+ }
+
/*********************************************************************
RENDERING
*********************************************************************/
@@ -3535,4 +3544,9 @@
{
return [];
}
+
+ public function getIcon():string
+ {
+ return $this->icon;
+ }
}
diff --git a/inc/managers/Dashboard/DashboardManager.php b/inc/managers/Dashboard/DashboardManager.php
new file mode 100644
index 0000000..2c4ae50
--- /dev/null
+++ b/inc/managers/Dashboard/DashboardManager.php
@@ -0,0 +1,659 @@
+<?php
+namespace JVBase\managers\Dashboard;
+
+use JetBrains\PhpStorm\NoReturn;
+use JVBase\forms\TaxonomySelector;
+use JVBase\base\Site;
+use JVBase\managers\Cache;
+use JVBase\managers\RoleManager;use JVBase\registrar\Registrar;
+use JVBase\ui\Navigation;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+class DashboardManager {
+ protected array $pages = [];
+ protected array $sections = [];
+ protected Cache $cache;
+ public function __construct() {
+ $this->cache = Cache::for('dashboard')->connect('post');
+ if (JVB_TESTING) {
+ $this->cache->flush();
+ }
+ $this->buildPages();
+ $this->buildSections();
+ //Since this is loaded via JVBase, it's already running on init
+ $this->registerDashboard();
+ $this->registerHooks();
+ }
+ protected function registerDashboard():void
+ {
+ $dash = Registrar::forPost('dash', 'Dashboard', 'Dashboards');
+ $dash->setIcon('gauge')
+ ->make([
+ 'show_in_admin_bar' => false,
+ 'rewrite' => [
+ 'slug' => 'dash',
+ 'with_front' => false,
+ ],
+ 'supports' => [ 'title', 'editor', 'custom-fields'],
+ 'hierarchical' => true
+ ])->setAll([
+ 'system'
+ ]);
+ }
+
+ protected function registerHooks():void
+ {
+
+ add_action('template_redirect', [$this, 'handleRedirects']);
+ add_action('template_include', [$this, 'dashboardTemplates']);
+ add_action('admin_init', [$this, 'redirectFromAdmin']);
+ add_action('wp_enqueue_scripts', [$this, 'dashboardScripts'], 50);
+ add_filter('the_seo_framework_sitemap_exclude_ids', [$this, 'excludeDashboard'], 8, 1);
+ }
+ public function handleRedirects():void
+ {
+ if (!is_singular(BASE.'dash') && !is_post_type_archive(BASE.'dash') && !is_404()) {
+ return;
+ }
+
+
+ if (!is_404()) {
+ if (!is_user_logged_in()) {
+ error_log('Redirecting to login - user not logged in');
+ $this->redirectToLogin();
+ } elseif (!isOurPeople() && !current_user_can('manage_options')) {
+ $this->redirectToHome();
+ } else {
+ $page = $this->getCurrentPageSlug();
+ if (array_key_exists($page, ['', 'dash'])) {
+ return;
+ }
+ if (!array_key_exists($page, $this->pages)) {
+ error_log('[DashboardManager]::handleRedirect could not find page for '.$page);
+ return;
+ }
+ $permission = $this->pages[$page]->getPermission();
+ if (!empty($permission) && !current_user_can($permission)) {
+ error_log('[DashboardManager]::handleRedirect User cannot manage '.$page);
+ $this->redirectToDashboard();
+ }
+ }
+ }
+ global $wp;
+ if (str_starts_with($wp->request, 'dash/') || $wp->request === 'dash') {
+ error_log('404 on dashboard URL, redirecting to dashboard home');
+ $this->redirectToDashboard();
+ }
+ }
+ public function dashboardTemplates(string $template):string
+ {
+ if (!is_singular(BASE.'dash') && !is_post_type_archive(BASE.'dash')) {
+ return $template;
+ }
+ $page = $this->getCurrentPage();
+ if (!empty($page->getIcon())) {
+ add_filter('jvbLoadingIcon', $page->getIcon());
+ }
+ ob_start();
+ jvbInlineStyles('nav');
+ jvbInlineStyles('dash');
+ jvbInlineStyles('forms');
+ $this->renderHeader();
+ $page->render();
+ $this->renderFooter();
+ return ob_get_clean();
+ }
+ protected function renderHeader():void
+ {
+ $page = $this->getCurrentPage();
+ ?>
+ <!DOCTYPE html>
+ <html <?php language_attributes(); ?>>
+ <head>
+ <title><?= $page->getTitle() . ' | Dashboard' ?></title>
+ <meta charset="<?php bloginfo('charset'); ?>">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <?php
+ foreach ($this->pages as $page) {
+ if (empty($page->getPermission()) || current_user_can($page->getPermission())) {
+ echo '<link rel="preconnect" href="'.$page->getURL().'">';
+ }
+ }
+ ?>
+ <link rel="preconnect" href="<?= get_home_url()?>"/>
+ <?php wp_head(); ?>
+ </head>
+ <body class="dashboard<?= ' '.$this->getCurrentPageSlug()?>">
+ <?php jvbAccessibility();?>
+ <header>
+ <?= jvbDarkModeToggle() ?>
+ <?php
+ $function = BASE.'render_core_site_logo';
+ if (function_exists($function)) {
+ echo $function([],'');
+ } else {
+ echo render_block( [
+ 'blockName' => 'core/site-logo',
+ 'attrs' => [],
+ ]);
+ }
+ ?>
+
+ <nav>
+ <ul>
+ <?= jvbNotificationMenu() ?>
+ <?= jvbHelpMenu() ?>
+ <li><a href="<?=wp_logout_url(get_home_url())?>" title="Logout"><?=jvbIcon('sign-out')?></a></li>
+ </ul>
+ </nav>
+ </header>
+
+ <main><section class="replace">
+ <?php
+ }
+
+ protected function renderFooter():void
+ {
+ ?>
+ </section>
+
+ <?= $this->outputSidebarNavigation(); ?>
+
+ <footer class="col">
+ <?= jvbLoadingScreen() ?>
+ <?= TaxonomySelector::outputSelectorModal() ?>
+ <!-- <nav class="dashboard-nav">-->
+ <?php
+ // $current_page = $this->getCurrentPageSlug();
+ // $pages = $this->getUserAllowedPages()?:[];
+ // echo '<ul>';
+ // foreach ($pages as $slug => $page) {
+ // $slug = $this->getSlug($slug, $page);
+ // $icon = $this->getIcon($slug, $page);
+ // // Add data-page attribute for the navigator
+ // $active = ($current_page == $slug) ? ' class="current"' : '';
+ // $current = ($current_page == $slug) ? ' aria-current="page"' : '';
+ //
+ //
+ // $link = ($page === 'dash') ? '/'.$page : "/dash/$slug";
+ // printf(
+ // '<li%s><a href="%s"%s data-page="%s" data-dash title="%s">%s<span>%s</span></a></li>',
+ // $active,
+ // get_home_url(null, $link),
+ // $current,
+ // $slug,
+ // $page,
+ // jvbDashIcon($icon, ['title'=> $page]),
+ // $page
+ // );
+ // }
+ //
+ // echo '</ul>';
+ ?>
+ <!-- </nav>-->
+ </footer>
+
+
+ <?php
+ do_action('jvbRenderDashboardSettings', $this->getCurrentPageSlug());
+ ?>
+ <?php wp_footer(); ?>
+
+ </body>
+ </html>
+
+ <?php
+ }
+
+ protected function outputSidebarNavigation():string
+ {
+ $menu = new Navigation('sidebar');
+ $menuClasses = ['left'];
+ $itemClasses = ['col'];
+ $menu->addClass('sidebar left')->hasToggle()->defaultMenuClasses($menuClasses);
+ $menu->defaultItemClasses($itemClasses);
+
+ //Add the Main Dashboard first
+ $dashboard = $menu->addItem('Dashboard', jvbDashIcon($this->pages['dash']->getIcon()))
+ ->url($this->pages['dash']->getURL());
+
+ foreach ($this->sections as $section) {
+ $pages = array_filter($this->pages, function($page) use ($section) {
+ $canDo = empty($page->getPermission()) || current_user_can($page->getPermission());
+ return $page->getSection() === $section && $canDo;
+ });
+
+ if (empty($pages)) {
+ continue;
+ }
+ $icon = !empty($section['icon']??'') ? jvbDashIcon($section['icon']) : null;
+ $section = $menu->addItem($section['title'], $icon)
+ ->submenu($section['slug'])
+ ->defaultMenuClasses($menuClasses)
+ ->defaultItemClasses($itemClasses);
+ foreach ($pages as $page) {
+ $icon = empty($page->getIcon()) ? null : jvbDashIcon($page->getIcon());
+ $item = $section->addItem($page->getTitle(), $icon)
+ ->url($page->getURL());
+ $registrar = Registrar::getInstance($page->getSlug());
+ if ($registrar && !empty($registrar->registrar->taxonomies)) {
+ $itemMenu = $item->submenu($page->getSlug());
+ foreach ($registrar->registrar->taxonomies as $taxonomy) {
+ $taxonomy = jvbNoBase($taxonomy);
+ if (!array_key_exists($taxonomy, $this->pages)) {
+ error_log('Could not add Taxonomy subpage for '.$taxonomy);
+ continue;
+ }
+ $icon = empty($this->pages[$taxonomy]->getIcon()) ? null : jvbDashIcon($this->pages[$taxonomy]->getIcon());
+ $itemMenu->addItem($this->pages[$taxonomy]->getTitle(),$icon)
+ ->url($this->pages[$taxonomy]->getURL());
+ }
+ }
+ }
+
+ }
+ $pages = $this->getUserAllowedPages()?:[];
+ //Dashboard
+ //Referrals
+ $dashboard = $menu->addItem('Dashboard',jvbDashIcon('door'))
+ ->url($this->baseURL);
+ // ->submenu('dashboard')
+ // ->defaultMenuClasses($menuClasses)
+ // ->defaultItemClasses($itemClasses);
+ //notifications
+ if (in_array('Notifications', $pages)) {
+ $menu->addItem('Notifications',jvbDashIcon('bell'))
+ ->url($this->baseURL.'/notifications');
+ }
+ if (in_array('Referrals', $pages)) {
+ $menu->addItem('Referrals', jvbDashIcon('hand-heart'))
+ ->url($this->baseURL.'/referrals');
+ }
+ if (in_array('Favourites', $pages)) {
+ $menu->addItem('Favourites', jvbDashIcon('heart'))
+ ->url($this->baseURL.'/favourites');
+ }
+
+ //Content
+ //content types
+ $all = array_merge(
+ Registrar::getRegistered('post'),
+ Registrar::withFeature('is_content', 'term')
+ );
+ $availableContent = [];
+// $availableContent = array_filter($pages, function($page, $key) use($all) {
+// return !is_numeric($key) && in_array($key, $all) && JVB()->roles()->checkRole($this->user, $key);
+// }, ARRAY_FILTER_USE_BOTH);
+ if (!empty ($availableContent)){
+ $content = $menu->addItem('Your Content', jvbDashIcon('book-bookmark'))
+ ->submenu('content')
+ ->defaultMenuClasses($menuClasses)
+ ->defaultItemClasses($itemClasses);
+ foreach ($availableContent as $slug => $page) {
+ $registrar = Registrar::getInstance($slug);
+
+ $item = $content->addItem($page, $registrar->getIcon())
+ ->url($this->baseURL.'/'.$slug);
+
+ if ($registrar->getType() === 'post') {
+ $taxonomies = $registrar->registrar->taxonomies;
+ if (!empty ($taxonomies)) {
+ //TODO: If we add a dedicated 'create item' page, remove this from the empty check
+ $itemMenu = $item->submenu($slug);
+ foreach ($taxonomies as $s) {
+ $taxRegistrar = Registrar::getInstance($s);
+ if ($taxRegistrar) {
+ $itemMenu->addItem($taxRegistrar->getPlural(), $taxRegistrar->getIcon())
+ ->url($this->baseURL.'/'.$s);
+ }
+
+ }
+ }
+ }
+
+
+ }
+ }
+
+ //Taxonomies
+
+ //Settings
+ $settings = $menu->addItem('Settings', jvbDashIcon('faders'))
+ ->submenu('settings')
+ ->defaultItemClasses($itemClasses)
+ ->defaultMenuClasses($menuClasses);
+
+ //SEO
+ if (in_array('SEO', $pages)) {
+ $settings->addItem('SEO', jvbDashIcon('robot'))
+ ->url($this->baseURL.'/seo');
+ }
+ //Integrations
+ if (in_array('Integrations', $pages)) {
+ $settings->addItem('Integrations', jvbDashIcon('plugs-connected'))
+ ->url($this->baseURL.'/integrations');
+ }
+ //Account
+ $account = $menu->addItem('Account', jvbDashIcon('user-circle'))
+ ->url($this->baseURL.'/account')
+ ->submenu('account')
+ ->defaultMenuClasses($menuClasses)
+ ->defaultItemClasses($itemClasses);
+ $account->addItem('Reset Password', jvbDashIcon('password'))
+ ->url($this->baseURL.'/reset-password');
+ //name + contact
+ //reset password
+
+ if (in_array('notifications', $pages)) {
+ $account->addItem('Permissions', jvbDashIcon('keyhole'))
+ ->url($this->baseURL.'/permissions');
+ }
+
+ echo $menu->render();
+ }
+ public function redirectFromAdmin():void
+ {
+ //Skip if already processing a redirect
+ if (defined('DOING_AJAX') && DOING_AJAX) {
+ return;
+ }
+
+ if (current_user_can('manage_options')) {
+ return;
+ }
+ //Redirect to custom dashboard
+ if (is_user_logged_in() && isOurPeople()) {
+ $this->redirectToDashboard();
+ }
+ }
+ public function dashboardScripts():void
+ {
+
+ }
+
+ public function excludeDashboard(array $IDs):array {
+ $exclude = $this->cache->remember(
+ 'dashboardIDs',
+ function() {
+ return get_posts([
+ 'post_type' => BASE.'dash',
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ ]);
+ });
+ if (!empty($exclude)) {
+ $IDs = array_merge($IDs, $exclude);
+ }
+
+ return $IDs;
+ }
+
+ private function buildPages():void
+ {
+ $this->addPage('dash', 'Dashboard','door');
+ $this->buildContentPages();
+ $this->buildReferrals();
+ $this->buildMembership();
+ $this->buildFavourites();
+ $this->buildKarma();
+ $this->buildNotifications();
+ $this->buildIntegrations();
+ $this->buildSettingsPages();
+ $this->buildAccountPages();
+ }
+
+ private function buildContentPages():void
+ {
+ $content = Registrar::getRegistered('post');
+ foreach ($content as $c) {
+ $registrar = Registrar::getInstance($c);
+ $page = $this->addPage($registrar->getPlural(), $c, $registrar->getIcon());
+ $page->setPermission(RoleManager::getPermissionName('edit', $c));
+ $page->setSection('content');
+ $this->pages[$c] = $page;
+ }
+ }
+
+ private function buildSettingsPages():void
+ {
+ $seo = $this->addPage('SEO', 'seo', 'robot');
+ $seo->setSection('settings');
+ $seo->setPermission('manage_options');
+ $this->pages['seo'] = $seo;
+
+ }
+ private function buildAccountPages():void
+ {
+ if (Site::has('support')) {
+ $page = $this->addPage('Support', 'support', 'question');
+ $page->setSection('support');
+ }
+
+ $account = $this->addPage('Account', 'account', 'user-circle');
+ $account->setSection('account');
+ $accountID = $account->getID();
+
+ $password = $this->addPage('Reset Password', 'reset-password', 'password', $accountID);
+ $password->setOrder(2);
+ $password->setSection('account');
+
+ }
+ private function buildReferrals():void
+ {
+ if (Site::has('referrals')) {
+ $page = $this->addPage('Referrals', 'referrals', 'hand-heart');
+ }
+ }
+ private function buildMembership():void
+ {
+ $membership = Site::membership();
+ if ($membership) {
+ if ($membership->has('can_invite')) {
+ $page = $this->addPage('Invite', 'invite', '');
+ $page->setSection('notifications');
+ }
+ if ($membership->has('term_approval')) {
+ $page = $this->addPage('Approvals', 'approvals', 'check-circle');
+ $page->setSection('notifications');
+ }
+ if ($membership->has('forum')) {
+ $page = $this->addPage('Forum', 'forum', 'chats-teardrop');
+ }
+ if ($membership->has('member_content')) {
+ $page = $this->addPage('Metrics', 'metrics', 'chart-line');
+ }
+ }
+ }
+ private function buildFavourites():void
+ {
+ if (Site::has('favourites')) {
+ $page = $this->addPage('Favourites', 'favourites', 'heart');
+ //TODO: Lists, Share permissions
+ }
+ }
+ private function buildKarma():void
+ {
+ if (!empty(Registrar::withFeature('karma'))) {
+ $page = $this->addPage('Karmic', 'karmic', 'arrow-fat-up');
+ }
+ }
+ private function buildNotifications():void
+ {
+ if (Site::has('notifications')) {
+ $page = $this->addPage('Notifications', 'notifications', 'bell');
+ $page->setSection('notifications');
+
+ $page = $this->addPage('Permissions', 'permissions', 'gear-six');
+ $page->setSection('notifications');
+ $page->setOrder(999);
+ }
+ }
+ private function buildIntegrations():void
+ {
+ if (Site::hasAnyIntegration()) {
+ $page = $this->addPage('Integrations', 'integrations', 'plugs-connected');
+ $page->setSection('settings');
+ $parent = $page->getID();
+ foreach (array_keys(Site::getIntegrations()) as $integration) {
+ $integration = match($integration) {
+ 'maps' => 'JVBase\integrations\GoogleMaps',
+ 'square' => 'JVBase\integrations\Square',
+ 'facebook' => 'JVBase\integrations\Facebook',
+ 'helcim' => 'JVBase\integrations\Helcim',
+ 'instagram' => 'JVBase\integrations\Instagram',
+ 'bluesky' => 'JVBase\integrations\BlueSky',
+ 'gmb' => 'JVBase\integrations\GoogleMyBusiness',
+ 'cloudflare' => 'JVBase\integrations\Cloudflare',
+ 'umami' => 'JVBase\integrations\Umami',
+ 'postmark' => 'JVBase\integrations\PostMark',
+ };
+ $title = $integration::title();
+ $icon = $integration::icon();
+ $page = $this->addPage($title, $title, $icon, $parent);
+ }
+ }
+ }
+
+ public function addPage(string $title, string $slug = '', string $icon = '', int $parent = 0):DashboardPage
+ {
+ $page = new DashboardPage($title, $slug, $icon, $parent);
+ $this->pages[$page->getSlug()] = $page;
+ return $page;
+ }
+
+ protected function buildSections():void
+ {
+ $sections = array_values(array_filter(array_unique(array_map(function ($page) {
+ return $page->getSection();
+ }, $this->pages))));
+ foreach ($sections as $section) {
+ $isLink = false;
+ switch ($section) {
+ case 'content':
+ $title = 'Your Content';
+ $icon = 'book-bookmark';
+ break;
+ case 'settings':
+ $title = 'Settings';
+ $icon = 'faders';
+ break;
+ case 'account':
+ $title = 'Account';
+ $icon = 'user-circle';
+ break;
+ case 'notifications':
+ $title = 'Notifications';
+ $icon = 'bell';
+ break;
+ case 'support':
+ $title = 'Support';
+ $icon = 'question';
+ break;
+ default:
+ $mainPage = $this->getMainPage($section);
+ if ($mainPage) {
+ $title = $mainPage->getTitle();
+ $icon = $mainPage->getIcon();
+ $isLink = true;
+ } else {
+ error_log('[DashboardManager]::buildSections Could not create section for '.$section);
+ return;
+ }
+ break;
+ }
+
+ if (!$isLink && $this->hasMainPage($section)) {
+ $isLink = true;
+ }
+
+ $this->sections[$section] = new Section($title, $section, $icon);
+ if ($isLink) {
+ $this->sections[$section]->setIsLink(true);
+ }
+ }
+ }
+ protected function getSectionPages(string $section):array
+ {
+ return array_filter($this->pages, function($page) use ($section) {
+ return $page->getSection() === $section;
+ });
+ }
+
+ protected function getMainPage(string $section):DashboardPage|false
+ {
+ $sectionPages = $this->getSectionPages($section);
+ $mainPage = array_filter($sectionPages, function ($page) use ($section) {
+ return $page->getSlug() === $section;
+ });
+
+ return empty($mainPage) ? false : $mainPage[0];
+ }
+
+ protected function hasMainPage(string $section):bool
+ {
+ return $this->getMainPage($section) !== false;
+ }
+
+ public function addSection(string $title, ?string $slug = null, string $icon = '', ?string $parent = null):void
+ {
+ $section = new Section($title, $slug, $icon, $parent);
+ $this->sections[$section->getSlug()] = $section;
+ }
+ public function addPageToSection(string $slug, ?string $section = null):bool
+ {
+ if (!array_key_exists($slug, $this->pages)) {
+ error_log('[DashboardManager]::addPageToSection Attempted to add page to section that doesn\'t exist: '.$slug);
+ return false;
+ }
+ if (!is_null($section) && !array_key_exists($section, $this->sections)) {
+ error_log('[DashboardManager]::addPageToSection Please configure section first for '.$section);
+ return false;
+ }
+ $this->pages[$slug]->setSection($section);
+ return true;
+ }
+
+
+ #[NoReturn]protected function redirectToLogin():void
+ {
+ wp_redirect(wp_login_url(get_home_url(null, '/dash')));
+ exit;
+ }
+
+ #[NoReturn]protected function redirectToDashboard():void
+ {
+ wp_redirect(get_home_url(null, '/dash'));
+ exit;
+ }
+ #[NoReturn]protected function redirectToHome():void
+ {
+ wp_redirect(get_home_url());
+ exit;
+ }
+
+ protected function getCurrentPage():DashboardPage|false
+ {
+ $slug = $this->getCurrentPageSlug();
+ if (!array_key_exists($slug, $this->pages)) {
+ error_log('[DashboardManager]::getCurrentPage Could not get configuration for '.$slug);
+ return false;
+ }
+ return $this->pages[$slug];
+ }
+
+ protected function getCurrentPageSlug():string
+ {
+ if (is_post_type_archive(BASE.'dash')) {
+ return 'dash';
+ }
+
+ global $post;
+ if (!$post) {
+ return '';
+ }
+
+ return $post->post_name;
+ }
+}
diff --git a/inc/managers/Dashboard/DashboardPage.php b/inc/managers/Dashboard/DashboardPage.php
new file mode 100644
index 0000000..653c86d
--- /dev/null
+++ b/inc/managers/Dashboard/DashboardPage.php
@@ -0,0 +1,159 @@
+<?php
+namespace JVBase\managers\Dashboard;
+
+use JVBase\managers\Cache;
+use WP_Query;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+class DashboardPage {
+ protected string $title;
+ protected string $slug;
+ protected string $icon;
+ protected string $URL;
+ protected int $ID;
+ protected ?string $permission = null;
+ protected ?string $section = null;
+ protected array $scripts = [];
+ protected int $order = 0;
+ protected Cache $cache;
+
+ public function __construct(string $title, string $slug = '', string $icon ='', int $parent = 0) {
+ $this->cache = Cache::for('dashboard');
+ $this->title = $title;
+ if (empty($slug)) {
+ $this->setSlug($title);
+ }else {
+ $this->setSlug($slug);
+ }
+ $this->icon = $icon;
+ $this->setID($parent);
+ $this->setURL();
+ }
+
+ public function setID(int $parentID):void
+ {
+ $this->ID = $this->cache->remember(
+ $this->slug.'_ID',
+ function() use ($parentID) {
+ $existing = new WP_Query([
+ 'post_type' => BASE.'dash',
+ 'name' => $this->slug,
+ 'fields' => 'ids',
+ 'posts_per_page'=> 1,
+ ]);
+ if ($existing->have_posts()) {
+ return $existing->posts[0];
+ }
+
+ $args = [
+ 'post_title' => $this->title,
+ 'post_name' => $this->slug,
+ 'post_type' => BASE.'dash',
+ 'post_status' => 'publish'
+ ];
+ if ($parentID > 0) {
+ $args['post_parent'] = $parentID;
+ }
+ return wp_insert_post($args);
+ }
+ );
+ }
+ public function getID():int
+ {
+ return $this->ID;
+ }
+
+ public function setURL():void
+ {
+ $this->URL = $this->cache->remember(
+ $this->slug.'_url',
+ function () {
+ return get_permalink($this->ID);
+ }
+ );
+ }
+
+ public function getURL():string
+ {
+ return $this->URL;
+ }
+
+
+ public function setTitle(string $title):void
+ {
+ $this->title = $title;
+ }
+ public function getTitle():string
+ {
+ return $this->title;
+ }
+
+ public function setSlug(string $slug):void
+ {
+ $this->slug = self::sanitizeString($slug);
+ }
+ public function getSlug():string
+ {
+ return $this->slug;
+ }
+
+ public function setIcon(string $icon):void
+ {
+ $this->icon = $icon;
+ }
+ public function getIcon():string
+ {
+ return $this->icon;
+ }
+
+ public function setPermission(string $permission):void
+ {
+ $this->permission = $permission;
+ }
+ public function getPermission():?string
+ {
+ return $this->permission;
+ }
+
+ public function setSection(string $section):void
+ {
+ $this->section = self::sanitizeString($section);
+ }
+ public function getSection():?string
+ {
+ return $this->section;
+ }
+
+ public function setOrder(int $order):void
+ {
+ $this->order = $order;
+ }
+ public function getOrder():int
+ {
+ return $this->order;
+ }
+
+ public function setRenderCallback(string $callback):void
+ {
+
+ }
+ public function render():string
+ {
+ return $this->cache->remember(
+ $this->ID,
+ function () {
+ return apply_filters(BASE.'render_'.$this->slug, '<h2>'.$this->title.' is not configured yet.</h2><p>Add a filter to '.BASE.'render_'.$this->slug.'</p>');
+ }
+ );
+ }
+ /*********************************************************
+ * UTILITY
+ ********************************************************/
+ public static function sanitizeString(string $string):string
+ {
+ return str_replace('_', '-', strtolower(sanitize_title($string)));
+ }
+}
diff --git a/inc/managers/Dashboard/Section.php b/inc/managers/Dashboard/Section.php
new file mode 100644
index 0000000..4df5287
--- /dev/null
+++ b/inc/managers/Dashboard/Section.php
@@ -0,0 +1,55 @@
+<?php
+namespace JVBase\managers\Dashboard;
+
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+class Section {
+ protected ?string $parent = null;
+ protected string $title;
+ protected string $slug;
+ protected string $icon = '';
+ protected int $order = 0;
+ protected bool $isLink = false;
+
+ public function __construct(string $title, ?string $slug = null, string $icon = '', ?string $parent = null) {
+ $this->title = $title;
+ $this->slug = is_null($slug) ? DashboardPage::sanitizeString($title) : DashboardPage::sanitizeString($slug);
+ $this->icon = $icon;
+ $this->parent = $parent;
+ }
+
+ public function getTitle():string
+ {
+ return $this->title;
+ }
+ public function getSlug():string
+ {
+ return $this->slug;
+ }
+ public function getIcon():string
+ {
+ return $this->icon;
+ }
+ public function getParent():?string
+ {
+ return $this->parent;
+ }
+ public function setOrder(int $order):void
+ {
+ $this->order = $order;
+ }
+ public function getOrder():int
+ {
+ return $this->order;
+ }
+ public function setIsLink(bool $isLink):void
+ {
+ $this->isLink = $isLink;
+ }
+ public function getIsLink():bool
+ {
+ return $this->isLink;
+ }
+}
diff --git a/inc/managers/Dashboard/_setup.php b/inc/managers/Dashboard/_setup.php
new file mode 100644
index 0000000..ebf6ef0
--- /dev/null
+++ b/inc/managers/Dashboard/_setup.php
@@ -0,0 +1,8 @@
+<?php
+if (!defined('ABSPATH')) {
+ exit;
+}
+
+require_once JVB_DIR . '/inc/managers/Dashboard/DashboardManager.php';
+require_once JVB_DIR . '/inc/managers/Dashboard/DashboardPage.php';
+require_once JVB_DIR . '/inc/managers/Dashboard/Section.php';
diff --git a/inc/managers/DashboardManager.php b/inc/managers/DashboardManager.php
index bb671e8..152afc6 100644
--- a/inc/managers/DashboardManager.php
+++ b/inc/managers/DashboardManager.php
@@ -1,7 +1,7 @@
<?php
namespace JVBase\managers;
-use JVBase\forms\TaxonomySelector;
+use JetBrains\PhpStorm\NoReturn;use JVBase\forms\TaxonomySelector;
use JVBase\base\Site;
use JVBase\meta\Form;
use JVBase\registrar\Registrar;
@@ -111,7 +111,7 @@
/**
* Redirect all non-admin users from wp-admin to custom dashboard
*/
- public function redirectFromAdmin()
+ public function redirectFromAdmin():void
{
// Skip if already processing a redirect
if (defined('DOING_AJAX') && DOING_AJAX) {
@@ -133,13 +133,13 @@
}
}
- protected function redirectToLogin():void
+ #[NoReturn]protected function redirectToLogin():void
{
wp_redirect(wp_login_url(get_home_url(null, '/dash')));
exit;
}
- protected function redirectToDashboard():void
+ #[NoReturn]protected function redirectToDashboard():void
{
wp_redirect(get_home_url(null, '/dash'));
exit;
@@ -191,7 +191,6 @@
if (!is_404() && !is_user_logged_in()) {
error_log('Redirecting to login - user not logged in');
$this->redirectToLogin();
- return;
}
// If logged in but doesn't have dashboard access, redirect to home
@@ -206,7 +205,6 @@
if (is_404() && (str_starts_with($wp->request, 'dash/') || $wp->request === 'dash')) {
error_log('404 on dashboard URL, redirecting to dashboard home');
$this->redirectToDashboard();
- return;
}
// For valid dashboard pages, check access permissions
@@ -395,7 +393,6 @@
$this->renderHeader();
// Pass to page handler
$constantSlug = $this->getConstantSlug($page);
-
echo apply_filters(
'jvbDashboardPage',
$this->renderPage($page),
diff --git a/inc/managers/RoleManager.php b/inc/managers/RoleManager.php
index a22b585..e859813 100644
--- a/inc/managers/RoleManager.php
+++ b/inc/managers/RoleManager.php
@@ -441,9 +441,9 @@
$content = jvbNoBase($content);
$registrar = Registrar::getInstance($content);
if ($registrar && $registrar->getPlural()) {
- return str_replace(' ', '_', $registrar->getPlural());
+ return strtolower(str_replace(' ', '_', $registrar->getPlural()));
}
- return str_replace(' ', '_', $content.'s');
+ return strtolower(str_replace(' ', '_', $content.'s'));
}
public static function activate(): void
@@ -821,6 +821,18 @@
}
return null;
}
+ public static function getPermissionName(string $action, string $content, ?int $ID = null):?string
+ {
+ $plural = (new self())->getContentPlural($content);
+ switch ($action) {
+ case 'edit':
+ if ($ID) {
+ return "edit_{$content}";
+ }
+ return "edit_{$plural}";
+ }
+ return null;
+ }
public function maybeSwitchPermissions(int $object_id, array $terms, array $tt_ids, string $taxonomy, bool $append, array $old_tt_ids):void
{
diff --git a/inc/managers/_setup.php b/inc/managers/_setup.php
index d55098a..379605d 100644
--- a/inc/managers/_setup.php
+++ b/inc/managers/_setup.php
@@ -10,11 +10,10 @@
require(JVB_DIR . '/inc/managers/CustomTable.php');
//require(JVB_DIR . '/inc/managers/CacheManager.php');
require(JVB_DIR . '/inc/managers/Cache.php');
-class_alias('JVBase\managers\Cache', 'JVBase\managers\CacheManager');
require(JVB_DIR . '/inc/managers/IconsManager.php');
-add_action('init', 'jvbInit', 1); // Priority 1 - very early
+add_action('init', 'jvbInit', 0); // Priority 1 - very early
function jvbInit(): void
{
@@ -37,6 +36,7 @@
}
if (Site::has('dashboard')) {
+ require(JVB_DIR . '/inc/managers/DashboardManager.php');
require(JVB_DIR . '/inc/managers/CRUDManager.php');
require(JVB_DIR . '/inc/managers/UploadManager.php');
}
@@ -75,10 +75,6 @@
require(JVB_DIR . '/inc/managers/DirectoryManager.php');
}
- if (Site::has('dashboard')) {
- require(JVB_DIR . '/inc/managers/DashboardManager.php');
- }
-
if (Site::has('referrals')) {
require(JVB_DIR . '/inc/managers/ReferralManager.php');
}
diff --git a/jvb.php b/jvb.php
index 6cc5ff8..2e188ad 100644
--- a/jvb.php
+++ b/jvb.php
@@ -260,7 +260,7 @@
add_action('plugins_loaded', 'jvb_registrar_definitions',2);
add_action('plugins_loaded', 'jvb_field_definitions', 3);
add_action('plugins_loaded', 'jvb_options_definitions',3);
-add_action('init', 'jvbLoadBase', 1);
+add_action('init', 'jvbLoadBase', 2);
add_action('init', 'jvb_integration_definitions',3);
add_action('init', 'jvb_field_section_definitions', 5);
/**
--
Gitblit v1.10.0