<?php
|
/*
|
Plugin Name: JakeVan Base
|
Plugin URI: https://jakevan.ca
|
Description: The Base Plugin for JakeVan clients
|
Author: Jake Vanderwerf
|
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\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;
|
|
//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),
|
// ];
|
//
|
// // 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'));
|
//const JVB_TESTING = false;
|
|
//if (!JVB_TESTING) {
|
add_filter('show_admin_bar', '__return_false');
|
//}
|
|
|
|
const JVB_DIR = WP_PLUGIN_DIR . '/jvb';
|
define('JVB_URL', plugin_dir_url(__FILE__));
|
|
// Session Security
|
const JVB_SESSION_FINGERPRINT = true;
|
|
// Login Security
|
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');
|
|
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' => MONTH_IN_SECONDS,
|
'display' => __('Once Monthly', 'jvb')
|
];
|
|
return $schedules;
|
}
|
|
register_activation_hook(JVB_DIR.'/jvb.php', 'jvbActivatePlugin');
|
register_deactivation_hook(JVB_DIR.'/jvb.php', 'jvbDeactivatePlugin');
|
|
|
//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');
|
|
/**
|
* 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
|
{
|
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();
|
}
|
}
|
|
|
|
|
|
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?>");
|
}
|
|
|
|
|
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());
|
// }
|
// }
|
// ');
|
}
|
|
|
|
|
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('after_setup_theme', 'jvbImageSize');
|
function jvbImageSize():void
|
{
|
add_theme_support('post-thumbnails');
|
add_image_size( 'tiny', 50, 50 );
|
add_image_size( 'directory-preview', 100, 100 );
|
}
|
|
|
//
|
//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_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')
|
);
|
}
|
|
|
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);
|
}
|