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();
?>
>
= $page->getTitle() . ' | Dashboard' ?>
pages as $page) {
if (empty($page->getPermission()) || current_user_can($page->getPermission())) {
echo '';
}
}
?>
= jvbDarkModeToggle() ?>
'core/site-logo',
'attrs' => [],
]);
}
?>
= $this->outputSidebarNavigation(); ?>
getCurrentPageSlug());
?>
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;
}
}