| | |
| | | <?php |
| | | namespace JVBase\managers; |
| | | |
| | | use JVBase\forms\TaxonomySelector; |
| | | use JVBase\managers\CRUD; |
| | | use JVBase\meta\MetaManager; |
| | | use JVBase\meta\Form; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\utility\Features; |
| | | use JVBase\ui\Navigation; |
| | | use WP_User; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | |
| | | class DashboardManager |
| | | { |
| | | protected WP_User $user; |
| | | protected CacheManager $cache; |
| | | protected Cache $cache; |
| | | protected string $role; |
| | | protected string $baseURL; |
| | | protected int $userLink; |
| | | |
| | | public function __construct() |
| | | { |
| | | $this->cache = new CacheManager('dashboard'); |
| | | $this->cache->invalidateGroup('dashboard'); |
| | | $this->cache = Cache::for('dashboard', WEEK_IN_SECONDS)->connect('user'); |
| | | add_action('init', [$this, 'registerDashboard']); |
| | | if (!$this->isRegistered()) { |
| | | add_action('init', [$this, 'buildDashboard']); |
| | | } |
| | | $this->cache->flush(); |
| | | $this->user = wp_get_current_user(); |
| | | $this->role = jvbUserRole(); |
| | | $this->role = jvbUserRole($this->user->ID); |
| | | $this->userLink = (int)get_user_meta($this->user->ID, BASE.'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); |
| | | |
| | | 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 |
| | |
| | | $singular = 'Dashboard'; |
| | | register_post_type(BASE.'dash', array( |
| | | 'labels' => [ |
| | | 'name' => $plural, |
| | | 'singular_name' => $singular, |
| | | 'menu_name' => $plural, |
| | | 'add_new' => "Add New {$singular}", |
| | | 'add_new_item' => "Add New {$singular}", |
| | | 'edit_item' => "Edit {$singular}", |
| | | 'new_item' => "New {$singular}", |
| | | 'view_item' => "View {$singular}", |
| | | 'search_items' => "Search {$plural}", |
| | | 'not_found' => "No {$plural} found", |
| | | 'not_found_in_trash' => "No {$plural} found in Trash" |
| | | ], |
| | | '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, |
| | |
| | | */ |
| | | 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 |
| | | wp_redirect(home_url('dash')); |
| | | exit; |
| | | 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):array |
| | | { |
| | | $pages = $this->getAllDashboardPages(); |
| | | $key = array_search($page, $pages); |
| | | if ($key === false || is_numeric($key)) { |
| | | return []; |
| | | } |
| | | return Features::getConfig($key); |
| | | } |
| | | |
| | | /** |
| | | * 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 |
| | | { |
| | | $config = $this->getConfig($slug); |
| | | if (!empty($config)) { |
| | | return $config['dash_title']??$config['plural']; |
| | | } |
| | | 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 ar created |
| | | * Ensures the necessary pages are created |
| | | * @return void |
| | | */ |
| | | public function buildDashboard():void |
| | | { |
| | | $manageableContent = jvbGetAllDashboardPages(); |
| | | foreach ($manageableContent as $slug) { |
| | | $title = $this->getTitle($slug); |
| | | $slug = sanitize_title($title); |
| | | $manageableContent = $this->getAllDashboardPages(); |
| | | foreach ($manageableContent as $slug => $page) { |
| | | if ($page === 'dash') { |
| | | continue; |
| | | } |
| | | $slug = $this->getSlug($slug, $page); |
| | | $existing = get_page_by_path($slug, OBJECT, BASE.'dash'); |
| | | if ($existing) { |
| | | continue; |
| | | } |
| | | $title = $page; |
| | | |
| | | $ID = wp_insert_post(array( |
| | | 'post_title' => $title, |
| | |
| | | )); |
| | | |
| | | if ($title === 'Integrations') { |
| | | $integrations = ['BlueSky', 'Cloudflare', 'Facebook', 'Google Maps', 'Google My Business', 'Helcim', 'Instagram', 'Square', 'Umami']; |
| | | foreach ($integrations as $integration) { |
| | | $slug = sanitize_title($integration); |
| | | wp_insert_post([ |
| | | 'post_title' => $integration, |
| | | 'post_name' => $slug, |
| | | 'post_type' => BASE.'dash', |
| | | 'post_status' => 'publish', |
| | | 'post_parent' => $ID |
| | | ]); |
| | | } |
| | | $this->buildIntegrationPages($ID); |
| | | } |
| | | } |
| | | update_option(BASE.'dashboard_registered', true); |
| | | remove_action('init', [$this, 'buildDashboard']); |
| | | } |
| | | |
| | | protected function getAllDashboardPages():array |
| | | /** |
| | | * Build integration sub-pages |
| | | * @param int $parentID |
| | | * @return void |
| | | */ |
| | | protected function buildIntegrationPages(int $parentID):void |
| | | { |
| | | $manageableContent = get_option(BASE.'all_dashboard_pages'); |
| | | if (JVB_TESTING) { |
| | | $manageableContent = false; |
| | | $integrations = JVB()->getAvailableServices(false); |
| | | |
| | | foreach ($integrations as $name => $integration) { |
| | | $title = $integration->getTitle(); |
| | | $slug = sanitize_title($title); |
| | | |
| | | // Check if integration page already exists |
| | | $existing = get_posts([ |
| | | 'post_type' => BASE.'dash', |
| | | 'name' => $slug, |
| | | 'post_parent' => $parentID, |
| | | 'posts_per_page' => 1, |
| | | ]); |
| | | |
| | | if (!empty($existing)) { |
| | | continue; // Skip if exists |
| | | } |
| | | |
| | | wp_insert_post([ |
| | | 'post_title' => $title, |
| | | 'post_name' => $slug, |
| | | 'post_type' => BASE.'dash', |
| | | 'post_status' => 'publish', |
| | | 'post_parent' => $parentID |
| | | ]); |
| | | } |
| | | if ($manageableContent === false) { |
| | | |
| | | $manageableContent = []; |
| | | $bios = []; |
| | | foreach (JVB_USER as $role => $config) { |
| | | $manageableContent = array_merge($manageableContent, jvbRolePages($role)); |
| | | } |
| | | |
| | | if (Features::forSite()->has('referrals')) { |
| | | $manageableContent[] = 'referrals'; |
| | | } |
| | | foreach (JVB_TAXONOMY as $tax => $config) { |
| | | if (Features::forTaxonomy($tax)->has('is_content')) { |
| | | $manageableContent[] = strtolower($config['plural']); |
| | | } |
| | | } |
| | | if (Features::forMembership()->has('can_invite')) { |
| | | $manageableContent[] = 'invites'; |
| | | } |
| | | |
| | | if (Features::forMembership()->has('term_approval')) { |
| | | $manageableContent[] = 'approvals'; |
| | | } |
| | | |
| | | if (Features::forMembership()->has('forum')) { |
| | | $manageableContent[] = 'news'; |
| | | } |
| | | |
| | | if (Features::forMembership()->has('member_content')) { |
| | | $manageableContent[] = 'metrics'; |
| | | } |
| | | |
| | | if (!empty($bios)) { |
| | | $manageableContent[] = 'bio'; |
| | | } |
| | | |
| | | if (Features::forSite()->has('favourites')) { |
| | | $manageableContent[] = 'favourites'; |
| | | } |
| | | |
| | | if (Features::anyContentHas('karma') || Features::anyTaxonomyHas('karma') || Features::anyUserHas('karma')){ |
| | | $manageableContent[] = 'karmic-score'; |
| | | } |
| | | |
| | | if (Features::forSite()->has('notifications')) { |
| | | $manageableContent[] = 'notifications'; |
| | | } |
| | | |
| | | if (Features::forSite()->has('support')){ |
| | | $manageableContent[] = 'support'; |
| | | } |
| | | |
| | | if (Features::hasAnyIntegration()) { |
| | | $manageableContent[] = 'integrations'; |
| | | } |
| | | |
| | | $manageableContent[] = 'admin'; |
| | | $manageableContent = apply_filters('jvbDashboardPages', $manageableContent); |
| | | $manageableContent = array_unique($manageableContent); |
| | | sort($manageableContent); |
| | | $manageableContent = array_map(function ($content) { |
| | | return str_replace('_', '-', $content); |
| | | }, $manageableContent); |
| | | update_option(BASE.'all_dashboard_pages', $manageableContent); |
| | | } |
| | | |
| | | |
| | | return $manageableContent; |
| | | } |
| | | |
| | | protected function getRolePages(string $role):array |
| | | { |
| | | if (!array_key_exists(jvbNoBase($role), JVB_USER)) { |
| | | return []; |
| | | } |
| | | $manageableContent = get_option(BASE.$role.'_pages'); |
| | | if (JVB_TESTING) { |
| | | $manageableContent = false; |
| | | } |
| | | if ($manageableContent === false) { |
| | | $manageableContent = []; |
| | | $config = JVB_USER[$role]; |
| | | $content = $config['can_create']; |
| | | $settings = $bio = false; |
| | | if (array_key_exists('profile', $config)) { |
| | | $manageableContent[] = $config['profile']; |
| | | } |
| | | |
| | | foreach ($content as $c) { |
| | | if (is_array($c)) { |
| | | foreach ($c as $type => $contents) { |
| | | $manageableContent = array_merge($manageableContent, $contents); |
| | | } |
| | | } else { |
| | | $manageableContent = array_merge($manageableContent, [$c]); |
| | | } |
| | | } |
| | | |
| | | if (array_key_exists('has_dashboard', $config)) { |
| | | $manageableContent[] = 'settings'; |
| | | } |
| | | |
| | | update_option(BASE.$role.'_pages', $manageableContent); |
| | | } |
| | | |
| | | return $manageableContent; |
| | | } |
| | | |
| | | protected function getTitle(string $page):string |
| | | { |
| | | $content = JVB_CONTENT; |
| | | $contentTax = array_filter(JVB_TAXONOMY, function ($tax) { |
| | | return jvbCheck('is_content', $tax); |
| | | }); |
| | | $content = array_merge($content, $contentTax); |
| | | $title = ''; |
| | | |
| | | |
| | | if (array_key_exists($page, $content)) { |
| | | $config = $content[$page]; |
| | | $title = (array_key_exists('dash_title', $config)) ? $config['dash_title'] : $config['plural']; |
| | | } else { |
| | | switch ($page) { |
| | | case 'admin': |
| | | $title = 'Admin'; |
| | | break; |
| | | default: |
| | | $title = ucwords(str_replace('_', ' ', str_replace('-', ' ', $page))); |
| | | } |
| | | } |
| | | |
| | | return $title; |
| | | } |
| | | |
| | | protected function getDescription(string $page):string |
| | | { |
| | | $content = JVB_CONTENT; |
| | | $contentTax = array_filter(JVB_TAXONOMY, function ($tax) { |
| | | return jvbCheck('is_content', $tax); |
| | | }); |
| | | $content = array_merge($content, $contentTax); |
| | | if (array_key_exists($page, $content)) { |
| | | $config = $content[$page]; |
| | | $config = $this->getConfig($page); |
| | | if (!empty($config)) { |
| | | $description = (array_key_exists('dash_description', $config)) ? $config['dash_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!'; |
| | |
| | | if (!is_singular(BASE.'dash') && !is_post_type_archive(BASE.'dash')) { |
| | | return $template; |
| | | } |
| | | if (!isOurPeople() && !current_user_can('manage_options')) { |
| | | error_log('Redirecting because:'); |
| | | if (!isOurPeople()) { |
| | | error_log('Not our people'); |
| | | } |
| | | if (!current_user_can('manage_options')) { |
| | | error_log('Cannot manage options'); |
| | | } |
| | | wp_redirect(wp_login_url(get_home_url(null, '/dash'))); |
| | | exit; |
| | | } |
| | | |
| | | // Get current page/section |
| | | |
| | | $page = $this->getCurrentPage(); |
| | | |
| | | switch ($page) { |
| | | case 'integrations': |
| | | if (!Features::hasAnyIntegration('user', $this->role)) { |
| | | wp_redirect(get_home_url(null, '/dash')); |
| | | exit; |
| | | } |
| | | break; |
| | | case 'bluesky': |
| | | case 'cloudflare': |
| | | case 'facebook': |
| | | case 'google-maps': |
| | | case 'google-my-business': |
| | | case 'helcim': |
| | | case 'instagram': |
| | | case 'square': |
| | | case 'umami': |
| | | if (!Features::hasIntegration($page,'user', $this->role)) { |
| | | wp_redirect(get_home_url(null, '/dash')); |
| | | exit; |
| | | } |
| | | break; |
| | | case 'bio': |
| | | $permission = JVB_USER[$this->role]['profile']??false; |
| | | if (!$permission || (!current_user_can('manage_'.$permission) && !current_user_can('manage_options'))) { |
| | | wp_redirect(get_home_url(null, '/dash')); |
| | | exit; |
| | | } |
| | | break; |
| | | case 'settings': |
| | | if (!current_user_can('manage_settings') && !current_user_can('manage_options')) { |
| | | wp_redirect(get_home_url(null, '/dash')); |
| | | exit; |
| | | } |
| | | break; |
| | | case 'approval': |
| | | if (!current_user_can('skip_moderation')) { |
| | | wp_redirect(get_home_url(null, '/dash')); |
| | | exit; |
| | | } |
| | | break; |
| | | case 'dash': |
| | | |
| | | break; |
| | | default: |
| | | $type = match($page) { |
| | | 'menu-item' => 'menu_item', |
| | | 'events' => 'event', |
| | | default => $page |
| | | }; |
| | | |
| | | $permission = strtolower(str_replace(' ', '_',JVB_CONTENT[$type]['plural']??$type.'s')); |
| | | |
| | | if (!current_user_can('edit_'.$permission)) { |
| | | error_log('User cannot edit: '.$permission); |
| | | wp_redirect(get_home_url(null, '/dash')); |
| | | exit; |
| | | } |
| | | break; |
| | | $page = $this->getCurrentPageTitle(); |
| | | $config = $this->getConfig($page); |
| | | if(!empty($config)) { |
| | | add_filter('jvbLoadingIcon', function() use ($config) { |
| | | return $config['icon']; |
| | | }); |
| | | } |
| | | // Enqueue needed styles/scripts |
| | | $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->cache->delete($page); |
| | | echo $this->cache->remember( |
| | | $this->renderHeader(); |
| | | // Pass to page handler |
| | | $constantSlug = $this->getConstantSlug($page); |
| | | echo apply_filters( |
| | | 'jvbDashboardPage', |
| | | $this->renderPage($page), |
| | | $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(); |
| | | } |
| | | $constantSlug |
| | | ); |
| | | |
| | | // Return empty string to prevent default template |
| | | return ''; |
| | | } |
| | | $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 (!Features::hasAnyIntegration('user', $this->role)) { |
| | | // $this->redirectToDashboard(); |
| | | // } |
| | | // } else { |
| | | // if (!Features::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 |
| | |
| | | */ |
| | | public function dashboardScripts():void |
| | | { |
| | | if (is_post_type_archive(BASE.'dash') || is_singular(BASE.'dash')) { |
| | | 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'); |
| | | |
| | | // wp_enqueue_style('quill-css', 'https://cdn.quilljs.com/1.3.6/quill.snow.css'); |
| | | |
| | | |
| | | wp_enqueue_script('jvb-loading'); |
| | | wp_enqueue_script('jvb-form'); |
| | | |
| | | |
| | | // Consolidate all dashboard settings |
| | | wp_localize_script('jvb-loading', 'dashboardSettings', array( |
| | | 'loadingMessages' => array( |
| | | 'default' => 'Loading...', |
| | | 'error' => 'Failed to load page' |
| | | ), |
| | | 'strings' => array( |
| | | 'deleteConfirm' => 'Are you sure you want to delete this item?', |
| | | 'bulkDeleteConfirm' => 'Are you sure you want to delete these items?', |
| | | 'deleteSuccess' => 'Item(s) deleted successfully', |
| | | 'deleteError' => 'Error deleting item(s)', |
| | | 'saveSuccess' => 'Changes saved successfully', |
| | | 'saveError' => 'Error saving changes', |
| | | 'loadError' => 'Error loading content' |
| | | ), |
| | | 'currentUser' => array( |
| | | 'id' => $this->user->ID, |
| | | 'name' => $this->user->display_name, |
| | | 'role' => array_values($this->user->roles)[0] ?? '', |
| | | 'type' => str_replace(BASE, '', array_values($this->user->roles)[0]), |
| | | 'city' => '', // Add if needed, |
| | | 'artistID' => $this->userLink, |
| | | ) |
| | | )); |
| | | |
| | | wp_enqueue_script('jvb-selector'); |
| | | wp_enqueue_script('jvb-uploader'); |
| | | wp_enqueue_script('jvb-content'); |
| | | wp_enqueue_script('jvb-crud'); |
| | | // wp_enqueue_script('jvb-dashboard-navigator'); |
| | | |
| | | $page = $this->getCurrentPage(); |
| | | $page = $this->getCurrentPageSlug(); |
| | | |
| | | switch ($page) { |
| | | case 'notifications': |
| | | if (jvbSiteHasNotifications()) { |
| | | if (Features::forSite()->has('notifications')) { |
| | | wp_enqueue_script('jvb-notification-manager'); |
| | | } |
| | | break; |
| | |
| | | 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 |
| | | ] |
| | | ); |
| | | // 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') |
| | | ] |
| | | ); |
| | | } |
| | | // 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 (jvbSiteHasFavourites()) { |
| | | if (Features::forSite()->has('favourites')) { |
| | | wp_enqueue_script('jvb-favourites'); |
| | | wp_localize_script('jvb-favourites-manager', 'favouritesSettings', [ |
| | | 'strings' => [ |
| | |
| | | |
| | | wp_enqueue_script('jvb-creator'); |
| | | |
| | | if (jvbSiteHasForum()) { |
| | | if (Features::forSite()->has('forum')) { |
| | | wp_enqueue_script('jvb-news'); |
| | | } |
| | | do_action('jvbDashScripts', $page); |
| | | } |
| | | } |
| | | |
| | | protected function getCurrentPage():string |
| | | protected function getCurrentPageTitle():string |
| | | { |
| | | global $wp; |
| | | $dash = str_replace('dash/', '', $wp->request); |
| | | if (str_starts_with($dash, 'integrations/')) { |
| | | $dash = str_replace('integrations/', '', $dash); |
| | | if (is_post_type_archive(BASE.'dash')) { |
| | | return 'dash'; |
| | | } |
| | | |
| | | return ($dash === '') ? 'dash' : $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)) { |
| | | $config = Features::getConfig($slug); |
| | | if (array_key_exists('icon', $config)) { |
| | | $icon = $config['icon']; |
| | | } |
| | | } |
| | | 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 |
| | | { |
| | |
| | | <meta charset="<?php bloginfo('charset'); ?>"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | | <?php |
| | | $pages = jvbGetUserDashboardPages(); |
| | | $pages = $this->getUserAllowedPages(); |
| | | |
| | | foreach($pages as $page) { |
| | | $page = str_replace('_', '-', $page); |
| | | $link = ($page === 'dash') ? '/'.$page : "/dash/$page"; |
| | |
| | | <link rel="preconnect" href="<?= get_home_url()?>"/> |
| | | <?php wp_head(); ?> |
| | | </head> |
| | | <body class="dashboard<?= ' '.$this->getCurrentPage()?>"> |
| | | <body class="dashboard<?= ' '.$this->getCurrentPageSlug()?>"> |
| | | <?php jvbAccessibility();?> |
| | | <header> |
| | | <?php |
| | | $checked = (is_user_logged_in() && current_user_can('prefers_dark_theme', true)) ? ' checked' : ''; |
| | | $title = ($checked == '') ? 'Toggle Dark Mode' : 'Toggle Light Mode'; |
| | | echo '<label title="'.$title.'" id="theme-switch" class="toggle-switch" for="theme-switcher"> |
| | | <input class="theme-switch row" id="theme-switcher" type="checkbox"'.$checked.' role="switch" name="dark-mode"><span class="slider">'. |
| | | jvbIcon('light'). |
| | | jvbIcon('dark').'</span></label>'; |
| | | <input class="theme-switch row" id="theme-switcher" name="theme-switcher" type="checkbox"'.$checked.' data-setting="theme" data-theme role="switch" name="dark-mode" aria-label="Toggle dark mode"><span class="slider">'. |
| | | jvbIcon('sun-dim', ['title'=> 'Light Mode']). |
| | | jvbIcon('moon', ['title'=>'Dark Mode']). |
| | | '</span></label>'; |
| | | ?> |
| | | <p class="title"> |
| | | <a href="<?= get_home_url(); ?>" rel="home" title="Back to Site"> |
| | | <?= jvbIcon('logo-basic'); ?> |
| | | <?php |
| | | $icon = (int) get_option( 'site_icon' ); |
| | | $out = ''; |
| | | if ($icon > 0) { |
| | | $url = wp_get_attachment_image_url( $icon); |
| | | if ($url) { |
| | | $out = '<img src="'.$url.'">'; |
| | | } |
| | | } |
| | | if ($out == '') { |
| | | $out =jvbIcon('house'); |
| | | } |
| | | ?><?= $out ?> |
| | | </a> |
| | | </p> |
| | | |
| | |
| | | <ul> |
| | | <?= jvbNotificationMenu() ?> |
| | | <?= jvbHelpMenu() ?> |
| | | <li><a href="<?=wp_logout_url(get_home_url())?>" title="Logout"><?=jvbIcon('sign-out')?></a></li> |
| | | </ul> |
| | | </nav> |
| | | </header> |
| | |
| | | { |
| | | ?> |
| | | </section> |
| | | |
| | | <?php |
| | | $menu = new Navigation('sidebar'); |
| | | $menuClasses = ['col', 'a-start', 'nowrap']; |
| | | $itemClasses = ['col']; |
| | | $menu->addClass('col a-start')->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 |
| | | //Taxonomies |
| | | $availableContent = array_filter($pages, function($page, $key) { |
| | | return !is_numeric($key) && array_key_exists($key, JVB_CONTENT); |
| | | }, 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) { |
| | | $config = JVB_CONTENT[$slug]; |
| | | $item = $content->addItem($page, jvbDashIcon($config['icon'])) |
| | | ->url($this->baseURL.'/'.$slug); |
| | | |
| | | $taxonomies = array_filter(JVB_TAXONOMY, function ($value, $key) use ($slug) { |
| | | return in_array($slug, $value['for_content']); |
| | | },1); |
| | | 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 => $config) { |
| | | $itemMenu->addItem($config['plural'], $config['icon']) |
| | | ->url($this->baseURL.'/'.$s); |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | //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"> |
| | | <nav class="dashboard-nav"> |
| | | <?= jvbLoadingScreen() ?> |
| | | <?= TaxonomySelector::outputSelectorModal() ?> |
| | | <!-- <nav class="dashboard-nav">--> |
| | | <?php |
| | | $current_page = $this->getCurrentPage(); |
| | | $pages = jvbGetUserDashboardPages()?:[]; |
| | | |
| | | global $jvb_everything; |
| | | echo '<ul>'; |
| | | foreach ($pages as $page) { |
| | | // Add data-page attribute for the navigator |
| | | $active = ($current_page == $page) ? ' class="current"' : ''; |
| | | $current = ($current_page == $page) ? ' aria-current="page"' : ''; |
| | | $icon = (array_key_exists($page, $jvb_everything)) ? $jvb_everything[$page]['icon'] ?? $page : $page; |
| | | |
| | | $title = $this->getTitle($page); |
| | | $page = str_replace('_', '-', $page); |
| | | |
| | | $link = ($page === 'dash') ? '/'.$page : "/dash/$page"; |
| | | |
| | | 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, |
| | | $page, |
| | | $title, |
| | | jvbIcon($icon, ['title'=> $title]), |
| | | $title |
| | | ); |
| | | } |
| | | |
| | | echo '</ul>'; |
| | | // $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> |
| | | <!-- </nav>--> |
| | | </footer> |
| | | |
| | | |
| | | <?php |
| | | do_action('jvbRenderDashboardSettings', $this->getCurrentPage()); |
| | | do_action('jvbRenderDashboardSettings', $this->getCurrentPageSlug()); |
| | | ?> |
| | | <?php wp_footer(); ?> |
| | | |
| | |
| | | <?php |
| | | } |
| | | |
| | | protected function renderIndex():void |
| | | public function renderIndex(string $content, string $page):string |
| | | { |
| | | $name = get_post_meta($this->userLink, BASE.'firstname', true); |
| | | $name = ($name === '') ? $this->user->display_name : $name; |
| | | if ($page !== '' && $page !== 'dash') { |
| | | return $content; |
| | | } |
| | | |
| | | if (Features::forSite()->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 style="text-transform:none;margin-top:2em!important;">Hey '.$name.'</h1>'; |
| | | echo '<p>Welcome back!</p>'; |
| | | |
| | | $pages = jvbGetUserDashboardPages(); |
| | | |
| | | $pages = $this->getUserAllowedPages(); |
| | | |
| | | echo '<h2>What would you like to do today?</h2>'; |
| | | |
| | | global $jvb_everything; |
| | | echo '<ul>'; |
| | | foreach ($pages as $page) { |
| | | |
| | | echo '<ul class="dashboard">'; |
| | | foreach ($pages as $slug => $page) { |
| | | if ($page === 'dash') { |
| | | continue; |
| | | } |
| | | $title = $this->getTitle($page); |
| | | $url = sanitize_title($title); |
| | | |
| | | $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/'.$url.'/').'" |
| | | data-page="'.$url.'" data-dash>'.jvbIcon($page).ucfirst($title).'</a></p>'.$description.'</li>'; |
| | | 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 |
| | |
| | | if ($content !== '') { |
| | | echo $content; |
| | | } else { |
| | | jvbRenderSections($this->user->ID, 'user', jvbUserRole()); |
| | | jvbRenderSections($this->user->ID, 'user', jvbUserRole()); |
| | | } |
| | | |
| | | } |
| | |
| | | $out = '<nav class="integrations"><ul>'; |
| | | |
| | | $url = get_home_url(null, '/dash/integrations/'); |
| | | $out .= '<li><a href="'.$url.'">'.jvbIcon('plugs-connected').'Integrations</a></li>'; |
| | | $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.'">'.jvbIcon($integration->icon).$integration->getTitle().'</a></li>'; |
| | | $out .= '<li><a href="'.$url.$link.'">'.jvbDashIcon($integration->icon).$integration->getTitle().'</a></li>'; |
| | | } |
| | | $out .= '</ul></nav>'; |
| | | } |
| | |
| | | <div class="approvals container"> |
| | | <nav class="tabs row start" role="tablist"> |
| | | <button type="button" class="tab active" data-tab="summary" role="tab" aria-selected="true"> |
| | | <h2><?= jvbIcon('all')?>All</h2> |
| | | <h2><?= jvbDashIcon('infinity')?>All</h2> |
| | | </button> |
| | | <button type="button" class="tab" data-tab="artists" role="tab" aria-selected="false"> |
| | | <h2><?= jvbIcon('artists')?>Artists</h2> |
| | | <h2><?= jvbDashIcon('users-three')?>Artists</h2> |
| | | </button> |
| | | <button type="button" class="tab" data-tab="terms" role="tab" aria-selected="false"> |
| | | <h2><?= jvbIcon('style')?>Terms</h2> |
| | | <h2><?= jvbDashIcon('hash')?>Terms</h2> |
| | | </button> |
| | | <button type="button" class="tab" data-tab="yours" role="tab" aria-selected="false"> |
| | | <h2><?= jvbIcon('artist')?>Yours</h2> |
| | | <h2><?= jvbDashIcon('user')?>Yours</h2> |
| | | </button> |
| | | </nav> |
| | | </div> |
| | |
| | | |
| | | } |
| | | |
| | | protected function renderCRUD(string $type):void |
| | | { |
| | | $type = match($type) { |
| | | 'menu-item' => 'menu_item', |
| | | 'events' => 'event', |
| | | default => $type |
| | | }; |
| | | $crud = new CRUD($type); |
| | | $crud->render(); |
| | | |
| | | } |
| | | |
| | | protected function renderAdmin():void |
| | | public function renderAdmin(string $content, string $page):string |
| | | { |
| | | if ($page !== '' && $page !== 'dash') { |
| | | return $content; |
| | | } |
| | | ob_start(); |
| | | ?> |
| | | <nav class="tabs row start" role="tablist"> |
| | | <?php |
| | |
| | | $active = ($i === 1) ? ' active' : ''; |
| | | ?> |
| | | <button type="button" class="tab<?=$active?>" data-tab="<?=$type?>" role="tab" aria-selected="<?= ($active !== '') ? 'true' : 'false'?>"> |
| | | <h2><?=jvbIcon($type)?> <?= $settings['plural'] ?></h2> |
| | | <h2><?=jvbDashIcon($settings['icon']??$key)?> <?= $settings['plural'] ?></h2> |
| | | </button> |
| | | <?php |
| | | $i++; |
| | |
| | | 'vertical', |
| | | 'TAB NAV:', |
| | | '', |
| | | jvbIcon('down'), |
| | | jvbIcon('right'))?> |
| | | jvbDashIcon('caret-double-down'), |
| | | jvbDashIcon('caret-double-right'))?> |
| | | |
| | | </div> |
| | | <div class="items-container"> |
| | | </div> |
| | | |
| | | <?php |
| | | global $jvb_everything; |
| | | |
| | | $jvb_everything = array_merge(JVB_CONTENT, JVB_TAXONOMY); |
| | | |
| | | foreach ($jvb_everything as $type => $settings) { |
| | | $meta = new MetaManager(null, 'form'); |
| | | $fields = jvbGetFields($type); |
| | | ?> |
| | | <template class="<?= $type ?>Table"> |
| | |
| | | <template class="<?= $type ?>Row"> |
| | | <tr> |
| | | <td> |
| | | <?= jvbIcon('grab') ?> |
| | | <?= jvbDashIcon('dots-six-vertical') ?> |
| | | </td> |
| | | <td data-id="actions" class="col"> |
| | | <?= jvbRenderToggleTextField( |
| | | 'public', |
| | | '', |
| | | '', |
| | | jvbIcon('show'), |
| | | jvbIcon('hide')) |
| | | jvbDashIcon('eye'), |
| | | jvbDashIcon('eye-closed')) |
| | | ?> |
| | | <button type="button" data-action="edit"> |
| | | <?= jvbIcon('edit') ?> |
| | | <?= jvbDashIcon('pencil-simple') ?> |
| | | </button> |
| | | </td> |
| | | <?php |
| | |
| | | <?php |
| | | $config['type'] = 'text'; |
| | | $config['description'] = ''; |
| | | $meta->render('form', $n, $config); |
| | | Form::render($n, null, $config); |
| | | ?> |
| | | </td> |
| | | <?php |
| | |
| | | echo jvbNewModal( |
| | | 'edit-modal '.$type, |
| | | 'Edit '.ucfirst($type), |
| | | $meta->renderForm('admin', [], $fields) |
| | | 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 (Features::forSite()->has('referrals')) { |
| | | $pages[] = 'Referrals'; |
| | | } |
| | | |
| | | if (Features::forMembership()->has('can_invite')) { |
| | | $pages[] = 'Invites'; |
| | | } |
| | | |
| | | if (Features::forMembership()->has('term_approval')) { |
| | | $pages[] = 'Approvals'; |
| | | } |
| | | |
| | | if (Features::forMembership()->has('forum')) { |
| | | $pages[] = 'News'; |
| | | } |
| | | |
| | | if (Features::forMembership()->has('member_content')) { |
| | | $pages[] = 'Metrics'; |
| | | } |
| | | |
| | | if (Features::forSite()->has('favourites')) { |
| | | $pages[] = 'Favourites'; |
| | | } |
| | | |
| | | if (Features::anyContentHas('karma') || Features::anyTaxonomyHas('karma') || Features::anyUserHas('karma')) { |
| | | $pages[] = 'Karmic Score'; |
| | | } |
| | | |
| | | if (Features::forSite()->has('notifications')) { |
| | | $pages[] = 'Notifications'; |
| | | } |
| | | |
| | | if (Features::forSite()->has('support')) { |
| | | $pages[] = 'Support'; |
| | | } |
| | | |
| | | if (Features::hasAnyIntegration()) { |
| | | $pages[] = 'Integrations'; |
| | | } |
| | | |
| | | // Add all content types (with config keys) |
| | | foreach (JVB_CONTENT as $slug => $config) { |
| | | $pages[$slug] = $config['plural']; |
| | | } |
| | | |
| | | foreach (JVB_TAXONOMY as $slug=>$config) { |
| | | $pages[$slug] = $config['plural']; |
| | | } |
| | | |
| | | // 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 available to a specific role |
| | | * @param string $role The role slug (with or without BASE prefix) |
| | | * @return array |
| | | */ |
| | | protected function getRolePages(string $role):array |
| | | { |
| | | $role = jvbNoBase($role); |
| | | |
| | | if (!array_key_exists($role, JVB_USER)) { |
| | | return []; |
| | | } |
| | | |
| | | return Features::forUser($role)->getDashboardPages(); |
| | | } |
| | | |
| | | /** |
| | | * 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)) { |
| | | $type = Features::getType($key); |
| | | if ($type) { |
| | | $permission = RoleManager::getPlural($key); |
| | | } |
| | | switch ($type) { |
| | | case 'content': |
| | | if (user_can($userID, "edit_{$permission}")) { |
| | | $remove = false; |
| | | } |
| | | break; |
| | | case 'taxonomy': |
| | | $config = Features::getConfig($key, 'taxonomy'); |
| | | if (array_key_exists('is_content', $config) && $config['is_content'] && (user_can($userID, "own_{$key}") || user_can($userID, "manage_{$key}"))) { |
| | | $remove = false; |
| | | } else if (count(array_intersect($config['for_content'], array_keys($pages))) > 0) { |
| | | $remove = false; |
| | | } |
| | | break; |
| | | } |
| | | } else { |
| | | switch ($slug) { |
| | | case 'Integrations': |
| | | foreach($roles as $role) { |
| | | if (Features::hasAnyIntegration('user', $role)) { |
| | | $remove = false; |
| | | } |
| | | } |
| | | break; |
| | | case 'invites': |
| | | $canInvite = JVB_MEMBERSHIP['can_invite']??[]; |
| | | foreach ($roles as $role) { |
| | | if (array_key_exists($role, $canInvite)) { |
| | | $remove = false; |
| | | } |
| | | } |
| | | if ($remove) { |
| | | if ($canSkip || array_key_exists('invitable', $config)) { |
| | | $remove = false; |
| | | } |
| | | } |
| | | break; |
| | | case 'Approvals': |
| | | $canApprove = false; |
| | | if (Features::forMembership()->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(Features::forUser($role)->getCreatableContent())) { |
| | | $remove = false; |
| | | } |
| | | } |
| | | break; |
| | | case 'karmic-score': |
| | | foreach ($roles as $role) { |
| | | $contents = Features::forUser($role)->getCreatableContent(); |
| | | if (!empty($contents)) { |
| | | foreach($contents as $content) { |
| | | if (Features::forContent($content)->has('karma')) { |
| | | $remove = false; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | 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 = Features::forUser($role)->getCreatableContent(); |
| | | $creatable = array_merge($creatable, $roleCreatable); |
| | | } |
| | | |
| | | return !empty($creatable); |
| | | } |
| | | |
| | | /** |
| | | * Get user roles that have dashboard access |
| | | * Replaces jvbRolesWithDashboard() |
| | | * @return array |
| | | */ |
| | | protected function getRolesWithDashboard():array |
| | | { |
| | | return array_keys(array_filter(JVB_USER, function ($role) { |
| | | return Features::forUser(array_search($role, JVB_USER))->has('has_dashboard'); |
| | | })); |
| | | } |
| | | |
| | | /** |
| | | * 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; |
| | | } |
| | | |
| | | /** |
| | | * Get the capability needed to access a content type |
| | | * @param string $type |
| | | * @return string |
| | | */ |
| | | protected function getPermissionForType(string $type):string |
| | | { |
| | | // Check if it's a registered content type |
| | | if (array_key_exists($type, JVB_CONTENT)) { |
| | | $plural = JVB_CONTENT[$type]['plural']; |
| | | return 'edit_'.$plural; |
| | | } |
| | | |
| | | // Default to edit_{type}s |
| | | return 'edit_'.$type.'s'; |
| | | } |
| | | } |