<?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;
|
}
|
}
|