is_compatible() ) { // AAFE FIX: add version compatibility check
return;
}
$this->includes();
$this->register_hooks();
}
/**
* Check compatibility.
*/
public function is_compatible() { // AAFE FIX: check version compatibility method
// Check if Elementor installed and activated
if ( ! did_action( 'elementor/loaded' ) ) {
add_action( 'admin_notices', [ $this, 'admin_notice_missing_main_plugin' ] );
return false;
}
// Check Elementor version
if ( ! version_compare( ELEMENTOR_VERSION, self::MINIMUM_ELEMENTOR_VERSION, '>=' ) ) {
add_action( 'admin_notices', [ $this, 'admin_notice_minimum_elementor_version' ] );
return false;
}
// Check PHP version
if ( version_compare( PHP_VERSION, self::MINIMUM_PHP_VERSION, '<' ) ) {
add_action( 'admin_notices', [ $this, 'admin_notice_minimum_php_version' ] );
return false;
}
return true;
}
public function admin_notice_missing_main_plugin() { // AAFE FIX: admin notice method
if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Checking activate param, no state change.
/* translators: 1: plugin name, 2: required plugin name */
$message = sprintf( esc_html__( '"%1$s" requires "%2$s" to be installed and activated.', 'addronix-addons-for-elementor' ), 'Addronix Addons for Elementor', 'Elementor' );
printf( '
', $message ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Message is already escaped via esc_html__.
}
public function admin_notice_minimum_elementor_version() { // AAFE FIX: admin notice method
if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Checking activate param, no state change.
/* translators: 1: plugin name, 2: required plugin name, 3: required version */
$message = sprintf( esc_html__( '"%1$s" requires "%2$s" version %3$s or greater.', 'addronix-addons-for-elementor' ), 'Addronix Addons for Elementor', 'Elementor', self::MINIMUM_ELEMENTOR_VERSION );
printf( '', $message ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Message is already escaped via esc_html__.
}
public function admin_notice_minimum_php_version() { // AAFE FIX: admin notice method
if ( isset( $_GET['activate'] ) ) unset( $_GET['activate'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Checking activate param, no state change.
/* translators: 1: plugin name, 2: required plugin name, 3: required version */
$message = sprintf( esc_html__( '"%1$s" requires "%2$s" version %3$s or greater.', 'addronix-addons-for-elementor' ), 'Addronix Addons for Elementor', 'PHP', self::MINIMUM_PHP_VERSION );
printf( '', $message ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Message is already escaped via esc_html__.
}
/**
* Prevent cloning of the singleton.
*/
private function __clone() {}
/**
* Prevent unserializing of the singleton.
*/
public function __wakeup() {
_doing_it_wrong( __FUNCTION__, esc_html__( 'Unserializing instances of this class is forbidden.', 'addronix-addons-for-elementor' ), '1.0.8' );
}
/**
* Require all dependency files.
*/
private function includes() {
require_once AAFE_PATH . 'includes/class-aafe-template.php'; // Unified CPT (must load first)
require_once AAFE_PATH . 'includes/class-widgets-manager.php';
require_once AAFE_PATH . 'includes/class-display-conditions.php';
// class-custom-code.php removed: per-element Custom CSS injection is not permitted on WordPress.org
require_once AAFE_PATH . 'includes/class-header-footer.php';
require_once AAFE_PATH . 'includes/class-advanced-backgrounds.php';
require_once AAFE_PATH . 'includes/class-loop-builder.php';
require_once AAFE_PATH . 'includes/class-mega-menu.php';
require_once AAFE_PATH . 'includes/class-menu-badges.php'; // AAFE FIX BUG#17: added require here
require_once AAFE_PATH . 'includes/class-sticky-elements.php';
require_once AAFE_PATH . 'includes/class-dynamic-tags.php';
// Only load the admin class in the admin context.
if ( is_admin() ) {
require_once AAFE_PATH . 'admin/class-admin.php';
}
}
/**
* Register all WordPress / Elementor hooks.
*/
private function register_hooks() {
// Register custom Elementor widget category.
add_action( 'elementor/elements/categories_registered', array( $this, 'register_widget_categories' ) );
// Initialize the widgets manager after Elementor widgets are registered.
add_action( 'elementor/widgets/register', array( $this, 'init_widgets_manager' ) );
// Register custom controls.
add_action( 'elementor/controls/register', array( $this, 'register_controls' ) );
// Register post dynamic tags (Post Title, Featured Image, ...) used by Loop Item templates.
add_action( 'elementor/dynamic_tags/register', array( '\AddronixAddons\Dynamic_Tags', 'register' ) );
// Initialize display conditions extension.
add_action( 'elementor/init', array( $this, 'init_display_conditions' ) );
// Initialize all advanced extensions.
add_action( 'elementor/init', array( $this, 'init_extensions' ) );
// Auto-enable all AAFE CPTs in Elementor's supported post types list.
add_action( 'elementor/init', array( $this, 'register_elementor_cpt_support' ) );
// Enqueue global frontend assets.
add_action( 'elementor/frontend/after_enqueue_styles', array( $this, 'enqueue_frontend_assets' ) ); // AAFE FIX: move to elementor/frontend/after_enqueue_styles
// Initialize admin panel.
if ( is_admin() ) {
new \AddronixAddons\Admin\Admin();
}
}
/**
* Automatically register the unified AAFE CPT with Elementor
* so it appears as ONE checkbox — "Addronix Addons Items" — in
* Elementor Settings → Post Types. One check enables everything.
*/
public function register_elementor_cpt_support() {
// NOTE: 'elementor_cpt_support' is Elementor's own built-in option key (not a custom prefix).
// We intentionally read and update this option to register our CPT with Elementor's builder.
// This is the standard WordPress approach for adding CPT support to Elementor.
$cpt_support = get_option( 'elementor_cpt_support', [ 'page', 'post' ] );
if ( ! in_array( \AddronixAddons\AAFE_Template::CPT, $cpt_support, true ) ) {
$cpt_support[] = \AddronixAddons\AAFE_Template::CPT;
update_option( 'elementor_cpt_support', $cpt_support );
}
}
/**
* Register the "Addronix Addons for Elementor" widget category in Elementor.
*
* @param \Elementor\Elements_Manager $elements_manager Elementor elements manager.
*/
public function register_widget_categories( $elements_manager ) {
$elements_manager->add_category(
'addronix-addons-for-elementor',
array(
'title' => esc_html__( 'Addronix Addons for Elementor', 'addronix-addons-for-elementor' ),
'icon' => 'eicon-flash',
)
);
// Unlock & promote "Site" (theme-elements) category ONLY when editing or creating a Header or Footer template.
// On normal pages/posts, leave categories in their standard order.
if ( ! $this->is_editing_header_or_footer() ) {
return;
}
// Unlock & promote "Site" (theme-elements) category so Site widgets appear at top for headers/footers
try {
$ref = new \ReflectionClass( $elements_manager );
if ( $ref->hasProperty( 'categories' ) ) {
$prop = $ref->getProperty( 'categories' );
$prop->setAccessible( true );
$cats = $prop->getValue( $elements_manager );
if ( is_array( $cats ) && isset( $cats['theme-elements'] ) ) {
$site_cat = $cats['theme-elements'];
$site_cat['active'] = true;
unset( $site_cat['promotion'] );
unset( $cats['theme-elements'] );
$new_cats = [];
if ( isset( $cats['favorites'] ) ) {
$new_cats['favorites'] = $cats['favorites'];
unset( $cats['favorites'] );
}
$new_cats['theme-elements'] = $site_cat;
$new_cats = array_merge( $new_cats, $cats );
$prop->setValue( $elements_manager, $new_cats );
}
}
} catch ( \Throwable $e ) {
// Fail-safe silently
}
}
/**
* Check whether the current Elementor editor session is editing a Header or Footer template.
*
* @return bool True if editing a Header or Footer, false otherwise.
*/
private function is_editing_header_or_footer() {
$post_id = 0;
// 1. Direct query parameters in admin / preview
if ( ! empty( $_GET['post'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$post_id = absint( $_GET['post'] );
} elseif ( ! empty( $_GET['elementor-preview'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$post_id = absint( $_GET['elementor-preview'] );
} elseif ( ! empty( $_REQUEST['editor_post_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$post_id = absint( $_REQUEST['editor_post_id'] );
} elseif ( ! empty( $_REQUEST['post_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$post_id = absint( $_REQUEST['post_id'] );
}
// 2. Elementor Editor API fallback
if ( ! $post_id && class_exists( '\Elementor\Plugin' ) ) {
if ( isset( \Elementor\Plugin::$instance->editor ) && method_exists( \Elementor\Plugin::$instance->editor, 'get_post_id' ) ) {
$post_id = \Elementor\Plugin::$instance->editor->get_post_id();
}
if ( ! $post_id && isset( \Elementor\Plugin::$instance->documents ) && method_exists( \Elementor\Plugin::$instance->documents, 'get_current' ) ) {
$doc = \Elementor\Plugin::$instance->documents->get_current();
if ( $doc && method_exists( $doc, 'get_main_id' ) ) {
$post_id = $doc->get_main_id();
}
}
}
// 3. New post screen check before post ID is assigned
if ( ! $post_id ) {
if ( isset( $_GET['post_type'] ) && 'aafe_item' === $_GET['post_type'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$type = isset( $_GET['aafe_type'] ) ? sanitize_key( wp_unslash( $_GET['aafe_type'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
return in_array( $type, [ 'header', 'footer', 'header-footer' ], true );
}
return false;
}
// Check post type of the post being edited
$post_type = get_post_type( $post_id );
// AAFE unified template CPT check
if ( 'aafe_item' === $post_type ) {
$type = get_post_meta( $post_id, \AddronixAddons\AAFE_Template::META_TYPE, true );
if ( ! $type ) {
$type = get_post_meta( $post_id, '_aafe_hf_type', true );
}
// If meta is not saved yet, check URL parameter
if ( ! $type && isset( $_GET['aafe_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$type = sanitize_key( wp_unslash( $_GET['aafe_type'] ) );
}
// Exclude non-header/footer types like loop and mega-menu
if ( in_array( $type, [ 'loop', 'mega-menu' ], true ) ) {
return false;
}
// If empty or explicitly header/footer/header-footer, it belongs to header-footer builder
return true;
}
// Check Elementor Pro Theme Builder document type if active
if ( class_exists( '\Elementor\Plugin' ) && isset( \Elementor\Plugin::$instance->documents ) ) {
$doc = \Elementor\Plugin::$instance->documents->get( $post_id );
if ( $doc && method_exists( $doc, 'get_name' ) && in_array( $doc->get_name(), [ 'header', 'footer' ], true ) ) {
return true;
}
}
return false;
}
/**
* Register custom Elementor controls.
*
* @param \Elementor\Controls_Manager $controls_manager Elementor controls manager.
*/
public function register_controls( $controls_manager ) {
require_once AAFE_PATH . 'includes/controls/class-control-display-conditions.php';
$controls_manager->register( new \AddronixAddons\Controls\Control_Display_Conditions() );
}
private $widgets_registered = false;
/**
* Widgets manager instance (declared to avoid PHP 8.2+ dynamic property deprecation).
*
* @var Widgets_Manager|null
*/
private $widgets_manager = null;
/**
* Initialize the Widgets Manager, which scans and registers all widgets.
*
* @param \Elementor\Widgets_Manager $widgets_manager Elementor widgets manager.
*/
public function init_widgets_manager( $widgets_manager ) {
if ( $this->widgets_registered ) {
return;
}
$this->widgets_registered = true;
// Check if plugin is globally enabled.
$settings = get_option( 'aafe_settings', array() );
$plugin_enabled = isset( $settings['plugin_enabled'] ) ? $settings['plugin_enabled'] : 'yes';
if ( 'yes' !== $plugin_enabled ) {
return;
}
// AAFE FIX BUG#14: Removed duplicate require_once — file is already loaded in includes().
$this->widgets_manager = new Widgets_Manager( $widgets_manager );
}
/**
* Initialize Display Conditions Extension.
*/
public function init_display_conditions() {
$settings = get_option( 'aafe_settings', array() );
$plugin_enabled = isset( $settings['plugin_enabled'] ) ? $settings['plugin_enabled'] : 'yes';
if ( 'yes' !== $plugin_enabled ) {
return;
}
// AAFE FIX BUG#2: Respect disabled_extensions setting for Display Conditions.
$disabled = isset( $settings['disabled_extensions'] ) && is_array( $settings['disabled_extensions'] )
? $settings['disabled_extensions'] : array();
if ( in_array( 'ext-display-conditions', $disabled, true ) ) {
return;
}
\AddronixAddons\Display_Conditions::instance();
}
/**
* Initialize all advanced extensions (Custom Code, Header/Footer, etc.).
*/
public function init_extensions() {
$settings = get_option( 'aafe_settings', array() );
$plugin_enabled = isset( $settings['plugin_enabled'] ) ? $settings['plugin_enabled'] : 'yes';
if ( 'yes' !== $plugin_enabled ) {
return;
}
// AAFE FIX BUG#2: Read disabled_extensions and conditionally init each extension.
$disabled = isset( $settings['disabled_extensions'] ) && is_array( $settings['disabled_extensions'] )
? $settings['disabled_extensions'] : array();
\AddronixAddons\AAFE_Template::instance(); // Unified CPT — must init before sub-classes
// Custom_Code::instance() removed — arbitrary CSS injection not permitted on WordPress.org
if ( ! in_array( 'ext-hf-builder', $disabled, true ) ) {
\AddronixAddons\Header_Footer::instance();
}
if ( ! in_array( 'ext-adv-backgrounds', $disabled, true ) ) {
\AddronixAddons\Advanced_Backgrounds::instance();
}
if ( ! in_array( 'ext-loop-builder', $disabled, true ) ) {
\AddronixAddons\Loop_Builder::instance();
}
if ( ! in_array( 'ext-mega-menu', $disabled, true ) ) {
\AddronixAddons\Mega_Menu::instance();
}
// AAFE FIX BUG#17: Menu_Badges moved here from class-menu-badges.php self-init.
if ( ! in_array( 'ext-menu-badges', $disabled, true ) ) {
\AddronixAddons\Menu_Badges::instance();
}
if ( ! in_array( 'ext-sticky-elements', $disabled, true ) ) {
\AddronixAddons\Sticky_Elements::instance();
}
}
/**
* Enqueue shared frontend CSS/JS (only when needed on the frontend).
*/
public function enqueue_frontend_assets() {
// AAFE FIX BUG#3: Respect performance_mode — only load on Elementor-built pages.
$settings = get_option( 'aafe_settings', array() );
$perf_mode = isset( $settings['performance_mode'] ) ? $settings['performance_mode'] : 'no';
if ( 'yes' === $perf_mode ) {
$is_preview = class_exists( '\Elementor\Plugin' )
&& \Elementor\Plugin::$instance->preview->is_preview_mode();
$is_elementor_page = class_exists( '\Elementor\Plugin' )
&& \Elementor\Plugin::$instance->db->is_built_with_elementor( get_the_ID() );
if ( ! $is_preview && ! $is_elementor_page ) {
return;
}
}
// Enqueue the shared frontend stylesheet.
wp_enqueue_style(
'aafe-frontend',
AAFE_URL . 'assets/css/frontend.css',
array(),
AAFE_VERSION
);
// Enqueue shared frontend script.
wp_enqueue_script(
'aafe-frontend',
AAFE_URL . 'assets/js/frontend.js',
array( 'jquery' ),
AAFE_VERSION, array( 'in_footer' => true, 'strategy' => 'defer' ) );
}
}