' . wp_kses_post( sprintf( /* translators: %s: current PHP version */ __( 'Essential Pro Pack requires PHP 8.0 or higher. Your server is running PHP %s. Please upgrade.', 'admore-widgets-suite' ), esc_html( PHP_VERSION ) ) ) . '

'; } ); return; // Bail — do not define constants or load any further code. } // ─── Constants ─────────────────────────────────────────────────────────────── define( 'EPPRO_VERSION', '1.0.5' ); define( 'EPPRO_PATH', plugin_dir_path( __FILE__ ) ); define( 'EPPRO_URL', plugin_dir_url( __FILE__ ) ); define( 'EPPRO_BASENAME', plugin_basename( __FILE__ ) ); // ─── Asset version helper ─────────────────────────────────────────────────── // Plugin version + file modification time, so browsers and caches fetch a // changed CSS/JS file right away (also when a build is re-uploaded without // a version bump). if ( ! function_exists( 'eppro_asset_ver' ) ) { function eppro_asset_ver( string $relative_path ): string { $file = EPPRO_PATH . ltrim( $relative_path, '/' ); $time = file_exists( $file ) ? filemtime( $file ) : false; return $time ? EPPRO_VERSION . '.' . $time : EPPRO_VERSION; } } // ─── Debug logging helper ─────────────────────────────────────────────────── if ( ! function_exists( 'eppro_log' ) ) { function eppro_log( string $message ): void { if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { error_log( $message ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Only when the site owner enabled WP_DEBUG_LOG. } } } // ─── Text domain is loaded automatically by WordPress since WP 4.6 ─────────── // ─── Boot EPP early — load all core classes before any page renders ─────────── // load_dependencies() only uses require_once + EPPRO_PATH, no Elementor classes, // so it is safe to run before elementor/loaded. init_components() only registers // WordPress/Elementor action hooks (never executes them), which is also safe. // The singleton prevents any double-initialisation when elementor/loaded fires. add_action( 'plugins_loaded', function (): void { require_once EPPRO_PATH . 'includes/class-admin-settings.php'; require_once EPPRO_PATH . 'includes/popup/class-popup-admin.php'; require_once EPPRO_PATH . 'includes/theme-builder/class-theme-builder-admin.php'; require_once EPPRO_PATH . 'includes/class-form-entries.php'; require_once EPPRO_PATH . 'includes/class-template-duplicator.php'; // Boot the full plugin core early so EPPRO_Popup_CPT, EPPRO_DB, etc. are defined // before any admin render_page callback fires. try { require_once EPPRO_PATH . 'core/Plugin.php'; EPPRO_Plugin::instance(); } catch ( \Throwable $e ) { $msg = $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine(); eppro_log( 'Essential Pro Pack early boot error: ' . $msg ); add_action( 'admin_notices', function () use ( $msg ): void { echo '

' . 'Essential Pro Pack: Boot error — ' . esc_html( $msg ) . '

'; } ); } }, 5 ); // ─── Elementor hooks are registered by EPPRO_Plugin::init_components() ────────── // The singleton pattern ensures categories and widgets are only registered once // via add_action calls inside init_components(). No duplicate registration needed. // ─── Feedback Manager (loads independently of Elementor) ────────────────────── require_once EPPRO_PATH . 'includes/class-feedback-manager.php'; add_action( 'plugins_loaded', [ 'EPPRO_Feedback_Manager', 'init' ] ); // ─── Show admin notice when Elementor is not active ────────────────────────── // admin_notices fires after ALL plugins_loaded callbacks have run, so // did_action('elementor/loaded') is a reliable check here regardless of // plugin load order. add_action( 'admin_notices', function (): void { if ( ! did_action( 'elementor/loaded' ) ) { echo '

' . wp_kses_post( sprintf( /* translators: %s: Elementor install URL */ __( 'Essential Pro Pack requires Elementor to be installed and activated.', 'admore-widgets-suite' ), esc_url( admin_url( 'plugin-install.php?s=elementor&tab=search&type=term' ) ) ) ) . '

'; } } ); // ─── Boot the plugin AFTER Elementor is fully loaded ────────────────────────── // elementor/loaded fires once Elementor has initialised all its classes // (Widget_Base, Controls_Manager, etc.). Hooking here guarantees that // base-widget.php and every widget class can safely extend/reference them. add_action( 'elementor/loaded', function (): void { try { require_once EPPRO_PATH . 'core/Plugin.php'; EPPRO_Plugin::instance(); } catch ( \Throwable $e ) { $msg = $e->getMessage() . ' — ' . $e->getFile() . ':' . $e->getLine(); eppro_log( 'Essential Pro Pack boot error: ' . $msg ); add_action( 'admin_notices', function () use ( $msg ): void { echo '

Essential Pro Pack: Boot error — ' . esc_html( $msg ) . '

'; } ); } } ); // ─── Activation Hook ────────────────────────────────────────────────────────── function eppro_activate(): void { if ( ! class_exists( 'Elementor\Plugin' ) ) { deactivate_plugins( plugin_basename( __FILE__ ) ); wp_die( esc_html__( 'Elementor must be installed and activated before using Essential Pro Pack.', 'admore-widgets-suite' ) ); } // Create / upgrade custom DB tables. require_once EPPRO_PATH . 'core/DB.php'; EPPRO_DB::install(); // Stamp install version for future migrations. if ( ! get_option( 'eppro_settings_version' ) ) { update_option( 'eppro_settings_version', EPPRO_VERSION ); } // Flush rewrite rules so any new endpoints take effect. flush_rewrite_rules(); // Show install notice once. EPPRO_Feedback_Manager::on_activate(); // Trigger setup wizard redirect on first install only. if ( ! get_option( 'eppro_setup_complete' ) ) { set_transient( 'eppro_activation_redirect', '1', 30 ); } } register_activation_hook( __FILE__, 'eppro_activate' ); // ─── Deactivation Hook ─────────────────────────────────────────────────────── function eppro_deactivate(): void { flush_rewrite_rules(); global $wpdb; // Removes this plugin's transients; there is no core API to delete by prefix. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE option_name LIKE %s OR option_name LIKE %s', $wpdb->options, $wpdb->esc_like( '_transient_eppro_' ) . '%', $wpdb->esc_like( '_transient_timeout_eppro_' ) . '%' ) ); wp_cache_flush(); } register_deactivation_hook( __FILE__, 'eppro_deactivate' );