load_dependencies(); $this->init_components(); // Migrations touch posts and options: run them once WordPress is fully loaded, // not while plugins are still being included (when this constructor runs). add_action( 'init', [ $this, 'settings_version_check' ], 20 ); } /** Prevent cloning. */ public function __clone() {} /** Prevent unserialization. */ public function __wakeup() {} // ------------------------------------------------------------------------- // Dependency Loading // ------------------------------------------------------------------------- private function load_dependencies(): void { // Database manager (must load before any class that queries DB) require_once EPPRO_PATH . 'core/DB.php'; // Core managers require_once EPPRO_PATH . 'core/Asset_Manager.php'; require_once EPPRO_PATH . 'core/widgets-manager.php'; // Shared Elementor helpers (widget lookup, safe template rendering) require_once EPPRO_PATH . 'includes/class-elementor-helper.php'; // Admin & back-end services require_once EPPRO_PATH . 'includes/class-admin-settings.php'; require_once EPPRO_PATH . 'includes/class-form-handler.php'; require_once EPPRO_PATH . 'includes/class-form-entries.php'; require_once EPPRO_PATH . 'includes/class-customizer.php'; // Setup wizard require_once EPPRO_PATH . 'includes/class-setup-wizard.php'; // Advanced search AJAX handler require_once EPPRO_PATH . 'includes/class-advanced-search-handler.php'; // Template duplicator require_once EPPRO_PATH . 'includes/class-template-duplicator.php'; // Advanced features (v1.4.0+) // Note: Display conditions are handled by EPPRO_Extension_Conditions (core/extensions/Conditions.php). // The standalone class-display-conditions.php is no longer loaded to avoid dead code. require_once EPPRO_PATH . 'includes/class-effects-manager.php'; // Popup builder system require_once EPPRO_PATH . 'includes/popup/class-popup-cpt.php'; require_once EPPRO_PATH . 'includes/popup/class-popup-settings.php'; require_once EPPRO_PATH . 'includes/popup/class-popup-design.php'; require_once EPPRO_PATH . 'includes/popup/class-popup-admin.php'; require_once EPPRO_PATH . 'includes/popup/class-popup-renderer.php'; // Theme Builder (Header/Footer) system require_once EPPRO_PATH . 'includes/theme-builder/class-theme-builder-cpt.php'; require_once EPPRO_PATH . 'includes/theme-builder/class-theme-builder-conditions.php'; require_once EPPRO_PATH . 'includes/theme-builder/class-theme-builder-admin.php'; require_once EPPRO_PATH . 'includes/theme-builder/class-theme-builder-renderer.php'; // Template shortcode require_once EPPRO_PATH . 'includes/class-template-shortcode.php'; // Post Grid AJAX handler require_once EPPRO_PATH . 'includes/class-post-grid-ajax.php'; // WooCommerce Products AJAX (Load More, Quick View) require_once EPPRO_PATH . 'includes/class-woo-products-ajax.php'; // Read settings to decide which extensions to load $eppro_s = get_option( 'eppro_settings', [] ); $disabled = (array) ( $eppro_s['disabled_extensions'] ?? [] ); // Dynamic Tags if ( ! in_array( 'dynamic_tags', $disabled, true ) ) { require_once EPPRO_PATH . 'core/dynamic-tags/Tag_Manager.php'; } // Extensions (per-element controls) if ( ! in_array( 'sticky', $disabled, true ) ) { require_once EPPRO_PATH . 'core/extensions/Sticky.php'; } if ( ! in_array( 'custom_css', $disabled, true ) ) { require_once EPPRO_PATH . 'core/extensions/Custom_CSS.php'; } if ( ! in_array( 'conditions', $disabled, true ) ) { require_once EPPRO_PATH . 'core/extensions/Conditions.php'; } } // ------------------------------------------------------------------------- // Component Initialisation // ------------------------------------------------------------------------- private function init_components(): void { // ── Elementor hooks FIRST ───────────────────────────────────────────── // These must be registered before anything else so that a failure in // any later init() cannot prevent widgets from appearing in the editor. add_action( 'elementor/elements/categories_registered', [ 'EPPRO_Widgets_Manager', 'register_category' ] ); add_action( 'elementor/widgets/register', [ 'EPPRO_Widgets_Manager', 'register_widgets' ] ); // ── DB ──────────────────────────────────────────────────────────────── try { EPPRO_DB::maybe_upgrade(); } catch ( \Throwable $e ) { eppro_log( 'EPP DB upgrade error: ' . $e->getMessage() ); } // ── Core admin & asset services ─────────────────────────────────────── try { EPPRO_Admin_Settings::init(); EPPRO_Form_Handler::init(); EPPRO_Form_Entries::init(); EPPRO_Asset_Manager::init(); EPPRO_Customizer::init(); } catch ( \Throwable $e ) { eppro_log( 'EPP core admin init error: ' . $e->getMessage() ); } // ── Optional extensions ─────────────────────────────────────────────── try { if ( class_exists( 'EPPRO_Tag_Manager' ) ) { EPPRO_Tag_Manager::init(); } if ( class_exists( 'EPPRO_Extension_Sticky' ) ) { EPPRO_Extension_Sticky::init(); } if ( class_exists( 'EPPRO_Extension_Conditions' ) ) { EPPRO_Extension_Conditions::init(); } if ( class_exists( 'EPPRO_Extension_Custom_CSS' ) ) { EPPRO_Extension_Custom_CSS::init(); } } catch ( \Throwable $e ) { eppro_log( 'EPP extensions init error: ' . $e->getMessage() ); } // ── Setup wizard + utilities ────────────────────────────────────────── try { EPPRO_Setup_Wizard::init(); EPPRO_Advanced_Search_Handler::init(); EPPRO_Template_Duplicator::init(); // Display Conditions handled by EPPRO_Extension_Conditions (core/extensions). EPPRO_Effects_Manager::init(); } catch ( \Throwable $e ) { eppro_log( 'EPP utilities init error: ' . $e->getMessage() ); } // ── Popup system ────────────────────────────────────────────────────── try { EPPRO_Popup_CPT::init(); EPPRO_Popup_Settings::init(); EPPRO_Popup_Design::init(); EPPRO_Popup_Admin::init(); EPPRO_Popup_Renderer::init(); } catch ( \Throwable $e ) { eppro_log( 'EPP popup init error: ' . $e->getMessage() ); } // ── Theme Builder system ────────────────────────────────────────────── try { EPPRO_Theme_Builder_CPT::init(); EPPRO_Theme_Builder_Admin::init(); EPPRO_Theme_Builder_Renderer::init(); EPPRO_Template_Shortcode::init(); } catch ( \Throwable $e ) { eppro_log( 'EPP theme builder init error: ' . $e->getMessage() ); } // ── Post Grid / WooCommerce Products AJAX ───────────────────────────── try { EPPRO_Post_Grid_AJAX::init(); } catch ( \Throwable $e ) { eppro_log( 'EPP post grid init error: ' . $e->getMessage() ); } try { EPPRO_Woo_Products_AJAX::init(); } catch ( \Throwable $e ) { eppro_log( 'EPP woo products init error: ' . $e->getMessage() ); } // ── WooCommerce: keep Mini Cart badge count in sync on AJAX add-to-cart ─ // Registered here (not in the widget file) so it fires on // wc-ajax=get_refreshed_fragments requests where Elementor widgets are // never registered. It is added unconditionally: this code runs while // Elementor loads, before WooCommerce, so class_exists( 'WooCommerce' ) // would still be false here. The filter only fires when WooCommerce runs. add_filter( 'woocommerce_add_to_cart_fragments', [ __CLASS__, 'mini_cart_fragment' ] ); } /** Refreshed item count and subtotal for the Mini Cart widget. */ public static function mini_cart_fragment( $fragments ) { if ( ! is_array( $fragments ) || ! function_exists( 'WC' ) || ! WC()->cart ) return $fragments; $count = WC()->cart->get_cart_contents_count(); $fragments['.eppro-wmc-count'] = '' . esc_html( $count ) . ''; $fragments['.eppro-wmc-subtotal'] = '' . wp_kses_post( WC()->cart->get_cart_subtotal() ) . ''; return $fragments; } // ------------------------------------------------------------------------- // Settings Versioning — runs a migration when EPPRO_VERSION changes. // ------------------------------------------------------------------------- public function settings_version_check(): void { $stored = get_option( 'eppro_settings_version', '0.0.0' ); if ( version_compare( $stored, EPPRO_VERSION, '<' ) ) { $this->migrate_old_prefix(); update_option( 'eppro_settings_version', EPPRO_VERSION ); // Widget style controls can change between versions (new CSS selectors). // Let Elementor rebuild every page's CSS file once, lazily, so existing // pages pick the new rules up without a manual "Regenerate CSS". if ( class_exists( '\Elementor\Plugin' ) && isset( \Elementor\Plugin::$instance->files_manager ) ) { \Elementor\Plugin::$instance->files_manager->clear_cache(); } } } private function migrate_old_prefix(): void { if ( get_option( 'eppro_prefix_migrated' ) ) return; global $wpdb; $option_map = [ 'epp_settings' => 'eppro_settings', 'epp_settings_version' => 'eppro_settings_version', 'epp_setup_complete' => 'eppro_setup_complete', 'epp_db_version' => 'eppro_db_version', 'epp_customizer_settings' => 'eppro_customizer_settings', 'epp_popup_cpt_flushed' => 'eppro_popup_cpt_flushed', 'epp_popup_cpt_flushed_v2' => 'eppro_popup_cpt_flushed_v2', 'epp_hf_cpt_flushed' => 'eppro_hf_cpt_flushed', 'epp_feedback_notice_dismissed' => 'eppro_feedback_notice_dismissed', 'epp_show_feedback_notice' => 'eppro_show_feedback_notice', 'epp_deactivation_feedback_log' => 'eppro_deactivation_feedback_log', ]; foreach ( $option_map as $old => $new ) { $val = get_option( $old ); if ( false !== $val && false === get_option( $new ) ) { update_option( $new, $val ); delete_option( $old ); } } $old_popups = get_posts( [ 'post_type' => 'epp_popup', 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', 'no_found_rows' => true, ] ); foreach ( $old_popups as $post_id ) { wp_update_post( [ 'ID' => $post_id, 'post_type' => 'eppro_popup' ] ); } $old_templates = get_posts( [ 'post_type' => 'epp_hf_template', 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', 'no_found_rows' => true, ] ); foreach ( $old_templates as $post_id ) { wp_update_post( [ 'ID' => $post_id, 'post_type' => 'eppro_hf_template' ] ); } $old_table = $wpdb->prefix . 'epp_form_entries'; $new_table = $wpdb->prefix . 'eppro_form_entries'; // One-time migration of the plugin's own table (old "epp_" prefix → "eppro_"). // phpcs:disable WordPress.DB.DirectDatabaseQuery $table_exists = wp_cache_get( 'eppro_old_table_check' ); if ( false === $table_exists ) { $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $old_table ) ) === $old_table; wp_cache_set( 'eppro_old_table_check', $table_exists ); } if ( $table_exists ) { $wpdb->query( $wpdb->prepare( 'RENAME TABLE %i TO %i', $old_table, $new_table ) ); // phpcs:enable WordPress.DB.DirectDatabaseQuery wp_cache_delete( 'eppro_old_table_check' ); } // No rewrite flush needed: the plugin's post types register no rewrite rules. update_option( 'eppro_prefix_migrated', '1' ); } }