Jake Vanderwerf
2026-09-15 bdf344f19158a980a99dcca7c82c6e34e84533c0
jvb.php
@@ -4,77 +4,108 @@
Plugin URI: https://jakevan.ca
Description: The Base Plugin for JakeVan clients
Author: Jake Vanderwerf
Version: 1.0.0
Version: 1.1.0
Author URI: https://jakevan.ca/
Textdomain: jvb
*/
if (!defined('ABSPATH')) {
   exit; // Exit if accessed directly
}
use JVBase\admin\SEOAdmin;
use JVBase\base\SchemaHelper;
use JVBase\blocks\CustomBlocks;
use JVBase\inc\rest\routes\OpenStreetMaps;
use JVBase\JVB;
use JVBase\managers\AdminPages;
use JVBase\managers\Approvals;
use JVBase\managers\Dashboard\Dashboard;
use JVBase\managers\DirectoryManager;
use JVBase\managers\Emails;
use JVBase\managers\Favourites;
use JVBase\managers\IconsManager;
use JVBase\managers\InvitationsManager;
use JVBase\managers\Login;
use JVBase\managers\MagicLinks;
use JVBase\managers\NotificationManager;
use JVBase\managers\queue\Queue;
use JVBase\managers\Referrals;
use JVBase\managers\Responses;
use JVBase\managers\Roles;
use JVBase\managers\SEO\render\SchemaOutput;
use JVBase\managers\TaxonomyRelationships;
use JVBase\managers\UserTermsManager;
use JVBase\registrar\Registrar;
use JVBase\base\Site;
//security
if (!defined('ABSPATH')) {
    exit;
}
//add_action('admin_enqueue_scripts', function() {
// global $wp_scripts;
// add_action('admin_print_scripts', function() use ($wp_scripts) {
//    $queue = $wp_scripts->queue ?? [];
//    foreach ($queue as $handle) {
//       $src = $wp_scripts->registered[$handle]->src ?? 'unknown';
//       error_log("Enqueued: $handle => $src");
//    }
// }, 999);
//}, 999);
/**
 * Track REST API errors by wrapping request execution
 */
add_filter('rest_pre_dispatch', function($result, $server, $request) {
   // Store request details globally for error logging
   $GLOBALS['jvb_rest_request'] = [
      'route' => $request->get_route(),
      'method' => $request->get_method(),
      'params' => $request->get_params(),
      'time' => microtime(true),
   ];
//add_filter('rest_pre_dispatch', function($result, $server, $request) {
// // Store request details globally for error logging
// $GLOBALS['jvb_rest_request'] = [
//    'route' => $request->get_route(),
//    'method' => $request->get_method(),
//    'params' => $request->get_params(),
//    'time' => microtime(true),
// ];
//
// // Set up error handler for this request
// set_error_handler(function($errno, $errstr, $errfile, $errline) {
//    if (!(error_reporting() & $errno)) {
//       return false;
//    }
//
//    $context = array_merge($GLOBALS['jvb_rest_request'] ?? [], [
//       'error_type' => $errno,
//       'error_message' => $errstr,
//       'file' => $errfile,
//       'line' => $errline,
//       'user_id' => get_current_user_id(),
//    ]);
//
//    error_log('JVB REST Error: ' . json_encode($context));
//
//    return false; // Let PHP handle it normally too
// });
//
// return $result;
//}, 10, 3);
//
//add_filter('rest_post_dispatch', function($result, $server, $request) {
// // Restore default error handler
// restore_error_handler();
//
// // Check for fatal errors that occurred during request
// $error = error_get_last();
// if ($error && in_array($error['type'], [E_ERROR, E_USER_ERROR, E_PARSE, E_COMPILE_ERROR])) {
//    $context = array_merge($GLOBALS['jvb_rest_request'] ?? [], [
//       'fatal_error' => $error,
//       'user_id' => get_current_user_id(),
//    ]);
//
//    error_log('JVB REST Fatal: ' . json_encode($context));
// }
//
// // Clean up
// unset($GLOBALS['jvb_rest_request']);
//
// return $result;
//}, 10, 3);
   // Set up error handler for this request
   set_error_handler(function($errno, $errstr, $errfile, $errline) {
      if (!(error_reporting() & $errno)) {
         return false;
      }
      $context = array_merge($GLOBALS['jvb_rest_request'] ?? [], [
         'error_type' => $errno,
         'error_message' => $errstr,
         'file' => $errfile,
         'line' => $errline,
         'user_id' => get_current_user_id(),
      ]);
      error_log('JVB REST Error: ' . json_encode($context));
      return false; // Let PHP handle it normally too
   });
   return $result;
}, 10, 3);
add_filter('rest_post_dispatch', function($result, $server, $request) {
   // Restore default error handler
   restore_error_handler();
   // Check for fatal errors that occurred during request
   $error = error_get_last();
   if ($error && in_array($error['type'], [E_ERROR, E_USER_ERROR, E_PARSE, E_COMPILE_ERROR])) {
      $context = array_merge($GLOBALS['jvb_rest_request'] ?? [], [
         'fatal_error' => $error,
         'user_id' => get_current_user_id(),
      ]);
      error_log('JVB REST Fatal: ' . json_encode($context));
   }
   // Clean up
   unset($GLOBALS['jvb_rest_request']);
   return $result;
}, 10, 3);
function jvbIgnoredPostTypes():array
@@ -90,28 +121,26 @@
   add_filter('show_admin_bar', '__return_false');
//}
//if (JVB_TESTING) {
// error_log('In testing mode...');
//} else {
// error_log('Not in testing mode...');
//}
const JVB_DIR = WP_PLUGIN_DIR . '/jvb';
define('JVB_URL', plugin_dir_url(__FILE__));
// Session Security
define('JVB_SESSION_FINGERPRINT', true);
const JVB_SESSION_FINGERPRINT = true;
// Login Security
define('JVB_MAX_LOGIN_ATTEMPTS', 5);
define('JVB_LOCKOUT_DURATION', 15 * MINUTE_IN_SECONDS);
const JVB_MAX_LOGIN_ATTEMPTS = 5;
const JVB_LOCKOUT_DURATION = 15 * MINUTE_IN_SECONDS;
require(JVB_DIR.'/inc/Instance.php');
require(JVB_DIR.'/base/_setup.php');
require(JVB_DIR.'/inc/utility/setup.php');
require(JVB_DIR.'/checks.php');
require(JVB_DIR . '/inc/integrations/ServiceMap.php');
require(JVB_DIR . '/inc/registrar/_setup.php');
require(JVB_DIR . '/activate.php');
@@ -200,7 +229,7 @@
        'display' => __('Every minute', 'jvb')
    ];
    $schedules[ 'monthly' ] = [
        'interval' => 604800 * 4,
        'interval' => MONTH_IN_SECONDS,
        'display' => __('Once Monthly', 'jvb')
    ];
@@ -211,20 +240,6 @@
register_deactivation_hook(JVB_DIR.'/jvb.php', 'jvbDeactivatePlugin');
add_action('init', 'jvbUserCheck');
function jvbUserCheck():void
{
    if (is_admin() && isOurPeople()) {
        wp_redirect(get_home_url(null, '/dash'));
        exit;
    }
}
//require(JVB_DIR . '/inc/users/UserSettings.php');
@@ -237,16 +252,200 @@
require(JVB_DIR . '/inc/admin/_setup.php');
require(JVB_DIR . '/JVBase.php');
add_action('init', 'jvbLoadBase', 1);
/**
 * After moving to the Registrar::based registration, we need to carefully time
 *    when Site gets defined, as well as Registrar initial definitions, field definitions,
 *    and integration config
 * These custom actions should simplify the timing for us.
 */
add_action('plugins_loaded', 'jvb_site_definitions',1);
add_action('plugins_loaded', 'jvb_registrar_definitions',2);
add_action('plugins_loaded', 'jvb_field_definitions', 3);
add_action('plugins_loaded', 'jvb_options_definitions',3);
add_action('init', 'jvbLoadBase', 2);
add_action('init', 'jvb_integration_definitions',3);
add_action('init', 'jvb_field_section_definitions', 5);
/**
 * Can define the Site settings
 * @return void
 */
function jvb_site_definitions():void
{
   do_action('jvb_define_site');
}
function jvb_registrar_definitions():void
{
   do_action('jvb_define_registrar');
}
function jvb_field_definitions():void
{
   do_action('jvb_define_fields');
}
function jvb_options_definitions():void
{
    do_action('jvb_define_options');
}
function jvbLoadBase():void
{
   JVB::getInstance();
   do_action('jvbloaded');
   Emails::getInstance();
   Queue::getInstance();
   Roles::getInstance();
   Login::getInstance();
   SEOAdmin::getInstance();
   SchemaOutput::getInstance();
   SchemaHelper::getInstance();
   CustomBlocks::getInstance();
   AdminPages::getInstance();
   if (Site::has('magic_link')) {
      MagicLinks::getInstance();
   }
   if (Site::has('dashboard')) {
      Dashboard::getInstance();
   }
   if (Site::has('referrals')) {
      Referrals::getInstance();
   }
   if (Site::has('is_directory')) {
      DirectoryManager::getInstance();
   }
   if (Site::has('feed_block')) {
      TaxonomyRelationships::getInstance();
   }
   if (Site::hasAll(['dashboard', 'membership', 'feed_block'])) {
      UserTermsManager::getInstance();
   }
   if (Site::has('favourites')) {
      Favourites::getInstance();
   }
   if (!empty(Registrar::withFeature('has_responses'))) {
      Responses::getInstance();
   }
   $membership = Site::membership();
   if ($membership && $membership->has('notifications')) {
      NotificationManager::getInstance();
   }
   if ($membership && $membership->has('invitable')) {
      InvitationsManager::getInstance();
   }
   if (!empty(Registrar::withFeature('approve_new'))) {
      Approvals::getInstance();
   }
   jvbLoadRoutes();
}
function jvb_integration_definitions():void
{
   do_action('jvb_define_integrations');
}
function jvb_field_section_definitions():void
{
   do_action('jvb_define_field_sections');
   Registrar::maybeBuildSections();
}
add_action('wp_footer', 'jvbAdditionalActions');
function jvbAdditionalActions():void
{
   if (Login::isLogin()) {
      return;
   }
   $extras = apply_filters('jvbAdditionalActions', []);
   $extras = array_filter($extras, function ($extra) {
      return is_array($extra) && array_key_exists('button', $extra) && array_key_exists('content', $extra);
   });
   if (empty ($extras)) {
      return;
   }
   $buttons = array_map(function ($extra) {
      return $extra['button'];
   }, $extras);
   $contents = array_map(function($extra) {
      return $extra['content'];
   }, $extras);
   if (!empty ($buttons)) {
      ?>
      <section class="main-actions">
         <div class="buttons col">
            <?= implode('', $buttons); ?>
         </div>
         <div class="actions">
            <?= implode('', $contents); ?>
         </div>
      </section>
      <?php
   }
}
function JVB(): JVB
{
   return JVB::getInstance();
}
use JVBase\rest\routes\AdminRoutes;
use JVBase\rest\routes\ContentRoutes;
use JVBase\rest\routes\ContentTermsRoutes;
use JVBase\rest\routes\ErrorRoutes;
use JVBase\rest\routes\FeedRoutes;
use JVBase\rest\routes\FormRoutes;
use JVBase\rest\routes\NewsRoutes;
use JVBase\rest\routes\OptionsRoutes;
use JVBase\rest\routes\SEORoutes;
use JVBase\rest\routes\SettingsRoutes;
use JVBase\rest\routes\TermRoutes;
use JVBase\rest\routes\UploadRoutes;
function jvbLoadRoutes():void
{
   SEORoutes::getInstance();
   SettingsRoutes::getInstance();
   UploadRoutes::getInstance();
   if (!empty(apply_filters('jvb_register_forms', []))) {
      FormRoutes::getInstance();
   }
   UserRoutes::getInstance();
   if (Site::has('feed_block')) {
      FeedRoutes::getInstance();
   }
   if (Site::has('feed_block') || Site::has('dashboard')) {
      TermRoutes::getInstance();
   }
   if (Site::has('dashboard')) {
      ErrorRoutes::getInstance();
      AdminRoutes::getInstance();
      ContentRoutes::getInstance();
      ContentTermsRoutes::getInstance();
      OptionsRoutes::getInstance();
   }
   $membership = Site::membership();
   if ($membership && $membership->has('forum'))
   {
      NewsRoutes::getInstance();
   }
   OpenStreetMaps::getInstance();
}
@@ -280,191 +479,7 @@
}
/**
 * Scripts
 */
add_action('wp_enqueue_scripts', 'jvbScripts', 10);
function jvbScripts():void
{
    add_action('wp_head', 'jvbInlineNavStyles');
   if (Site::has('dashboard')) {
      wp_enqueue_script('jvb-queue');
   }
   wp_enqueue_script('jvb-auth');
   wp_enqueue_script('jvb-settings');
    wp_enqueue_script('jvb-navigation');
//    wp_enqueue_script('jvb-ui');
//    wp_enqueue_script('jvb-media');
   wp_enqueue_script('jvb-gallery');
    wp_enqueue_script('jvb-cache');
   $interactions = [];
   if (Site::has('favourites')) {
      $interactions[] = 'favourites';
   }
   if (!empty(Registrar::getFeatured('karma'))) {
      $interactions[] = 'karma';
   }
   if (Site::has('notifications')) {
      $interactions[] = 'notifications';
   }
   if (!empty($interactions)) {
      wp_enqueue_script('jvb-interactions');
      foreach($interactions as $interaction) {
         wp_enqueue_script('jvb-'.$interaction);
      }
   }
   $queue = [
      'api' => rest_url('jvb/v1/'),
      'redirect' => get_home_url(null, '/login/'),
      'labels' => Registrar::getLabels(),
   ];
    wp_localize_script('jvb-auth', 'jvbSettings', $queue);
   $initUserSettings = 'async function initUserItems() {
   if (!jvbSettings.currentUser) return;
   window.userFavourites = {};
   window.userVotes = {};
   ';
   if (Site::has('favourites')) {
      wp_enqueue_script('jvb-favourites');
      $initUserSettings .= '
      //Fetch user favourites
      try {
         const result = await window.jvbCache.fetchWithCache(
            `${jvbSettings.api}favourites?all=true`,
                {
                    method: \'GET\',
                    headers: {\'X-WP-Nonce\': jvbSettings.nonce}
                }
            );
            if (result && result.favourites) {
            for (const key in result.favourites) {
               window.userFavourites[key] = new Set();
               result.favourites[key].forEach(id => {
                  window.userFavourites[key].add(parseInt(id));
               });
                }
            }
        } catch (error) {
         console.error(\'Failed to fetch user favourites:\', error);
      }';
   }
   if (!empty(Registrar::getFeatured('karma'))) {
      wp_enqueue_script('jvb-votes');
      $initUserSettings .= '// Fetch user votes
        try {
         const result = await window.jvbCache.fetchWithCache(
            `${jvbSettings.api}vote?user=${jvbSettings.currentUser}`,
                {
                    method: \'GET\',
                    headers: {\'X-WP-Nonce\': jvbSettings.nonce}
                },
                {forceRefresh: true}
            );
            window.userVotes = {};
            if (result) {
            for (const key in result) {
               window.userVotes[key] = new Map();
               for (const [id, vote] of Object.entries(result[key])) {
                  window.userVotes[key].set(parseInt(id), vote);
               }
                }
            }
        } catch (error) {
         console.error(\'Failed to fetch user votes:\', error);
      }';
   }
   $initUserSettings .= '} initUserItems();';
// wp_add_inline_script('jvb-queue', $initUserSettings, 'after');
    if (is_search() ||
        is_tax(BASE.'shop') ||
        is_singular(BASE.'artist') ||
        jvbIsDirectory()
    ) {
        wp_enqueue_script('jvb-page-nav');
    }
   if (has_block('jvb/summaryBlock')) {
      wp_enqueue_script('jvb-page-nav');
   }
        // Only load on single shop pages or other relevant pages
    if (is_tax(BASE.'shop') ||
        is_singular(BASE.'partner')) {
            $ID = get_queried_object_id();
        if (is_tax()) {
            $check = get_term_meta($ID, BASE.'has_map', true);
        } else {
            $check = get_post_meta($ID, BASE.'has_map', true);
        }
//        if ($check && $check !== '') {
////            wp_enqueue_script(
////                'google-maps-frontend',
////                'https://maps.googleapis.com/maps/api/js?key=' . GOOGLE_API . '&callback=googleMapsLoaded',
////                [],
////                null,
////                ['strategy' => 'async',
////                 'in_footer' => true]
////            );
//
//            // Add the callback function
//            wp_add_inline_script('google-maps-frontend', '
//            function googleMapsLoaded() {
//                if (window.googleMapsCallbacks && window.googleMapsCallbacks.length) {
//                    window.googleMapsCallbacks.forEach(callback => callback());
//                }
//            }
//        ');
//        }
    }
    if (is_user_logged_in() && Site::has('notifications')) {
        wp_enqueue_script('jvb-notifications');
        wp_localize_script('jvb-notifications', 'notificationSettings', array(
            'homeUrl' => home_url(),
            'strings' => array(
                'loading' => 'Loading...',
                'error' => 'Failed to load notifications',
                'noNotifications' => 'No new notifications',
                'viewAll' => 'View All Notifications',
                'markRead' => 'Mark as read',
                'review' => 'Review'
            )
        ));
    }
    jvbAddScriptDependency('jvb-feed-view-script', 'jvb-queue');
    jvbAddScriptDependency('jvb-feed-view-script', 'jvb-selector');
    jvbAddScriptDependency('jvb-feed-view-script', 'jvb-data-store');
    jvbAddScriptDependency('jvb-feed-view-script', 'jvb-cache');
    jvbAddScriptDependency('jvb-feed-view-script', 'jvb-a11y');
    jvbAddScriptDependency('jvb-feed-view-script', 'jvb-utility');
    jvbAddScriptDependency('jvb-feed-view-script', 'jvb-gallery');
//    jvbAddScriptDependency('jvb-feed-view-script', 'jvb-loading');
   jvbAddScriptDependency('jvb-forms-view-script', 'jvb-queue');
   jvbAddScriptDependency('jvb-forms-view-script', 'jvb-quill');
   jvbAddScriptDependency('jvb-forms-view-script', 'jvb-form');
   jvbAddScriptDependency('jvb-forms-view-script', 'jvb-tabs');
}
function jvbAdminMap():void
{
@@ -489,21 +504,7 @@
//    ');
}
function jvbAddScriptDependency(string $handle, string $dep):bool
{
    global $wp_scripts;
    $script = $wp_scripts->query($handle);
    if (!$script) {
        return false;
    }
    if (!in_array($dep, $script->deps)) {
        $script->deps[] = $dep;
    }
    return true;
}
add_action('wp_body_open', 'jvbAccessibility');
@@ -511,27 +512,23 @@
{
    echo '<div class="screen-reader-text live-region" aria-live="polite" role="status"></div>';
}
/*
add_action('wp_head', 'jvbFrontendBase',1);
function jvbFrontendBase():void
{
   ?>
   <script type="text/javascript">window.jvbBase = '<?= BASE ?>';</script>
   <?php
}
*/
//add_action('wp_head', 'jvbDumpIt');
function jvbDumpIt()
{
}
add_action('after_setup_theme', 'jvbImageSize');
function jvbImageSize():void
{
   add_theme_support('post-thumbnails');
   add_image_size( 'tiny', 50, 50, false );
   add_image_size( 'directory-preview', 100, 100, false );
   add_image_size( 'tiny', 50, 50 );
   add_image_size( 'directory-preview', 100, 100 );
}
@@ -550,23 +547,6 @@
// return $result;
//}, 10, 3);
add_filter('rest_authentication_errors', function($result) {
   // Don't override existing authentication
   if (is_wp_error($result) || $result === true) {
      return $result;
   }
   // Try to authenticate from cookie
   $cookie_user = wp_validate_auth_cookie('', 'logged_in');
   if ($cookie_user) {
      wp_set_current_user($cookie_user);
      return true;
   }
   return $result;
}, 99);
add_action('wp_footer', 'jvb_back_to_top');
function jvb_back_to_top():void
@@ -577,9 +557,11 @@
   );
}
add_action( 'doing_it_wrong_run', function ( $function_name ) {
   if ( '_load_textdomain_just_in_time' === $function_name ) {
      // This will print the full execution path to your screen or log
      debug_print_backtrace();
   }
} );
add_filter('the_seo_framework_sitemap_exclude_ids', 'jvb_maybe_exclude_singles', 8, 1);
function jvb_maybe_exclude_singles(array $IDs):array
{
   return Registrar::maybeExcludeSingles($IDs);
}