Jake Vanderwerf
2026-06-29 4089ba01e0881c89a72332e13bc3a80b6bddec2a
inc/managers/Dashboard/DashboardManager.php
@@ -5,42 +5,57 @@
use JVBase\forms\TaxonomySelector;
use JVBase\base\Site;
use JVBase\managers\Cache;
use JVBase\managers\RoleManager;use JVBase\registrar\Registrar;
use JVBase\managers\IconsManager;use JVBase\managers\RoleManager;use JVBase\meta\Form;use JVBase\meta\Meta;use JVBase\registrar\Fields;use JVBase\registrar\Registrar;
use JVBase\ui\Navigation;
if (!defined('ABSPATH')) {
   exit;
}
class DashboardManager {
   public static string $based;
   protected string $slug = 'dash';
   protected string $baseURl;
   protected array $currentUserPages = [];
   protected array $pages = [];
   protected array $sections = [];
   protected Cache $cache;
   public function __construct() {
      self::$based = BASE.$this->slug;
      $this->cache = Cache::for('dashboard')->connect('post');
      if (JVB_TESTING) {
         $this->cache->flush();
      }
      $this->registerDashboard();
      $this->buildPages();
      $this->buildSections();
      $this->setCurrentUserPages();
      //Since this is loaded via JVBase, it's already running on init
      $this->registerDashboard();
      $this->registerHooks();
      $doit = apply_filters('jvb_base_dashboard_dash', true);
      if ($doit) {
         add_action(BASE.'dashboard_page_dash', [$this, 'renderMain'], 10);
      }
      add_action(BASE.'dashboard_page_account', [$this, 'renderAccount'], 10);
   }
   protected function registerDashboard():void
   {
      $dash = Registrar::forPost('dash', 'Dashboard', 'Dashboards');
      $dash = Registrar::forPost($this->slug, 'Dashboard', 'Dashboards');
      $dash->setIcon('gauge')
         ->make([
            'show_in_admin_bar'  => false,
            'rewrite'      => [
               'slug'         => 'dash',
               'slug'         => $this->slug,
               'with_front'   => false,
            ],
            'supports'     => [ 'title', 'editor', 'custom-fields'],
            'hierarchical' => true
            'hierarchical' => true,
            'has_archive'  => true,
         ])->setAll([
            'system'
         ]);
      $dash->register();
   }
   protected function registerHooks():void
@@ -54,14 +69,12 @@
   }
      public function handleRedirects():void
      {
         if (!is_singular(BASE.'dash') && !is_post_type_archive(BASE.'dash') && !is_404()) {
         if (!is_singular(self::$based) && !is_post_type_archive(self::$based) && !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();
@@ -70,40 +83,46 @@
               if (array_key_exists($page, ['', 'dash'])) {
                  return;
               }
               if (!array_key_exists($page, $this->pages)) {
               $page = $this->getCurrentPage();
               if (!in_array($page, $this->pages)) {
                  error_log('Looking for page: '.$page. ' in: '.print_r($this->pages, true));
                  error_log('[DashboardManager]::handleRedirect could not find page for '.$page);
                  return;
               }
               $permission = $this->pages[$page]->getPermission();
               $permission = $page->getPermission();
               if (!empty($permission) && !current_user_can($permission)) {
                  error_log('[DashboardManager]::handleRedirect User cannot manage '.$page);
                  $this->redirectToDashboard();
               }
            }
            return;
         }
         global $wp;
         if (str_starts_with($wp->request, 'dash/') || $wp->request === 'dash') {
         if (is_404() && str_starts_with($wp->request, $this->slug.'/') || $wp->request === $this->slug) {
            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')) {
         if (!is_singular(self::$based) && !is_post_type_archive(self::$based)) {
            return $template;
         }
         $page = $this->getCurrentPage();
         if (!empty($page->getIcon())) {
            add_filter('jvbLoadingIcon', $page->getIcon());
         if ($page && !empty($page->getIcon())) {
            add_filter('jvbLoadingIcon', [$page, 'getIcon']);
         }
         ob_start();
         jvbInlineStyles('nav');
         jvbInlineStyles('dash');
         jvbInlineStyles('forms');
         $this->renderHeader();
         $page->render();
         echo $page->render();
         $this->renderFooter();
         return ob_get_clean();
         $got = ob_get_clean();
         echo $got;
         return $got;
      }
         protected function renderHeader():void
         {
@@ -164,35 +183,6 @@
            <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>
@@ -210,6 +200,7 @@
         protected function outputSidebarNavigation():string
         {
            $menu = new Navigation('sidebar');
            $menuClasses = ['left'];
            $itemClasses = ['col'];
            $menu->addClass('sidebar left')->hasToggle()->defaultMenuClasses($menuClasses);
@@ -219,140 +210,63 @@
            $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;
               });
            $all = $this->getSectionPagesAndSections();
               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());
                     }
                  }
               }
            unset($all['dash']);
//          uasort($pages, fn($a, $b) => $a->getOrder() <=> $b->getOrder());
            uasort($all, fn($a, $b) => $a->getOrder() <=> $b->getOrder());
            }
            $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);
                           }
                        }
                     }
                  }
            foreach ($all as $slug => $item) {
               if (is_a($item, Section::class)) {
                  $this->buildMenuSection($item, $menu);
               } else {
                  $menuItem = $menu->addItem($item->getTitle(), jvbDashIcon($item->getIcon()));
                  $menuItem->url($item->getURL());
               }
            }
            //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();
            return $menu->render();
         }
            protected function buildMenuSection(Section $section, Navigation $menu, array $pages = []):void
            {
               $all = $this->getSectionPagesAndSections($section->getSlug());
               if (empty($all)) {
                  return;
               }
               uasort($all, fn($a, $b) => $a->getOrder() <=> $b->getOrder());
               $page = $menu->addItem($section->getTitle(), jvbDashIcon($section->getIcon()));
               $main = $this->getMainPage($section->getSlug());
               if ($main && $section->getIsLink()) {
                  $page->url($main->getURL());
               }
               $submenu = $page->submenu();
               foreach ($all as $slug=>$item) {
                  if (is_a($item, Section::class)) {
                     $this->buildMenuSection($item, $submenu);
                  } elseif ($slug !== $section->getSlug()) {
                     $menuItem = $submenu->addItem($item->getTitle(), jvbDashIcon($item->getIcon()));
                     $menuItem->url($item->getURL());
                  }
               }
            }
            protected function getSectionPagesAndSections(?string $section = null):array
            {
               $pages = $this->getSectionPages($section);
               $pages = array_filter($pages, function ($p) {
                  return empty($p->getPermission()) || current_user_can('manage_options') || current_user_can($p->getPermission());
               });
               $sections = $this->getSectionSections($section);
               $sections = array_filter($sections, function ($s) {
                  $check = empty($s->getPermission()) || current_user_can('manage_options') || current_user_can($s->getPermission());
                  $pages = $this->getSectionPagesAndSections($s->getSlug());
                  return $check && !empty($pages);
               });
               return array_merge($pages, $sections);
            }
      public function redirectFromAdmin():void
      {
         //Skip if already processing a redirect
@@ -370,7 +284,21 @@
      }
      public function dashboardScripts():void
      {
         if (!is_singular(self::$based) && !is_post_type_archive(self::$based)) {
            return;
         }
         IconsManager::for('forms')->enqueueIconStyles();
         IconsManager::for('dash')->enqueueIconStyles();
         wp_enqueue_script('jvb-form');
         wp_enqueue_script('jvb-selector');
         wp_enqueue_script('jvb-uploader');
         wp_enqueue_script('jvb-content');
         $page = $this->getCurrentPage();
         foreach ($page->getScripts() as $script) {
            wp_enqueue_script($script);
         }
      }
      public function excludeDashboard(array $IDs):array {
@@ -378,7 +306,7 @@
            'dashboardIDs',
            function() {
               return get_posts([
                  'post_type' => BASE.'dash',
                  'post_type' => self::$based,
                  'posts_per_page' => -1,
                  'fields' => 'ids',
               ]);
@@ -392,110 +320,182 @@
   private function buildPages():void
   {
      $this->addPage('dash', 'Dashboard','door');
      $this->buildContentPages();
      $this->buildReferrals();
      $this->addPage('Dashboard', 'dash','door');
      $this->buildContentPages(10);
      $this->buildReferrals(30);
      $this->buildFavourites(5);
      $this->buildKarma(15);
      $this->buildNotifications(20);
      $this->buildMembership();
      $this->buildFavourites();
      $this->buildKarma();
      $this->buildNotifications();
      $this->buildIntegrations();
      $this->buildSettingsPages();
      $this->buildAccountPages();
      $this->buildIntegrations(35);
      $this->buildSettingsPages(40);
      $this->buildAccountPages(50);
   }
      private function buildContentPages():void
      private function buildContentPages(int $order = 10):void
      {
         $content = Registrar::getRegistered('post');
         $this->sections['content'] = new Section('Your Content', 'content', 'book-bookmark');
         $this->sections['content']->setOrder($order);
         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');
            $page->setScripts(['jvb-crud', 'jvb-creator']);
            $this->sections[$c] = new Section($registrar->getPlural(), $c, $registrar->getIcon(), 'content');
            $this->sections[$c]->setPermission(RoleManager::getPermissionName('edit', $c));
            $this->sections[$c]->setIsLink(true);
            $taxonomies = $registrar->registrar->taxonomies;
            foreach ($taxonomies as $tax) {
               $taxReg = Registrar::getInstance($tax);
               if ($taxReg) {
                  $taxPage = $this->addPage($taxReg->getPlural(), $tax, $registrar->getIcon(), $page->getID());
                  $taxPage->setPermission(RoleManager::getPermissionName('edit', $c));
                  $taxPage->setSection($c);
                  $taxPage->setScripts(['jvb-crud', 'jvb-creator']);
               }
            }
            $this->pages[$c] = $page;
         }
         $contentTax = Registrar::withFeature('is_content', 'term');
         foreach ($contentTax as $c) {
            $registrar = Registrar::getInstance($c);
            $page = $this->addPage($registrar->getPlural(), $c, $registrar->getIcon());
            $page->setPermission(RoleManager::getPermissionName('edit', $c));
            $page->setSection($c);
            //TODO: add artist management and invitations, if applicable
         }
      }
      private function buildSettingsPages():void
      private function buildSettingsPages(int $order = 40):void
      {
         $this->sections['settings'] = new Section('Settings', 'settings', 'gear-six');
         $this->sections['settings']->setOrder($order);
         $seo = $this->addPage('SEO', 'seo', 'robot');
         $seo->setSection('settings');
         $seo->setPermission('manage_options');
         $seo->setScripts(['jvb-schema']);
         //TODO: Create permission that user has editable content
         $this->pages['seo'] = $seo;
      }
      private function buildAccountPages():void
      private function buildAccountPages(int $order = 50):void
      {
         if (Site::has('support')) {
            $this->sections['support'] = new Section('Support', 'support', 'question');
            $this->sections['support']->setOrder($order + 5);
            $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);
         $this->sections['account'] = new Section('Account', 'account', 'user-circle');
         $this->sections['account']->setOrder($order);
         $this->sections['account']->setIsLink(true);
         $password = $this->addPage('Reset Password', 'reset-password', 'password', $account->getID());
         $password->setOrder(2);
         $password->setSection('account');
      }
      private function buildReferrals():void
      private function buildReferrals(int $order = 30):void
      {
         if (Site::has('referrals')) {
            $page = $this->addPage('Referrals', 'referrals', 'hand-heart');
            $page->setOrder($order);
         }
      }
      private function buildMembership():void
      {
         $membership = Site::membership();
         if ($membership) {
            $buildNotification = false;
            if ($membership->has('can_invite')) {
               $buildNotification = true;
               $page = $this->addPage('Invite', 'invite', '');
               $page->setSection('notifications');
            }
            if ($membership->has('term_approval')) {
               $buildNotification = true;
               $page = $this->addPage('Approvals', 'approvals', 'check-circle');
               $page->setSection('notifications');
            }
            if ($membership->has('forum')) {
               $page = $this->addPage('Forum', 'forum', 'chats-teardrop');
               $page->setScripts(['jvb-news']);
            }
            if ($membership->has('member_content')) {
               $page = $this->addPage('Metrics', 'metrics', 'chart-line');
            }
            if ($buildNotification){
               $this->buildNotificationSection();
            }
         }
      }
      private function buildFavourites():void
      private function buildFavourites(int $order = 5):void
      {
         if (Site::has('favourites')) {
            $this->sections['favourites'] = new Section('Favourites', 'favourites', 'heart');
            $this->sections['favourites']->setOrder($order);
            $page = $this->addPage('Favourites', 'favourites', 'heart');
            //TODO: Lists, Share permissions
            $page->setSection('favourites');
            $page->setScripts(['jvb-favourites']);
            $lists = $this->addPage('Lists', 'lists', 'list-heart', $page->getID());
            $lists->setSection('favourites');
            $lists->setScripts(['jvb-favourites']);
            $permissions = $this->addPage('Permissions', 'permissions', 'hand-heart', $page->getID());
            $permissions->setSection('favourites');
            $permissions->setScripts(['jvb-favourites']);
         }
      }
      private function buildKarma():void
      private function buildKarma(int $order = 15):void
      {
         if (!empty(Registrar::withFeature('karma'))) {
            $page = $this->addPage('Karmic', 'karmic', 'arrow-fat-up');
            $page->setOrder($order);
         }
      }
      private function buildNotifications():void
      private function buildNotifications(int $order = 20):void
      {
         if (Site::has('notifications')) {
            $this->buildNotificationSection($order);
            $page = $this->addPage('Notifications', 'notifications', 'bell');
            $page->setSection('notifications');
            $page->setScripts(['jvb-notifications-manager']);
            $page = $this->addPage('Permissions', 'permissions', 'gear-six');
            $page->setSection('notifications');
            $page->setOrder(999);
         }
      }
      private function buildIntegrations():void
         private function buildNotificationSection(int $order = 20):void
         {
            $this->sections['notifications'] = new Section('Notifications', 'notifications', 'bell');
            $this->sections['notifications']->setOrder($order);
         }
      private function buildIntegrations(int $order = 35):void
      {
         if (Site::hasAnyIntegration()) {
            $page = $this->addPage('Integrations', 'integrations', 'plugs-connected');
            $page->setSection('settings');
            $page->setOrder($order);
            $page->setScripts(['jvb-integrations']);
            $parent = $page->getID();
            $this->sections['integrations'] = new Section('Integrations', 'integrations', 'plugs-connected', 'settings');
            $this->sections['integrations']->setOrder($order);
            $this->sections['integrations']->setIsLink(true);
            foreach (array_keys(Site::getIntegrations()) as $integration) {
               $integration = match($integration) {
                  'maps' => 'JVBase\integrations\GoogleMaps',
@@ -511,7 +511,13 @@
               };
               $title = $integration::title();
               $icon = $integration::icon();
               $page = $this->addPage($title, $title, $icon, $parent);
               if ($integration::hasExtraOptions()) {
                  $page = $this->addPage($title, $title, $icon, $parent);
                  $page->setScripts(['jvb-integrations']);
                  $page->setSection('integrations');
               }
            }
         }
      }
@@ -519,7 +525,7 @@
   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;
      $this->pages[$slug] = $page;
      return $page;
   }
@@ -528,71 +534,46 @@
      $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);
         }
      $missing = array_diff($sections, array_map(function($s) { return $s->getSlug(); }, $this->sections));
      if (!empty($missing)) {
         error_log('[DashboardManager]::buildSections Section mentioned, but not defined: '.print_r($missing, true));
      }
//    foreach ($sections as $section) {
//       $isLink = $this->hasMainPage($section);
//       if ($isLink) {
//          $this->sections[$section]->setIsLink(true);
//       }
//    }
   }
      protected function getSectionPages(string $section):array
      protected function getSectionPages(?string $section = null):array
      {
         return array_filter($this->pages, function($page) use ($section) {
            return $page->getSection() === $section;
         });
      }
      protected function getSectionSections(?string $section = null):array
      {
         return array_filter($this->sections, function($s) use ($section) {
            return $s->getParent() === $section;
         });
      }
      protected function getMainPage(string $section):DashboardPage|false
      {
         $sectionPages = $this->getSectionPages($section);
         $mainPage = array_filter($sectionPages, function ($page) use ($section) {
         $section = DashboardPage::sanitizeString($section);
         $mainPage = array_values(array_filter($this->pages, function ($page) use ($section) {
            return $page->getSlug() === $section;
         });
         }));
         return empty($mainPage) ? false : $mainPage[0];
      }
      protected function hasMainPage(string $section):bool
      {
         $section = DashboardPage::sanitizeString($section);
         return $this->getMainPage($section) !== false;
      }
@@ -615,16 +596,22 @@
      return true;
   }
   protected function setCurrentUserPages():void
   {
      $this->currentUserPages = array_filter($this->pages, function($page) {
         return current_user_can('manage_options') || empty($page->getPermission()) || current_user_can($page->getPermission());
      });
   }
   #[NoReturn]protected function redirectToLogin():void
   {
      wp_redirect(wp_login_url(get_home_url(null, '/dash')));
      wp_redirect(wp_login_url(get_home_url(null, '/'.$this->slug)));
      exit;
   }
   #[NoReturn]protected function redirectToDashboard():void
   {
      wp_redirect(get_home_url(null, '/dash'));
      wp_redirect(get_home_url(null, '/'.$this->slug));
      exit;
   }
   #[NoReturn]protected function redirectToHome():void
@@ -636,16 +623,19 @@
   protected function getCurrentPage():DashboardPage|false
   {
      $slug = $this->getCurrentPageSlug();
      if (!array_key_exists($slug, $this->pages)) {
      $page = array_filter($this->pages, function($p) use ($slug) {
         return $p->getSlug() === $slug;
      });
      if (empty($page)) {
         error_log('[DashboardManager]::getCurrentPage Could not get configuration for '.$slug);
         return false;
      }
      return $this->pages[$slug];
      return $page[array_key_first($page)];
   }
   protected function getCurrentPageSlug():string
   {
      if (is_post_type_archive(BASE.'dash')) {
      if (is_post_type_archive(self::$based)) {
         return 'dash';
      }
@@ -656,4 +646,49 @@
      return $post->post_name;
   }
   public function renderMain(string $slug):void
   {
      if ($slug !== 'dash') {
         return;
      }
      $user = get_userdata(get_current_user_id());
      $name = ($user->first_name !== '') ? $user->first_name : $user->display_name;
      echo sprintf(
         '<h1>Hey %s</h1><p>Welcome back!</p>',
         $name
      );
      $reg = Registrar::getInstance(jvbUserRole($user->ID));
      if ($reg && !empty($reg->getCreatable())) {
         echo '<p>Everything saves auto-magically - so rest easy.</p>';
      }
   }
   public function renderAccount():void
   {
      $user = get_userdata(get_current_user_id());
      $role = jvbUserRole($user->ID);
      $registrar = Registrar::getInstance($role);
      if ($registrar) {
         $meta = Meta::forUser($user->ID);
         $fields = $registrar->getFields();
      } else {
         $fields = Fields::getUserFields();
         $meta = new Meta($user->ID, 'user', $fields);
      }
      echo Form::renderFormFrom($meta, 'user', [
         'heading'   => 'Your Account',
         'description' => [
            'You can set your information here.',
            'To reset your password, check the side menu under "Account"'
         ]
      ]);
   }
}