<?php
|
/*
|
Plugin Name: JakeVan Base
|
Plugin URI: https://jakevan.ca
|
Description: The Base Plugin for JakeVan clients
|
Author: Jake Vanderwerf
|
Version: 1.0.0
|
Author URI: https://jakevan.ca/
|
Textdomain: jvb
|
*/
|
|
use JVBase\JVB;
|
use JVBase\managers\IconsManager;
|
use JVBase\registrar\Registrar;
|
|
use JVBase\base\Site;
|
|
//security
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
/**
|
* 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),
|
];
|
|
// 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
|
{
|
return [BASE.'directory', BASE.'dash', 'attachment', 'revision', 'nav_menu_item'];
|
}
|
|
|
define('JVB_TESTING', str_contains(get_home_url(),'.test'));
|
|
//if (!JVB_TESTING) {
|
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);
|
|
// Login Security
|
define('JVB_MAX_LOGIN_ATTEMPTS', 5);
|
define('JVB_LOCKOUT_DURATION', 15 * MINUTE_IN_SECONDS);
|
|
|
require(JVB_DIR.'/base/_setup.php');
|
|
require(JVB_DIR.'/inc/utility/setup.php');
|
require(JVB_DIR.'/checks.php');
|
|
require(JVB_DIR . '/inc/registrar/_setup.php');
|
require(JVB_DIR . '/activate.php');
|
|
require(JVB_DIR . '/inc/helpers/all.php');
|
require(JVB_DIR . '/inc/ui/_setup.php');
|
require(JVB_DIR . '/inc/meta/_setup.php');
|
require(JVB_DIR . '/inc/importers/_setup.php');
|
require(JVB_DIR . '/inc/managers/_setup.php');
|
/**
|
* Get an icon element
|
*
|
* @param string $name Icon name
|
* @param array $options Options array:
|
* - 'source' => 'icons'|'dash'|'forms'|etc. (default: 'icons')
|
* - 'style' => 'regular'|'bold'|'fill'|etc.
|
* - 'label' => 'Accessible label'
|
* - 'decorative' => true
|
* - 'class' => 'additional classes'
|
* - 'size' => 24
|
* @return string HTML icon element
|
*/
|
function jvbIcon(string $name, array $options = []): string
|
{
|
$source = $options['source'] ?? 'icons';
|
|
// Remove source from options before passing to IconsManager
|
unset($options['source']);
|
|
return IconsManager::for($source)->get($name, $options);
|
}
|
|
function jvbFullIcon(string $name, array $options = []):string
|
{
|
$source = $options['source'] ?? 'icons';
|
unset($options['source']);
|
return IconsManager::for($source)->getRawSvg($name, $options['style']??null);
|
}
|
|
/**
|
* Get a CSS data URI for an icon
|
*
|
* @param string $name Icon name
|
* @param array $options Options array:
|
* - 'style' => 'regular'|'bold'|'fill'|etc.
|
* - 'source' => 'icons'|'dash'|'forms'|etc. (for tracking purposes)
|
* @return string data:image/svg+xml;base64,... URL
|
*/
|
function jvbCSSIcon(string $name, array $options = []): string
|
{
|
$style = $options['style'] ?? null;
|
$source = $options['source'] ?? 'icons';
|
|
return IconsManager::for($source)->getCSSIcon($name, $style);
|
}
|
|
/**
|
* Get a dashboard icon
|
*/
|
function jvbDashIcon(string $name, array $options = []): string
|
{
|
$options['source'] = 'dash';
|
return jvbIcon($name, $options);
|
}
|
|
/**
|
* Get a form editor icon
|
*/
|
function jvbFormIcon(string $name, array $options = []): string
|
{
|
$options['source'] = 'forms';
|
return jvbIcon($name, $options);
|
}
|
|
require(JVB_DIR . '/inc/integrations/_setup.php');
|
require(JVB_DIR . '/inc/rest/_setup.php');
|
|
add_filter( 'cron_schedules', 'jvbCronSchedules');
|
function jvbCronSchedules($schedules)
|
{
|
$schedules[ 'every-5-minutes' ] = [
|
'interval' => 5 * 60,
|
'display' => __('Every 5 minutes', 'jvb')
|
];
|
$schedules[ 'every-minute' ] = [
|
'interval' => 60,
|
'display' => __('Every minute', 'jvb')
|
];
|
$schedules[ 'monthly' ] = [
|
'interval' => 604800 * 4,
|
'display' => __('Once Monthly', 'jvb')
|
];
|
|
return $schedules;
|
}
|
|
register_activation_hook(JVB_DIR.'/jvb.php', 'jvbActivatePlugin');
|
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');
|
|
|
require(JVB_DIR . '/inc/templates.php');
|
|
/**
|
* Utilities
|
*/
|
require(JVB_DIR . '/cleanup.php');
|
require(JVB_DIR . '/inc/admin/_setup.php');
|
|
require(JVB_DIR . '/JVBase.php');
|
add_action('init', 'jvbLoadBase', 1);
|
function jvbLoadBase():void
|
{
|
JVB::getInstance();
|
}
|
function JVB(): JVB
|
{
|
return JVB::getInstance();
|
}
|
|
|
|
|
|
require(JVB_DIR . '/inc/blocks/_setup.php');
|
|
|
|
|
|
|
|
add_filter('upload_mimes', function ($mimes) {
|
$mimes['webp'] = 'image/webp';
|
$mimes['avif'] = 'image/avif';
|
return $mimes;
|
});
|
|
// And this to ensure WebP is treated as an image
|
add_filter('file_is_displayable_image', function ($result, $path) {
|
if (preg_match('/\.webp$/i', $path)) {
|
return true;
|
}
|
return $result;
|
}, 10, 2);
|
|
|
function jvbDump(mixed $array, string $name = 'var'):void
|
{
|
highlight_string("<?php\n\$$name =\n" . var_export($array, true) . ";\n?>");
|
}
|
|
|
/**
|
* 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
|
{
|
// wp_enqueue_script(
|
// 'google-maps',
|
// 'https://maps.googleapis.com/maps/api/js?key=' . GOOGLE_API . '&libraries=places&callback=googleMapsLoaded',
|
// [],
|
// null,
|
// [
|
// 'strategy' => 'async',
|
// 'in_footer' => true
|
// ]
|
// );
|
//
|
// // Add the callback function
|
// wp_add_inline_script('google-maps', '
|
// function googleMapsLoaded() {
|
// if (window.googleMapsCallbacks && window.googleMapsCallbacks.length) {
|
// window.googleMapsCallbacks.forEach(callback => callback());
|
// }
|
// }
|
// ');
|
}
|
|
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');
|
function jvbAccessibility():void
|
{
|
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_action('rest_api_init', function() {
|
// error_log('User ' . get_current_user_id() . ' can edit pages: ' . (current_user_can('edit_pages') ? 'yes' : 'no'));
|
// error_log('User roles: ' . print_r(wp_get_current_user()->roles, true));
|
//});
|
//
|
//add_filter('rest_pre_dispatch', function($result, $server, $request) {
|
// if (strpos($request->get_route(), '/wp/v2/pages') !== false) {
|
// error_log('Pages request - User: ' . get_current_user_id());
|
// error_log('Can edit pages: ' . (current_user_can('edit_pages') ? 'yes' : 'no'));
|
// error_log('Nonce: ' . $request->get_header('X-WP-Nonce'));
|
// }
|
// 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
|
{
|
echo sprintf(
|
'<a id="back-to-top" class="btn sticky" href="#">%s<span>Back to Top</span></a>',
|
jvbIcon('caret-double-up')
|
);
|
}
|