cache = new CacheManager('dashboard');
$this->cache->invalidateGroup('dashboard');
add_action('init', [$this, 'registerDashboard']);
if (!$this->isRegistered()) {
add_action('init', [$this, 'buildDashboard']);
}
$this->user = wp_get_current_user();
$this->role = jvbUserRole();
$this->userLink = (int)get_user_meta($this->user->ID, BASE.'link', true);
add_action('template_include', [$this, 'dashboardTemplates']);
add_action('admin_init', [$this, 'redirectFromAdmin']);
add_action('wp_enqueue_scripts', [$this, 'dashboardScripts'], 50);
}
/**
* 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,
'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"
],
'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()
{
// 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;
}
/**
* Ensures the necessary pages ar created
* @return void
*/
public function buildDashboard():void
{
$manageableContent = jvbGetAllDashboardPages();
foreach ($manageableContent as $slug) {
$title = $this->getTitle($slug);
$slug = sanitize_title($title);
$ID = wp_insert_post(array(
'post_title' => $title,
'post_name' => $slug,
'post_type' => BASE.'dash',
'post_status' => 'publish',
));
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
]);
}
}
}
update_option(BASE.'dashboard_registered', true);
remove_action('init', [$this, 'buildDashboard']);
}
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];
$description = (array_key_exists('dash_description', $config)) ? $config['dash_description'] : '';
} else {
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. (coming soon) ';
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 825-823-9916 ';
break;
case 'settings':
$description = 'Control your privacy and email frequency.';
break;
}
}
return $description;
}
/**
* Checks if we've already created the need pages
* @return bool
*/
protected function isRegistered():bool
{
return get_option(BASE.'dashboard_registered', false);
}
/**
* 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;
}
if (!isOurPeople() && !current_user_can('manage_options')) {
wp_redirect(wp_login_url(get_home_url(2, '/dash')));
exit;
}
// Get current page/section
$page = $this->getCurrentPage();
// Enqueue needed styles/scripts
jvbInlineStyles('nav');
jvbInlineStyles('dash');
jvbInlineStyles('forms');
$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 '';
}
/**
* Enqueues necessary scripts
* @return void
*/
public function dashboardScripts():void
{
if (is_post_type_archive(BASE.'dash') || is_singular(BASE.'dash')) {
// 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();
switch ($page) {
case 'notifications':
if (jvbSiteHasNotifications()) {
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;
}
if (jvbSiteHasFavourites()) {
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 (jvbSiteHasForum()) {
wp_enqueue_script('jvb-news');
}
do_action('jvbDashScripts', $page);
}
}
protected function getCurrentPage():string
{
global $wp;
$dash = str_replace('dash/', '', $wp->request);
if (str_starts_with($dash, 'integrations/')) {
$dash = str_replace('integrations/', '', $dash);
}
return ($dash === '') ? 'dash' : $dash;
}
protected function renderHeader():void
{
?>
>
= (array_key_exists('dashboard_title', JVB_SITE)) ? JVB_SITE['dashboard_title'] : 'Dashboard | '.get_bloginfo('name') ?>
getCurrentPage();
$pages = jvbGetUserDashboardPages()?:[];
global $jvb_everything;
echo '';
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(
'%s%s ',
$active,
get_home_url(2, $link),
$current,
$page,
$title,
jvbIcon($icon, ['title'=> $title]),
$title
);
}
echo ' ';
?>
getCurrentPage());
?>
userLink, BASE.'firstname', true);
$name = ($name === '') ? $this->user->display_name : $name;
echo 'Hey '.$name.' ';
echo 'Welcome back!
';
$pages = jvbGetUserDashboardPages();
echo 'What would you like to do today? ';
global $jvb_everything;
echo '';
echo 'Everything saves auto-magically, so rest easy.
';
}
/**
* 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
{
if (!current_user_can('manage_'.$type)) {
wp_redirect(get_home_url(2, '/dash'));
exit;
}
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
{
if (!current_user_can('manage_options') && !current_user_can('manage_settings')) {
wp_redirect(get_home_url(2, '/dash'));
exit;
}
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 = ' ';
}
return $out;
}
protected function renderIntegrations(string $page):void
{
//TODO: Make manage_integrations permission
// if (!current_user_can('manage_integrations')) {
// wp_redirect(get_home_url(2, '/dash'));
// exit;
// }
echo $this->getIntegrationsMenu();
$map = [
'google-my-business' => 'gmb',
'google-maps' => 'maps'
];
$connection = (array_key_exists($page, $map)) ? $map[$page] : $page;
if ($connection !== 'integrations') {
$userID = (jvbSiteHasMembership()) ? $this->user->ID : null;
$integration = JVB()->connect($connection, $userID);
echo 'Managing '.$integration->title.' ';
$integration->renderDefaults();
} else {
?>
getAvailableServices();
foreach ($all as $name) {
if (current_user_can('manage_options')) {
$userID = null;
} else {
$userID = $this->user->ID;
}
JVB()->connect($name, $userID)->renderConnection();
}
?>
= jvbIcon('all')?>All
= jvbIcon('artists')?>Artists
= jvbIcon('style')?>Terms
= jvbIcon('artist')?>Yours
'menu_item',
'events' => 'event',
default => $type
};
$permission = JVB_CONTENT[$type]['plural']??$type.'s';
if (!current_user_can('edit_'.$permission)) {
wp_redirect(get_home_url(2, '/dash'));
exit;
}
$crud = new CRUD($type);
$crud->render();
}
protected function renderAdmin():void
{
//TODO: This has to be built from the settings from setup.php
if (!current_user_can('manage_options')) {
wp_redirect(get_home_url(2, '/dash'));
exit;
}
?>
$config) {
unset($taxonomies[$key]);
}
$content = array_merge($content, $contentTax);
foreach ($content as $type => $settings) {
$active = ($i === 1) ? ' active' : '';
?>
=jvbIcon($type)?> = $settings['plural'] ?>
Taxonomy:
... Taxonomy
$settings) {
echo ''.$settings['plural'].' ';
}
?>
ArtistsManage artists here
= jvbRenderToggleTextField(
'vertical',
'TAB NAV:',
'',
jvbIcon('down'),
jvbIcon('right'))?>
$settings) {
$meta = new MetaManager(null, 'form');
$fields = jvbGetFields($type);
?>
Actions
$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 === '') ? '' : ''.$extra.'
';
if (!array_key_exists('label', $config)) {
error_log('No label set for: '.print_r($n, true));
}
?>
= $config['label'].$extra ?>
= jvbIcon('grab') ?>
= jvbRenderToggleTextField(
'public',
'',
'',
jvbIcon('show'),
jvbIcon('hide'))
?>
= jvbIcon('edit') ?>
$config) {
if (array_key_exists('quickEdit', $config)) {
?>
render('form', $n, $config);
?>
renderForm('admin', [], $fields)
);
}
}
}