<?php // Define constant to prevent original metabox scripts from loading define('STM_CUSTOM_METABOX_SCRIPTS', true); // Ensure proper loading order add_action('after_setup_theme', function() { // Load text domains after theme setup add_action('init', function() { do_action('motors_load_textdomains'); }, 100); }, 100); // Load our custom metabox scripts add_action('admin_enqueue_scripts', function() { global $pagenow; if (in_array($pagenow, array('post.php', 'post-new.php', 'edit.php'))) { if (defined('STM_WPCFTO_URL')) { $base = STM_WPCFTO_URL . 'metaboxes/assets/'; // Load Select2 first wp_enqueue_style('stmselect2', get_theme_file_uri('/assets/css/select2.min.css'), array(), '4.0.13'); wp_enqueue_script('stmselect2', get_theme_file_uri('/assets/js/select2.full.min.js'), array('jquery'), '4.0.13', true); // Load media uploader wp_enqueue_media(); // Load Vue.js first wp_enqueue_script('vue.js', $base . 'js/vue.min.js', array('jquery'), '2.6.11', true); // Load all Vue dependencies in correct order wp_enqueue_script('vue-resource.js', $base . 'js/vue-resource.min.js', array('vue.js'), '2.6.11', true); wp_enqueue_script('vue2-datepicker.js', $base . 'js/vue2-datepicker.min.js', array('vue.js'), '2.6.11', true); wp_enqueue_script('vue-select.js', $base . 'js/vue-select.js', array('vue.js'), '2.6.11', true); wp_enqueue_script('vue2-editor.js', $base . 'js/vue2-editor.min.js', array('vue.js'), '2.6.11', true); wp_enqueue_script('vue2-color.js', $base . 'js/vue-color.min.js', array('vue.js'), '2.6.11', true); wp_enqueue_script('sortable.js', $base . 'js/sortable.min.js', array('vue.js'), '2.6.11', true); wp_enqueue_script('vue-draggable.js', $base . 'js/vue-draggable.min.js', array('sortable.js'), '2.6.11', true); wp_enqueue_script('wpcfto_mixins.js', $base . 'js/mixins.js', array('vue.js'), '2.6.11', true); // Load the main metaboxes script wp_enqueue_script('wpcfto_metaboxes.js', $base . 'js/metaboxes.js', array('vue.js'), '2.6.11', true); // Add the required EventBus wp_add_inline_script('wpcfto_metaboxes.js', 'const WPCFTO_EventBus = new Vue();'); // Load required styles wp_enqueue_style('wpcfto-metaboxes.css', $base . 'css/main.css', array(), '2.6.11'); wp_enqueue_style('linear-icons', $base . 'css/linear-icons.css', array('wpcfto-metaboxes.css'), '2.6.11'); wp_enqueue_style('font-awesome-min', $base . '/vendors/font-awesome.min.css', null, '2.6.11', 'all'); wp_enqueue_style('vue-multiselect-min', $base . '/vendors/vue-multiselect.min.css', null, '2.6.11', 'all'); // Add debugging wp_add_inline_script('vue.js', 'console.log("✅ Custom Vue.js loaded successfully! Version:", Vue.version);'); // Load all the component scripts that the original method loads $components = array( 'group_title', 'text', 'time', 'number', 'image', 'checkbox', 'date', 'dates', 'select', 'radio', 'textarea', 'hint_textarea', 'color', 'autocomplete', 'editor', 'repeater', 'file', 'notice', 'notice_banner', 'notification_message', 'button_group', 'button_list', 'image_select', 'spacing', 'link_color', 'multi_checkbox', 'sorter', 'gallery', 'multi_input', 'ace_editor', 'color_gradient', 'icon_picker', 'range_slider', 'typography', 'multiselect', 'import_export' ); // Load components asynchronously to prevent blocking foreach ($components as $component) { wp_enqueue_script( "wpcfto_{$component}_component", STM_WPCFTO_URL . "/metaboxes/general_components/js/{$component}.js", array('wpcfto_metaboxes.js'), '2.6.11', true ); } wp_enqueue_script( 'wpcfto_fields_layout_component', STM_WPCFTO_URL . '/metaboxes/general_components/js/fields_aside.js', array('wpcfto_metaboxes.js'), '2.6.11', true ); } } }); add_action('wp_enqueue_scripts', 'stm_enqueue_parent_styles', 5); function stm_enqueue_parent_styles() { wp_enqueue_style( 'child-style', get_stylesheet_directory_uri() . '/style.css', array('stm-theme-style') ); // REMOVED: WordPress core editor scripts should NOT be loaded on frontend // These scripts (wp-data, wp-api-fetch, wp-blocks, wp-block-editor, wp-editor) // are for admin/editor use only and cause 401 errors when trying to access // /wp-json/wp/v2/users/me on public pages // If you need specific WP scripts on frontend, load them conditionally: // Example: // if (is_checkout()) { // wp_enqueue_script('wp-i18n'); // } // Load Select2 for frontend if needed (using CDN since child theme doesn't have the files) if (!is_admin()) { // Register under the 'select2' handle so parent theme scripts that expect // jQuery.fn.select2 (e.g. app-auto-parts.js) can pick it up as a dependency. if (!wp_script_is('select2', 'registered') && !wp_script_is('select2', 'enqueued')) { wp_enqueue_style('select2', 'https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/css/select2.min.css', array(), '4.0.13'); wp_register_script('select2', 'https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/js/select2.min.js', array('jquery'), '4.0.13', true); wp_enqueue_script('select2'); } // Force parent-theme scripts that use select2() to depend on our select2 // handle, so jQuery.fn.select2 exists by the time they execute. add_action('wp_print_scripts', function() { global $wp_scripts; if (!isset($wp_scripts)) { return; } foreach (array('stm-theme-scripts', 'app-auto-parts', 'motors-auto-parts', 'stm-app-auto-parts') as $handle) { if (isset($wp_scripts->registered[$handle])) { $deps = &$wp_scripts->registered[$handle]->deps; if (!in_array('select2', $deps, true)) { $deps[] = 'select2'; } } } }, 9); // Add JavaScript to hide placeholder images wp_add_inline_script('jquery', ' jQuery(document).ready(function($) { function hideAwaitingImages() { $("img[alt*=\"Awaiting product image\"]").hide(); $("img[src*=\"1530.jpg\"]").hide(); $("img[src*=\"/services/1530.jpg\"]").hide(); // Also hide images that fail to load (404s) $("img").on("error", function() { if ($(this).attr("alt") && $(this).attr("alt").includes("Awaiting product image")) { $(this).hide(); } if ($(this).attr("src") && $(this).attr("src").includes("1530.jpg")) { $(this).hide(); } }); } hideAwaitingImages(); setTimeout(hideAwaitingImages, 1000); setTimeout(hideAwaitingImages, 3000); }); '); } } // Hook the filter after WooCommerce is initialized add_action('woocommerce_init', function() { add_filter('wp_get_attachment_image_src', 'custom_attachment_image_src', 30, 4); }); function custom_attachment_image_src($image, $attachmentId, $size, $icon) { if (is_admin()) { return $image; } // Double-check that WooCommerce functions are available if (!function_exists('wc_get_product') || !function_exists('wc_get_product_term_ids')) { return $image; } // Add caching to prevent repeated database queries $cache_key = "custom_image_src_" . $attachmentId . "_" . (is_array($size) ? implode('x', $size) : $size); $cached_result = wp_cache_get($cache_key, 'custom_images'); if ($cached_result !== false) { return $cached_result; } if ($size == 'woocommerce_thumbnail') { $post = get_post($attachmentId); if ($post) { $product = wc_get_product($post->post_parent); if ($product) { $cateID = wc_get_product_term_ids($product->get_id(), 'product_cat'); if (!empty($cateID)) { $src = '/wp-content/uploads/services/' . $cateID[0] . '.jpg'; $width = ''; $height = ''; $image = [$src, $width, $height]; } } } } else if (function_exists('is_product') && is_product()) { $productId = get_the_ID(); if ($productId) { $product = wc_get_product($productId); if ($product) { $cateID = wc_get_product_term_ids($product->get_id(), 'product_cat'); if (!empty($cateID) && $attachmentId == 1) { $src = '/wp-content/uploads/services/' . $cateID[0] . '.jpg'; $width = ''; $height = ''; $image = array($src, $width, $height); } } } } // Cache the result for 1 hour (3600 seconds) wp_cache_set($cache_key, $image, 'custom_images', 3600); return $image; } function my_gform_pre_submission( $form ) { $fields_to_sanitize = array( 'input_18_2', 'input_18_3', 'input_21_2', 'input_38_1', 'input_38_2', 'input_38_3', 'input_24_2', 'input_24_3', 'input_26', 'input_23_2', 'input_23_3', 'input_26', 'input_25_2', 'input_25_3' ); foreach ($fields_to_sanitize as $field) { if (isset($_POST[$field])) { $_POST[$field] = sanitize_title($_POST[$field]); } } } add_action( 'gform_pre_submission', 'my_gform_pre_submission' ); function appointment_gform_pre_submission( $form ) { $mapping = array( 'car-tinting' => 'input_21_2', 'clear-bra-ppf' => 'input_25', 'ceramic-coating' => 'input_28', 'clear-plex-windshield' => 'clear-plex-windshield', 'tint-removal' => function() { return isset($_POST['input_39']) ? sanitize_title($_POST['input_39']) : sanitize_title($_POST['input_35']); } ); $field_to_update = sanitize_title($_POST['input_24']); if (array_key_exists($field_to_update, $mapping)) { if (is_callable($mapping[$field_to_update])) { $_POST['input_34'] = $mapping[$field_to_update](); } else { $_POST['input_34'] = sanitize_title($_POST[$mapping[$field_to_update]]); } } } add_action( 'gform_pre_submission_12', 'appointment_gform_pre_submission' ); add_action( 'gform_pre_submission_20', 'appointment_gform_pre_submission' ); add_action( 'gform_pre_submission_22', 'appointment_gform_pre_submission' ); add_action( 'gform_pre_submission_23', 'appointment_gform_pre_submission' ); // Fix Gravity Forms iframe issues that trigger ModSecurity // Option 1: Disable AJAX only for problematic form 21 add_filter( 'gform_form_args_21', function( $form_args ) { $form_args['ajax'] = false; // This prevents the iframe creation return $form_args; }); // Option 2: Alternative - modify iframe attributes to avoid ModSecurity detection add_filter( 'gform_ajax_iframe', function( $iframe, $form, $field_values, $is_ajax ) { // Replace display:none with visibility:hidden to avoid WAF detection $iframe = str_replace( "style='display:none;", "style='visibility:hidden;position:absolute;top:-9999px;", $iframe ); return $iframe; }, 10, 4 ); /** * Remove related products output */ remove_action( 'woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 20 ); /** * Remove product data tabs */ add_filter( 'woocommerce_product_tabs', 'woo_remove_product_tabs', 98 ); function woo_remove_product_tabs( $tabs ) { unset( $tabs['description'] ); // Remove the description tab unset( $tabs['reviews'] ); // Remove the reviews tab unset( $tabs['additional_information'] ); // Remove the additional information tab return $tabs; } function wc_remove_return_to_shop() { remove_action( 'woocommerce_before_cart', 'wc_print_notices' ); } add_action( 'init', 'wc_remove_return_to_shop' ); function stackoverflow_redirect_back_from_shop(){ // Only run on frontend and specific pages to reduce overhead if(!is_admin() && (is_shop() || is_product_category())){ global $wp_query; if ($wp_query->found_posts == 1) { wp_redirect(get_permalink($wp_query->posts[0]->ID)); exit; } } } add_action('the_post', 'stackoverflow_redirect_back_from_shop'); // Remove the product description Title add_filter( 'woocommerce_product_description_heading', '__return_null' ); // Remove the additional information title add_filter( 'woocommerce_product_additional_information_heading', '__return_null' ); add_filter( 'woocommerce_checkout_before_order_review', 'add_my_checkout_section' ); function add_my_checkout_section() { wc_print_notice( __( '50% Deposit To Confirm A Booking Is Required', 'woocommerce' ), 'notice' ); } add_filter('site_transient_update_plugins', 'remove_update_nag_for_plugin'); function remove_update_nag_for_plugin($value) { if (isset($value) && is_object($value)) { unset($value->response['woocommerce-bookings/woocommerce-bookings.php']); } return $value; } // Clear custom image cache when products are updated add_action('save_post', 'clear_custom_image_cache'); add_action('wp_update_nav_menu', 'clear_custom_image_cache'); function clear_custom_image_cache($post_id = null) { // Clear the entire custom images cache group wp_cache_flush_group('custom_images'); } // Get Instant Quote Button Widget function add_instant_quote_button_styles() { ?> <style> .instant-quote-button { display: none; } @media (max-width: 767px) { .instant-quote-button { position: fixed; bottom: 20px; right: 20px; width: 232px; height: 50px; background: linear-gradient(135deg, #34C759 0%, #30B350 100%); color: #FFF !important; border: none; border-radius: 25px; font-size: 15px; font-weight: 700; text-decoration: none; display: flex; align-items: center; justify-content: center; gap: 10px; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(52, 199, 89, 0.4); z-index: 9999; padding: 12px 20px; letter-spacing: 0.3px; } .instant-quote-button:hover { background: linear-gradient(135deg, #30B350 0%, #28A745 100%); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(52, 199, 89, 0.5); color: #FFF !important; text-decoration: none; } .instant-quote-button:focus { outline: none; box-shadow: 0 0 0 3px rgba(52, 199, 89, 0.3); } .instant-quote-button i { font-size: 22px; margin-right: 2px; } } </style> <?php } add_action('wp_head', 'add_instant_quote_button_styles'); function add_instant_quote_button_html() { ?> <a href="tel:9496370296" class="instant-quote-button"> <i class="fa fa-phone" aria-hidden="true"></i> <span>Call (949) 637-0296</span> </a> <?php } add_action('wp_footer', 'add_instant_quote_button_html', 10); // REMOVED: Fallback code for wp.data/wp.preferences is no longer needed // since we're not loading admin/editor scripts on the frontend anymore // Analytics (with ModSecurity note) add_action('wp_footer', function () { echo <<<'HTML' <!-- Google tag (gtag.js) --> <!-- Note: ModSecurity may detect GTM iframes as potentially malicious --> <!-- This is normal behavior for Google Analytics tracking --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-PBPNBSM1TJ"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-PBPNBSM1TJ'); </script> HTML; }, 99); // Speed up calendar by ignoring old bookings and only looking forward // TEMPORARILY DISABLED 2026-04-20 while investigating calendar regression after WC Bookings 3.x upgrade // add_filter('woocommerce_bookings_in_date_range_query', function($query_args) { // // Only check bookings from last 30 days forward // $query_args['meta_query'][] = array( // 'key' => '_booking_start', // 'value' => strtotime('-30 days'), // 'compare' => '>', // 'type' => 'NUMERIC' // ); // return $query_args; // }, 10, 1); // Cache availability queries for 5 minutes // add_filter('woocommerce_bookings_get_time_slots_transient_name', function($name, $product, $args) { // return 'wc_bookings_slots_' . md5(serialize($args)) . '_' . $product->get_id(); // }, 10, 3); // add_filter('woocommerce_bookings_get_time_slots_transient_expiry', function() { // return 300; // 5 minutes cache // }); /** * Redirect /services/ page to homepage unless it has product_cat query string or is a subpage */ add_action('template_redirect', 'redirect_services_to_homepage'); function redirect_services_to_homepage() { if (is_admin()) { return; } $current_uri = $_SERVER['REQUEST_URI']; $parsed_url = parse_url($current_uri); $path = isset($parsed_url['path']) ? rtrim($parsed_url['path'], '/') : ''; $query = isset($parsed_url['query']) ? $parsed_url['query'] : ''; if ($path === '/services' || $path === '/services/') { if (isset($_GET['product_cat']) || strpos($query, 'product_cat') !== false) { return; } global $wp; if (!empty($wp->request)) { $request_path = '/' . trim($wp->request, '/'); if ($request_path !== '/services' && strpos($request_path, '/services/') === 0) { return; } } wp_redirect(home_url('/'), 301); exit; } }