<?php
|
namespace JVBase\managers;
|
|
use JVBase\forms\TaxonomySelector;
|
use JVBase\base\Site;
|
use JVBase\meta\Form;
|
use JVBase\registrar\Registrar;
|
use JVBase\ui\Navigation;
|
use WP_Error;use WP_Query;use WP_User;
|
|
if (!defined('ABSPATH')) {
|
exit; // Exit if accessed directly
|
}
|
|
/**
|
* The base constructor for our custom dashboard
|
*/
|
class DashboardManager
|
{
|
protected WP_User $user;
|
protected Cache $cache;
|
protected string $role;
|
protected string $baseURL;
|
protected int $userLink;
|
|
public function __construct()
|
{
|
$this->cache = Cache::for('dashboard', WEEK_IN_SECONDS)->connect('user');
|
add_action('init', [$this, 'registerDashboard']);
|
|
$this->user = wp_get_current_user();
|
$this->role = jvbUserRole($this->user->ID);
|
$this->userLink = (int)get_user_meta($this->user->ID, BASE.'profile_link', true);
|
$this->baseURL = get_home_url(null, '/dash');
|
|
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('jvbDashboardPage', [$this, 'renderIndex'], 10, 2);
|
|
jvb_register_do_once('buildDashboard', [$this, 'activate']);
|
|
add_filter('the_seo_framework_sitemap_exclude_ids', [$this, 'excludeDashboard'], 10, 1);
|
}
|
|
public function excludeDashboard(array $ids):array {
|
$cached = $this->cache->remember(
|
'dashboardIDs',
|
function() {
|
return get_posts([
|
'post_type' => BASE.'dash',
|
'posts_per_page' => -1,
|
'fields' => 'ids',
|
]);
|
});
|
return array_merge($ids, $cached);
|
}
|
|
/**
|
* Registers the custom post type that handles the dashboard
|
* @return void
|
*/
|
public function registerDashboard():void
|
{
|
|
$plural = 'Dashboards';
|
$singular = 'Dashboard';
|
register_post_type(BASE.'dash', array(
|
'labels' => [
|
'name' => $plural,
|
'singular_name' => $singular,
|
'menu_name' => $plural,
|
'name_admin_bar' => $singular,
|
'add_new' => "Add New",
|
'add_new_item' => "Add New {$singular}",
|
'new_item' => "New {$singular}",
|
'edit_item' => "Edit {$singular}",
|
'view_item' => "View {$singular}",
|
'all_items' => "All {$plural}",
|
'search_items' => "Search {$plural}",
|
'parent_item_colon' => "Parent {$plural}:",
|
'not_found' => "No {$plural} found.",
|
'not_found_in_trash' => "No {$plural} found in Trash.",
|
],
|
'menu_icon' => jvbCSSIcon('gauge'),
|
'public' => true,
|
'publicly_queryable' => true,
|
'show_in_menu' => true,
|
'show_in_admin_bar' => false,
|
'has_archive' => true,
|
'hierarchical' => true,
|
'rewrite' => array(
|
'slug' => 'dash',
|
'with_front' => false
|
),
|
'capability_type' => 'post',
|
'supports' => array('title', 'editor', 'custom-fields')
|
));
|
}
|
|
/**
|
* Redirect all non-admin users from wp-admin to custom dashboard
|
*/
|
public function redirectFromAdmin()
|
{
|
// Skip if already processing a redirect
|
if (defined('DOING_AJAX') && DOING_AJAX) {
|
return;
|
}
|
|
// Ensure user is fully loaded
|
if (!did_action('wp_loaded')) {
|
return;
|
}
|
|
// Allow admins to access wp-admin if needed
|
if (current_user_can('manage_options')) {
|
return;
|
}
|
// Redirect to custom dashboard
|
if (is_user_logged_in() && isOurPeople()) {
|
$this->redirectToDashboard();
|
}
|
}
|
|
protected function redirectToLogin():void
|
{
|
wp_redirect(wp_login_url(get_home_url(null, '/dash')));
|
exit;
|
}
|
|
protected function redirectToDashboard():void
|
{
|
wp_redirect(get_home_url(null, '/dash'));
|
exit;
|
}
|
|
|
protected function getConfig(string $page):Registrar|false
|
{
|
$pages = $this->getAllDashboardPages();
|
$key = array_search($page, $pages);
|
if ($key === false || is_numeric($key)) {
|
return false;
|
}
|
return Registrar::getInstance($key)??false;
|
}
|
|
/**
|
* Check if user can access page and redirect if not
|
* @param string $page
|
* @param int|null $userID
|
* @return void
|
*/
|
protected function requirePageAccess(string $page, ?int $userID = null):void
|
{
|
$allowedPages = $this->getUserAllowedPages($userID);
|
|
if (!in_array($page, $allowedPages)) {
|
$this->redirectToDashboard();
|
}
|
}
|
|
protected function getTitle(string $slug):string
|
{
|
$registrar = $this->getConfig($slug);
|
if ($registrar) {
|
return $registrar->getConfig('dashboard')['title']??$registrar->getPlural();
|
}
|
return ucwords(str_replace('-', ' ', str_replace('_', ' ', $slug)));
|
}
|
|
public function handleRedirects():void
|
{
|
// Only process dashboard-related pages and 404s
|
if (!is_singular(BASE.'dash') && !is_post_type_archive(BASE.'dash') && !is_404()) {
|
return;
|
}
|
|
// Check if user is logged in first
|
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
|
if (!is_404() && !isOurPeople() && !current_user_can('manage_options')) {
|
error_log('Redirecting to home - user lacks dashboard access');
|
wp_redirect(home_url());
|
exit;
|
}
|
|
// Handle 404s that are trying to access dashboard URLs
|
global $wp;
|
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
|
if (!is_404()) {
|
$page = $this->getCurrentPageSlug();
|
|
// Dashboard home is always accessible (if authenticated)
|
if ($page === '' || $page === 'dash') {
|
return;
|
}
|
$page = $this->getCurrentPageTitle();
|
// Check if page exists in allowed pages
|
$allowedPages = $this->getUserAllowedPages();
|
|
if (!in_array($page, $allowedPages)) {
|
error_log("User not allowed to access page: {$page}");
|
$this->redirectToDashboard();
|
}
|
}
|
}
|
|
/**
|
* Ensures the necessary pages are created
|
* @return void
|
*/
|
public function activate():void
|
{
|
$manageableContent = $this->getAllDashboardPages();
|
error_log('[DashboardManager]::buildDashboard Manageable Content: '.print_r($manageableContent, true));
|
foreach ($manageableContent as $slug => $page) {
|
if ($page === 'dash') {
|
continue;
|
}
|
|
$ID = $this->createDashboardPage($slug, $page);
|
|
|
$registrar = Registrar::getInstance($slug);
|
if ($registrar) {
|
$create = [
|
'new_' => 'Create New ',
|
'edit_' => 'Edit '
|
];
|
$parentID = (int)$ID;
|
foreach ($create as $s => $t) {
|
$s .= $slug;
|
$t .= $page;
|
$this->createDashboardPage($s, $t, $parentID);
|
}
|
}
|
if ($page === 'Integrations') {
|
$this->buildIntegrationPages($ID);
|
}
|
}
|
}
|
public function createDashboardPage(string $slug, string $page, int $parentID = 0):int|WP_Error
|
{
|
if (is_numeric($slug)) {
|
$slug = $this->getSlug($slug, $page);
|
}
|
|
$existing = new WP_Query([
|
'post_type' => BASE.'dash',
|
'name' => $slug,
|
'fields' => 'ids',
|
'posts_per_page' => 1,
|
]);
|
|
if ($existing->have_posts()) {
|
return $existing->posts[0];
|
}
|
|
|
$args = [
|
'post_title' => $page,
|
'post_name' => $slug,
|
'post_type' => BASE.'dash',
|
'post_status' => 'publish',
|
];
|
if ($parentID > 0) {
|
$args['post_parent'] = $parentID;
|
}
|
return wp_insert_post($args);
|
}
|
|
/**
|
* Build integration sub-pages
|
* @param int $parentID
|
* @return void
|
*/
|
protected function buildIntegrationPages(int $parentID):void
|
{
|
$integrations = JVB()->getAvailableServices(false);
|
|
foreach ($integrations as $name => $integration) {
|
$title = $integration->getTitle();
|
|
$slug = sanitize_title($title);
|
$this->createDashboardPage($slug, $title, $parentID);
|
}
|
}
|
|
|
protected function getDescription(string $page):string
|
{
|
$registrar = $this->getConfig($page);
|
if ($registrar) {
|
$description = $registrar->getConfig('dashboard')['description']??'';
|
} else {
|
$description = apply_filters('jvbDashboardDescription', $page);
|
switch ($page) {
|
case 'approval':
|
$description = 'See your approval requests for term creation, joining shops, or joining edmonton.ink. You can also help shape the community by approving other\'s requests!';
|
break;
|
case 'metrics':
|
$description = 'See what kind of traffic you\'re getting. <i>(coming soon)</i>';
|
break;
|
case 'favourites':
|
$description = 'See what you favourited, and create, manage, and share lists.';
|
break;
|
case 'support':
|
$description = 'Sometimes we all need a hand. This is your direct access to the site admin - or text Jake at <a href="sms:18258239916">825-823-9916</a>';
|
break;
|
case 'settings':
|
$description = 'Control your privacy and email frequency.';
|
break;
|
}
|
}
|
|
return $description;
|
}
|
|
|
/**
|
* Hacking into the template_include to set our custom templates and protections
|
* @param string $template
|
*
|
* @return string
|
*/
|
public function dashboardTemplates(string $template):string
|
{
|
if (!is_singular(BASE.'dash') && !is_post_type_archive(BASE.'dash')) {
|
return $template;
|
}
|
|
// Get current page/section
|
$page = $this->getCurrentPageTitle();
|
$registrar = $this->getConfig($page);
|
if($registrar) {
|
add_filter('jvbLoadingIcon', function() use ($registrar) {
|
return $registrar->getIcon();
|
});
|
}
|
$integrationSlugs = array_map(function($name) {
|
return sanitize_title(str_replace('_', '-', $name));
|
}, array_keys(JVB()->getAvailableServices(false)));
|
|
// Check if this is an integration subpage
|
if (in_array($page, $integrationSlugs)) {
|
// Pass along to the Integrations template handler which knows to check for subpages
|
$page = 'integrations';
|
}
|
// echo $this->renderDashboard($page);
|
|
echo $this->cache->remember(
|
$page,
|
function() use ($page) {
|
return $this->renderDashboard($page);
|
}
|
);
|
|
return '';
|
}
|
|
protected function getConstantSlug(string $page):string
|
{
|
$slug = array_search($page, $this->getAllDashboardPages());
|
return (is_numeric($slug)) ? '' : $slug;
|
}
|
protected function renderDashboard(string $page):string
|
{
|
ob_start();
|
jvbInlineStyles('nav');
|
jvbInlineStyles('dash');
|
jvbInlineStyles('forms');
|
$this->renderHeader();
|
// Pass to page handler
|
$constantSlug = $this->getConstantSlug($page);
|
|
echo apply_filters(
|
'jvbDashboardPage',
|
$this->renderPage($page),
|
$page,
|
$constantSlug
|
);
|
|
$this->renderFooter();
|
return ob_get_clean();
|
// $integrationSlugs = array_map(function($name) {
|
// return sanitize_title(str_replace('_', '-', $name));
|
// }, array_keys(JVB()->getAvailableServices(false)));
|
//
|
// if ($page === 'integrations' || in_array($page, $integrationSlugs)) {
|
// // Check integration access
|
// if ($page === 'integrations') {
|
// if (!Site::hasAnyIntegration('user', $this->role)) {
|
// $this->redirectToDashboard();
|
// }
|
// } else {
|
// if (!Site::hasIntegration($page, 'user', $this->role)) {
|
// $this->redirectToDashboard();
|
// }
|
// }
|
// } elseif ($page === 'bio') {
|
// // Bio page logic
|
// $permission = JVB_USER[$this->role]['profile'] ?? false;
|
// if (!$permission || (!current_user_can('manage_'.$permission) && !current_user_can('manage_options'))) {
|
// $this->redirectToDashboard();
|
// }
|
// } elseif ($page === 'settings') {
|
// // Settings page logic
|
// if (!current_user_can('manage_settings') && !current_user_can('manage_options')) {
|
// $this->redirectToDashboard();
|
// }
|
// } elseif ($page === 'approval') {
|
// // Approval page logic
|
// if (!current_user_can('skip_moderation')) {
|
// $this->redirectToDashboard();
|
// }
|
// } elseif ($page !== 'dash') {
|
// // Regular content type - check permission
|
// $type = match($page) {
|
// 'menu-item' => 'menu_item',
|
// 'events' => 'event',
|
// default => $page
|
// };
|
//
|
// $permission = $this->getPermissionForType($type);
|
// if (!current_user_can($permission)) {
|
// $this->redirectToDashboard();
|
// }
|
// }
|
// // Enqueue needed styles/scripts
|
//
|
// $this->cache->delete($page);
|
// echo $this->cache->remember(
|
// $page,
|
// function() use ($page) {
|
// ob_start();
|
// $this->renderHeader();
|
//
|
// switch ($page) {
|
// case 'dash':
|
// if (current_user_can('manage_options')) {
|
// $content = apply_filters('jvbAdminDashboard', '');
|
//
|
// if ($content !== '') {
|
// echo $content;
|
// }else {
|
// $this->renderAdmin();
|
// }
|
// } else {
|
// $this->renderIndex();
|
// }
|
//
|
// break;
|
// case 'admin':
|
// $this->renderAdmin();
|
// break;
|
// case 'bio':
|
// $this->renderForm(JVB_USER[$this->role]['profile']);
|
// break;
|
// case 'settings':
|
// $this->renderSettings();
|
// break;
|
// case 'integrations':
|
// case 'bluesky':
|
// case 'cloudflare':
|
// case 'facebook':
|
// case 'google-maps':
|
// case 'google-my-business':
|
// case 'helcim':
|
// case 'instagram':
|
// case 'square':
|
// case 'umami':
|
// $this->renderIntegrations($page);
|
// break;
|
// case 'approval':
|
// $this->renderApprovals();
|
// break;
|
// default:
|
// $this->renderCRUD($page);
|
// break;
|
// }
|
//
|
// echo jvbLoadingScreen();
|
// $this->renderFooter();
|
//
|
// // Get buffer contents and clean buffer
|
// return ob_get_clean();
|
// }
|
// );
|
//
|
// // Return empty string to prevent default template
|
// return '';
|
}
|
|
protected function renderPage(string $page):string
|
{
|
return '<h1>Whoops</h1>
|
<p>It seems this page isn\'t configured yet.</p>
|
<p>If this keeps happening, maybe contact the admin.</p>';
|
}
|
|
/**
|
* Enqueues necessary scripts
|
* @return void
|
*/
|
public function dashboardScripts():void
|
{
|
if (!is_singular(BASE.'dash') && !is_post_type_archive(BASE.'dash')) {
|
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->getCurrentPageSlug();
|
|
switch ($page) {
|
case 'notifications':
|
if (Site::has('notifications')) {
|
wp_enqueue_script('jvb-notification-manager');
|
}
|
break;
|
case 'integrations':
|
wp_enqueue_script('jvb-integrations');
|
|
break;
|
case 'admin':
|
case 'dash':
|
// if (current_user_can('manage_options') && apply_filters('jvbAdminDashboard', '') === '') {
|
// wp_enqueue_script(
|
// 'jvb-admin',
|
// JVB_URL . 'assets/js/min/admin.min.js',
|
// [
|
// 'jvb-queue',
|
//// 'jvb-loading'
|
// ],
|
// [
|
// 'strategy' => 'defer',
|
// 'in_footer' => true
|
// ]
|
// );
|
|
// wp_localize_script(
|
// 'jvb-admin',
|
// 'jvbAdmin',
|
// [
|
// 'nonce' => wp_create_nonce('itsme')
|
// ]
|
// );
|
// }
|
break;
|
case 'seo':
|
wp_enqueue_script('jvb-schema');
|
break;
|
default:
|
wp_enqueue_script('jvb-crud');
|
break;
|
}
|
if (Site::has('favourites')) {
|
wp_enqueue_script('jvb-favourites');
|
wp_localize_script('jvb-favourites-manager', 'favouritesSettings', [
|
'strings' => [
|
'loadError' => 'Error loading favourites',
|
'saveError' => 'Error updating favourite',
|
'removeSuccess' => 'Removed from favourites',
|
'addSuccess' => 'Added to favourites'
|
]
|
]);
|
}
|
|
|
|
wp_enqueue_script('jvb-creator');
|
|
if (Site::has('forum')) {
|
wp_enqueue_script('jvb-news');
|
}
|
do_action('jvbDashScripts', $page);
|
}
|
|
protected function getCurrentPageTitle():string
|
{
|
if (is_post_type_archive(BASE.'dash')) {
|
return 'dash';
|
}
|
|
global $post;
|
if (!$post) {
|
return '';
|
}
|
return html_entity_decode($post->post_title);
|
}
|
protected function getCurrentPageSlug():string
|
{
|
if (is_post_type_archive(BASE.'dash')) {
|
return 'dash';
|
}
|
|
global $post;
|
if (!$post) {
|
return '';
|
}
|
|
return $post->post_name;
|
}
|
protected function getIcon(string $slug, string $page):string
|
{
|
return $this->cache->remember('icon_'.sanitize_title($page), function() use ($slug, $page) {
|
$icon = sanitize_title($page);
|
if (!is_numeric($slug)) {
|
$registrar =Registrar::getInstance($slug);
|
if ($registrar) {
|
return $registrar->getIcon();
|
}
|
|
}
|
return $icon;
|
});
|
}
|
protected function getSlug(string $slug, string $page):string
|
{
|
return $this->cache->remember('slug_'.sanitize_title($page), function() use ($slug, $page) {
|
if (!is_numeric($slug)) {
|
return $slug;
|
} else {
|
return sanitize_title($page);
|
}
|
});
|
}
|
|
protected function renderHeader():void
|
{
|
?>
|
<!DOCTYPE html>
|
<html <?php language_attributes(); ?>>
|
<head>
|
<title><?= Site::dashboardTitle()??'Dashboard | '.get_bloginfo('name') ?></title>
|
<meta charset="<?php bloginfo('charset'); ?>">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<?php
|
$pages = $this->getUserAllowedPages();
|
|
foreach($pages as $page) {
|
$page = str_replace('_', '-', $page);
|
$link = ($page === 'dash') ? '/'.$page : "/dash/$page";
|
?>
|
<link rel="preconnect" href="<?= get_home_url(null, $link)?>"/>
|
<?php
|
}
|
?>
|
<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>
|
|
<?php
|
$menu = new Navigation('sidebar');
|
$menuClasses = ['col', 'left', 'nowrap'];
|
$itemClasses = ['col'];
|
$menu->addClass('col left')->hasToggle()->defaultMenuClasses($menuClasses);
|
$menu->defaultItemClasses($itemClasses);
|
$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::getFeatured('is_content', 'term')
|
);
|
$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();
|
?>
|
|
<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
|
}
|
|
public function renderIndex(string $content, string $page):string
|
{
|
if ($page !== '' && $page !== 'dash') {
|
return $content;
|
}
|
|
if (Site::has('referrals')) {
|
$whatever = JVB()->referrals()->getReferralWelcomeMessage($this->user->ID);
|
if (!empty($whatever)) {
|
return $whatever;
|
}
|
}
|
|
ob_start();
|
$name = ($this->user->first_name !== '') ? $this->user->first_name : $this->user->display_name;
|
|
echo '<h1>Hey '.$name.'</h1>';
|
echo '<p>Welcome back!</p>';
|
|
$pages = $this->getUserAllowedPages();
|
|
echo '<h2>What would you like to do today?</h2>';
|
|
echo '<ul class="dashboard">';
|
foreach ($pages as $slug => $page) {
|
if ($page === 'dash') {
|
continue;
|
}
|
$title = $this->getTitle($page);
|
|
$description = $this->getDescription($page);
|
|
$slug = $this->getSlug($slug, $page);
|
$icon = $this->getIcon($slug, $page);
|
if ($title !== '') {
|
echo '<li><p><a href="'.get_home_url(null, '/dash/'.$slug.'/').'"
|
data-page="'.$slug.'" data-dash>'.jvbDashIcon($icon).ucwords($title).'</a></p></li>';
|
}
|
|
}
|
echo '</ul>';
|
|
echo '<p>Everything saves auto-magically, so rest easy.</p>';
|
return ob_get_clean();
|
}
|
/**
|
* Similar to CRUD, except it only manages a single item, such as a user's profile or a shop
|
* @param string $type
|
*
|
* @return void
|
*/
|
protected function renderForm(string $type):void
|
{
|
|
wp_enqueue_script(
|
'jvb-bio-manager',
|
JVB_URL.'assets/js/min/bioManager.min.js',
|
array('jvb-queue', 'sortablejs', 'quill-js', 'jvb-selector'),
|
'1.0.0',
|
true
|
);
|
wp_localize_script('jvb-bio-manager', 'bioSettings', [
|
'type' => 'bio_update',
|
]);
|
jvbRenderSections($this->userLink, 'post', $type);
|
}
|
|
protected function renderSettings():void
|
{
|
wp_enqueue_script('jvb-form');
|
wp_enqueue_script(
|
'jvb-bio-manager',
|
JVB_URL.'assets/js/min/bioManager.min.js',
|
array('jvb-client-queue', 'sortablejs', 'quill-js', 'jvb-taxonomy-selector'),
|
'1.0.0',
|
true
|
);
|
wp_localize_script('jvb-bio-manager', 'bioSettings', [
|
'type' => 'user_settings',
|
]);
|
$content = apply_filters('jvbDashboardSettings', '');
|
if ($content !== '') {
|
echo $content;
|
} else {
|
jvbRenderSections($this->user->ID, 'user', jvbUserRole());
|
}
|
|
}
|
|
protected function getIntegrationsMenu():string
|
{
|
$integrations = JVB()->getAvailableServices(false);
|
$out = '';
|
if (!empty($integrations)) {
|
$out = '<nav class="integrations"><ul>';
|
|
$url = get_home_url(null, '/dash/integrations/');
|
$out .= '<li><a href="'.$url.'">'.jvbDashIcon('plugs-connected').'Integrations</a></li>';
|
foreach ($integrations as $name=> $integration) {
|
if (!JVB()->userCanConnect($name, $this->user->ID) || !$integration->hasDefaults()) {
|
continue;
|
}
|
$link = sanitize_title(str_replace('_', '-',$name));
|
$out .= '<li><a href="'.$url.$link.'">'.jvbDashIcon($integration->icon).$integration->getTitle().'</a></li>';
|
}
|
$out .= '</ul></nav>';
|
}
|
return $out;
|
}
|
|
protected function renderIntegrations(string $page):void
|
{
|
echo $this->getIntegrationsMenu();
|
$map = [
|
'google-my-business' => 'gmb',
|
'google-maps' => 'maps'
|
];
|
$connection = (array_key_exists($page, $map)) ? $map[$page] : $page;
|
if ($connection !== 'integrations') {
|
|
$userID = (Site::has('has_membership')) ? $this->user->ID : null;
|
$integration = JVB()->connect($connection, $userID);
|
|
echo '<h1>Managing '.$integration->title.'</h1>';
|
$integration->renderDefaults();
|
} else {
|
?>
|
<section class="item-grid integrations">
|
<?php
|
$all = JVB()->getAvailableServices();
|
foreach ($all as $name) {
|
if (current_user_can('manage_options')) {
|
$userID = null;
|
} else {
|
$userID = $this->user->ID;
|
}
|
|
JVB()->connect($name, $userID)->renderConnection();
|
}
|
?>
|
</section>
|
<?php
|
|
}
|
|
|
}
|
|
protected function renderApprovals():void
|
{
|
?>
|
<div class="approvals container">
|
<nav class="tabs row left" role="tablist">
|
<button type="button" class="tab active" data-tab="summary" role="tab" aria-selected="true">
|
<?= jvbDashIcon('infinity')?>All
|
</button>
|
<button type="button" class="tab" data-tab="artists" role="tab" aria-selected="false">
|
<?= jvbDashIcon('users-three')?>Artists
|
</button>
|
<button type="button" class="tab" data-tab="terms" role="tab" aria-selected="false">
|
<?= jvbDashIcon('hash')?>Terms
|
</button>
|
<button type="button" class="tab" data-tab="yours" role="tab" aria-selected="false">
|
<?= jvbDashIcon('user')?>Yours
|
</button>
|
</nav>
|
</div>
|
<section>
|
<table>
|
<thead>
|
<tr>
|
<th scope="col">
|
<input type="checkbox" id="select-all" name="select-all">
|
<label for="select-all">All</label>
|
</th>
|
<th scope="col">
|
Name
|
</th>
|
<th scope="col">
|
Requester
|
</th>
|
<th>
|
Actions
|
</th>
|
</tr>
|
</thead>
|
<tbody>
|
|
</tbody>
|
<tfoot>
|
<tr>
|
<th scope="col">
|
<input type="checkbox" id="select-all" name="select-all">
|
<label for="select-all">All</label>
|
</th>
|
<th scope="col">
|
Name
|
</th>
|
<th scope="col">
|
Requester
|
</th>
|
<th scope="col">
|
Actions
|
</th>
|
</tr>
|
</tfoot>
|
</table>
|
</section>
|
<?php
|
}
|
|
protected function renderFavourites():void
|
{
|
|
}
|
|
|
public function renderAdmin(string $content, string $page):string
|
{
|
if ($page !== '' && $page !== 'dash') {
|
return $content;
|
}
|
ob_start();
|
?>
|
<nav class="tabs row left" role="tablist">
|
<?php
|
$i=1;
|
$content = Registrar::getRegistered('post');
|
$contentTax = Registrar::getFeatured('is_content', 'term');
|
$taxonomies = Registrar::getRegistered('term');
|
foreach($contentTax as $index => $tax) {
|
unset($taxonomies[$index]);
|
}
|
$content = array_merge($content, $contentTax);
|
foreach ($content as $type) {
|
$registrar = Registrar::getInstance($type);
|
$active = ($i === 1) ? ' active' : '';
|
?>
|
<button type="button" class="tab<?=$active?>" data-tab="<?=$type?>" role="tab" aria-selected="<?= ($active !== '') ? 'true' : 'false'?>">
|
<h2><?=jvbDashIcon($registrar->getIcon())?> <?= $registrar->getPlural() ?></h2>
|
</button>
|
<?php
|
$i++;
|
}
|
?>
|
|
<label for="tab-select">Taxonomy:</label><select class="tab-list" id="tab-select">
|
<option> ... Taxonomy</option>
|
<?php
|
|
foreach ($taxonomies as $type) {
|
$taxRegistrar = Registrar::getInstance($type);
|
echo '<option value="'.$type.'">'.$taxRegistrar->getPlural().'</option>';
|
}
|
?>
|
</select>
|
|
</nav>
|
|
<div class="tab-content active" data-tab="artist" role="tabpanel">
|
<h1>Artists<small>Manage artists here</small></h1>
|
</div>
|
<div class="actions">
|
<?= jvbRenderToggleTextField(
|
'vertical',
|
'TAB NAV:',
|
'',
|
jvbDashIcon('caret-double-down'),
|
jvbDashIcon('caret-double-right'))?>
|
|
</div>
|
<div class="items-container">
|
</div>
|
|
<?php
|
|
|
|
foreach (Registrar::getRegistered() as $type) {
|
$fields = Registrar::getFieldsFor($type);
|
?>
|
<template class="<?= $type ?>Table">
|
<table>
|
<thead>
|
<tr>
|
<th id="select" scope="col"></th>
|
<th scope="col">Actions</th>
|
<?php
|
foreach ($fields as $n => $config) {
|
if (array_key_exists('quickEdit', $config)) {
|
$extra = '';
|
switch ($config['type']) {
|
case 'set':
|
case 'radio':
|
$extra = '( '.implode(', ', $config['options']).' )';
|
break;
|
case 'repeater':
|
$temp = [];
|
foreach ($config['fields'] as $f => $c) {
|
$temp[] = $c['label'];
|
}
|
$extra = '[ '.implode(', ', $temp).' ]';
|
break;
|
}
|
$extra = ($extra === '') ? '' : '<p>'.$extra.'</p>';
|
if (!array_key_exists('label', $config)) {
|
error_log('No label set for: '.print_r($n, true));
|
}
|
?>
|
<th scope="col" data-id="<?=$n?>">
|
<?= $config['label'].$extra ?>
|
</th>
|
<?php
|
}
|
}
|
?>
|
</tr>
|
</thead>
|
<tbody></tbody>
|
<tfoot></tfoot>
|
</table>
|
</template>
|
|
<template class="<?= $type ?>Row">
|
<tr>
|
<td>
|
<?= jvbDashIcon('dots-six-vertical') ?>
|
</td>
|
<td data-id="actions" class="col">
|
<?= jvbRenderToggleTextField(
|
'public',
|
'',
|
'',
|
jvbDashIcon('eye'),
|
jvbDashIcon('eye-closed'))
|
?>
|
<button type="button" data-action="edit">
|
<?= jvbDashIcon('pencil-simple') ?>
|
</button>
|
</td>
|
<?php
|
foreach ($fields as $n => $config) {
|
if (array_key_exists('quickEdit', $config)) {
|
?>
|
<td data-id="<?= $n ?>">
|
<?php
|
$config['type'] = 'text';
|
$config['description'] = '';
|
Form::render($n, null, $config);
|
?>
|
</td>
|
<?php
|
}
|
}
|
?>
|
</tr>
|
</template>
|
|
<?php
|
|
echo jvbNewModal(
|
'edit-modal '.$type,
|
'Edit '.ucfirst($type),
|
jvbRenderForm('admin', $fields)
|
);
|
}
|
return ob_get_clean();
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
* Get all possible dashboard pages regardless of user
|
* Used during dashboard build process
|
* @return array
|
*/
|
protected function getAllDashboardPages():array
|
{
|
$cacheKey = 'all_pages';
|
$pages = $this->cache->get($cacheKey);
|
if ($pages === false || JVB_TESTING) {
|
$pages = [];
|
$pages[] = 'SEO';
|
// Add feature-dependent pages (non-config)
|
if (Site::has('referrals')) {
|
$pages[] = 'Referrals';
|
}
|
$membership = Site::membership();
|
if ($membership && $membership->has('can_invite')) {
|
$pages[] = 'Invites';
|
}
|
|
if ($membership && $membership->has('term_approval')) {
|
$pages[] = 'Approvals';
|
}
|
|
if ($membership && $membership->has('forum')) {
|
$pages[] = 'News';
|
}
|
|
if ($membership && $membership->has('member_content')) {
|
$pages[] = 'Metrics';
|
}
|
|
if (Site::has('favourites')) {
|
$pages[] = 'Favourites';
|
}
|
|
if (!empty(Registrar::getFeatured('karma'))) {
|
$pages[] = 'Karmic Score';
|
}
|
|
if (Site::has('notifications')) {
|
$pages[] = 'Notifications';
|
}
|
|
if (Site::has('support')) {
|
$pages[] = 'Support';
|
}
|
|
if (Site::hasAnyIntegration()) {
|
$pages[] = 'Integrations';
|
}
|
|
// Add all content types (with config keys)
|
foreach (Registrar::getRegistered('post') as $slug) {
|
$registrar = Registrar::getInstance($slug);
|
$pages[$slug] = $registrar->getPlural();
|
}
|
|
foreach (Registrar::getRegistered('term') as $slug) {
|
$registrar = Registrar::getInstance($slug);
|
$pages[$slug] = $registrar->getPlural();
|
}
|
|
// Allow filtering
|
$pages = apply_filters('jvbAllDashboardPages', $pages);
|
|
// Remove duplicates while preserving keys
|
$pages = array_unique($pages);
|
|
// Dash home always first
|
array_unshift($pages, 'dash');
|
|
$this->cache->set($cacheKey, $pages, WEEK_IN_SECONDS);
|
}
|
|
return $pages;
|
}
|
|
|
/**
|
* Get pages that a specific user is allowed to access
|
* Filters based on capabilities and features
|
* @param int|null $userID Optional user ID (defaults to current user)
|
* @return array
|
*/
|
public function getUserAllowedPages(?int $userID = null):array
|
{
|
if ($userID === null) {
|
$user = $this->user;
|
$userID = $user->ID;
|
} else {
|
$user = get_userdata($userID);
|
}
|
|
if (!$user || !$this->userHasDashboardAccess($user)) {
|
return [];
|
}
|
|
|
$pages = $this->cache->get($userID);
|
|
if ($pages === false || JVB_TESTING) {
|
if (user_can($userID, 'manage_options')) {
|
// Admin gets all pages as flat array
|
$pages = $this->getAllDashboardPages();
|
// Extract just the values (slugs)
|
$this->cache->set($userID, $pages, WEEK_IN_SECONDS);
|
return $pages;
|
}
|
$roles = array_map('jvbNoBase', $user->roles);
|
$pages = $this->getAllDashboardPages();
|
|
$canSkip = user_can($userID, 'skip_moderation');
|
foreach($pages as $key => $slug) {
|
//Default to Remove pages
|
$remove = true;
|
if (!is_numeric($key)) {
|
$registrar = Registrar::getInstance($key);
|
$membership = Site::membership();
|
$type = $registrar? $registrar->getType() : false;
|
if ($type) {
|
$permission = JVB()->roles()->getPermission('edit', $key);
|
}
|
switch ($type) {
|
case 'content':
|
if (user_can($userID, $permission)) {
|
$remove = false;
|
}
|
break;
|
case 'taxonomy':
|
$registrar = Registrar::getInstance($key);
|
if ($registrar && $registrar->hasFeature('is_content') && (!empty(JVB()->roles()->getOwnedTerms($userID, $key)) || !empty(JVB()->roles()->getManagedTerms($userID, $key)))){
|
$remove = false;
|
} else if (count(array_intersect($registrar->registrar->for, array_keys($pages))) > 0) {
|
$remove = false;
|
}
|
break;
|
}
|
} else {
|
switch ($slug) {
|
case 'Integrations':
|
foreach($roles as $role) {
|
if (Registrar::getInstance($role)->hasAnyIntegrations()) {
|
$remove = false;
|
}
|
}
|
break;
|
case 'invites':
|
$canInvite = JVB_MEMBERSHIP['can_invite']??[];
|
foreach ($roles as $role) {
|
if (array_key_exists($role, $canInvite)) {
|
$remove = false;
|
}
|
}
|
if ($remove) {
|
//TODO: Figure out what $config was supposed to be
|
if ($canSkip || ($registrar && $registrar->hasFeature('invitable'))) {
|
$remove = false;
|
}
|
}
|
break;
|
case 'Approvals':
|
$canApprove = false;
|
if ($membership && $membership->has('term_approval')) {
|
if (array_key_exists('can_approve', JVB_MEMBERSHIP)) {
|
foreach ($roles as $role) {
|
if (in_array($role, JVB_MEMBERSHIP['can_approve'])) {
|
$canApprove = true;
|
}
|
}
|
} else {
|
//Anyone can approve
|
$canApprove = true;
|
}
|
}
|
if ($canSkip && $canApprove) {
|
$remove = false;
|
}
|
break;
|
case 'news':
|
$canAccess = false;
|
if (array_key_exists('member_only', JVB_MEMBERSHIP)){
|
foreach ($roles as $role) {
|
if (in_array($role, JVB_MEMBERSHIP['member_only'])) {
|
$canAccess = true;
|
}
|
}
|
}
|
if ($canAccess && $canSkip) {
|
$remove = false;
|
}
|
break;
|
case 'metrics':
|
foreach ($roles as $role) {
|
if (!empty(Registrar::getInstance($role)?->getCreatable())) {
|
$remove = false;
|
}
|
}
|
break;
|
case 'karmic-score':
|
foreach ($roles as $role) {
|
$contents = Registrar::getInstance($role)?->getCreatable();
|
if (!empty($contents)) {
|
$hasKarma = Registrar::getFeatured('karma');
|
$remove = empty(array_intersect($contents, $hasKarma));
|
}
|
}
|
break;
|
case 'dash':
|
case 'Referrals':
|
case 'favourites':
|
case 'notifications':
|
case 'support':
|
$remove = false;
|
break;
|
default:
|
break;
|
}
|
}
|
if ($remove) {
|
unset($pages[$key]);
|
}
|
}
|
|
//Allow Filtering
|
$pages = apply_filters('jvbUserDashboardPages', $pages, $user->roles, $userID);
|
$pages = array_unique($pages);
|
|
$this->cache->set($userID, $pages, WEEK_IN_SECONDS);
|
}
|
|
return $pages;
|
}
|
|
/**
|
* Check if user can create content
|
* Replaces jvbUserCanCreate()
|
* @param int $userID
|
* @return bool
|
*/
|
protected function userCanCreate(int $userID = 0):bool
|
{
|
$user = ($userID === 0) ? wp_get_current_user() : get_userdata($userID);
|
$roles = array_intersect(
|
$this->getRolesWithDashboard(),
|
array_map('jvbNoBase', $user->roles)
|
);
|
|
$creatable = [];
|
foreach ($roles as $role) {
|
$roleCreatable = Registrar::getInstance($role)?->getCreatable();
|
$creatable = array_merge($creatable, $roleCreatable);
|
}
|
|
return !empty($creatable);
|
}
|
|
/**
|
* Get user roles that have dashboard access
|
* Replaces jvbRolesWithDashboard()
|
* @return array
|
*/
|
protected function getRolesWithDashboard():array
|
{
|
return Registrar::getFeatured('has_dashboard', 'user');
|
}
|
|
/**
|
* Check if user has dashboard access
|
* @param WP_User $user
|
* @return bool
|
*/
|
protected function userHasDashboardAccess(WP_User $user):bool
|
{
|
if (user_can($user, 'manage_options')) {
|
return true;
|
}
|
|
$userRoles = array_map('jvbNoBase', $user->roles);
|
$dashboardRoles = $this->getRolesWithDashboard();
|
|
return count(array_intersect($dashboardRoles, $userRoles)) > 0;
|
}
|
|
}
|