Plugin Directory

Changeset 3429643


Ignore:
Timestamp:
12/30/2025 01:34:29 PM (3 months ago)
Author:
rijensky
Message:

Release 2.4.0

Location:
otppal/trunk
Files:
29 added
13 deleted
4 edited

Legend:

Unmodified
Added
Removed
  • otppal/trunk/assets/js/integrations/otppal-elementor-pro.js

    r3423725 r3429643  
    2929            }
    3030
    31             // Find the original input
     31            // Check if this is a custom otppal_phone field (already has the container)
     32            const $existingContainer = $field.find('.otppal-phone-field[data-otppal-phone-field="true"]');
     33            const $hiddenInput = $field.find('input.otppal-original-phone-input, input[type="hidden"]').first();
     34
     35            if ($existingContainer.length > 0) {
     36                // This is a custom otppal_phone field - initialize the existing container
     37                const phoneFormat = $existingContainer.data('phone-format') ||
     38                    (typeof otppalConfig !== 'undefined' ? otppalConfig.phoneFormat : 'international') ||
     39                    'international';
     40                const defaultCountry = $existingContainer.data('default-country') ||
     41                    (typeof otppalConfig !== 'undefined' ? otppalConfig.defaultCountry : 'US') ||
     42                    'US';
     43
     44                // Initialize phone input on existing container
     45                const phoneInput = new OtpPalPhoneInput($existingContainer, {
     46                    format: phoneFormat,
     47                    defaultCountry: defaultCountry,
     48                    onValidate: function(valid, phone, error) {
     49                        // Update hidden input with formatted phone number
     50                        if ($hiddenInput.length > 0) {
     51                            if (valid) {
     52                                $hiddenInput.val(phone);
     53                            } else {
     54                                $hiddenInput.val('');
     55                            }
     56                        }
     57                    }
     58                });
     59
     60                // Store reference
     61                embeddedPhoneFields[formId] = phoneInput;
     62                $field.data('otppal-initialized', true);
     63                return;
     64            }
     65
     66            // Legacy: Find the original input (for forms that use CSS classes on regular fields)
    3267            const $originalInput = $field.find('input[type="text"], input[type="tel"], input[type="email"], input[type="url"]').first();
    3368            if ($originalInput.length === 0) {
     
    97132
    98133    /**
     134     * Get phone number from tel field in form
     135     */
     136    function getPhoneFromTelField($form) {
     137        // Look for tel field (elementor-field-type-tel)
     138        const $telField = $form.find('.elementor-field-type-tel input[type="tel"]');
     139        if ($telField.length > 0) {
     140            const phoneValue = $telField.val();
     141            if (phoneValue && phoneValue.trim()) {
     142                return phoneValue.trim();
     143            }
     144        }
     145        return null;
     146    }
     147
     148    /**
    99149     * Check if form has OTP verification enabled
    100150     */
     
    105155        }
    106156
    107         // Check for embedded phone field
     157        // Check for embedded phone field (by class)
    108158        if ($form.find('.elementor-field-group.otppal_phone').length > 0) {
     159            return true;
     160        }
     161
     162        // Fallback: Check for otppal_phone field type
     163        if ($form.find('.elementor-field-type-otppal_phone').length > 0) {
    109164            return true;
    110165        }
     
    131186        }
    132187       
    133         // Remove our submit handler temporarily to allow normal submission
     188        // Remove our handlers temporarily to allow normal submission
    134189        $form.off('submit.otppal');
     190        $form.find('button[type="submit"], input[type="submit"]').off('click.otppal');
    135191       
    136192        // Use a small delay to ensure lightbox is hidden before form submission
    137193        setTimeout(function() {
    138             // Trigger Elementor's form submission
     194            // Get the submit button
    139195            const $submitButton = $form.find('button[type="submit"], input[type="submit"]');
     196           
    140197            if ($submitButton.length) {
    141                 $submitButton.click();
     198                // Create a new click event that will trigger Elementor's handler
     199                const clickEvent = new jQuery.Event('click', {
     200                    bubbles: true,
     201                    cancelable: true
     202                });
     203               
     204                // Trigger the click event on the button
     205                // This should trigger Elementor's form submission handler
     206                $submitButton.trigger(clickEvent);
     207               
     208                // If that doesn't work, try triggering submit directly
     209                if (!clickEvent.isDefaultPrevented()) {
     210                    $form.trigger('submit');
     211                }
    142212            } else {
    143213                // Fallback: trigger submit event
    144                 $form.submit();
     214                $form.trigger('submit');
    145215            }
    146216           
     
    150220            }, 2000);
    151221        }, 100);
     222    }
     223
     224    // Hook into forms immediately, before Elementor initializes
     225    function attachFormHandlers() {
     226        // Find all Elementor forms and attach handlers directly
     227        $('form.elementor-form').each(function() {
     228            const $form = $(this);
     229           
     230            // Skip if already processed
     231            if ($form.data('otppal-handlers-attached')) {
     232                return;
     233            }
     234           
     235            $form.data('otppal-handlers-attached', true);
     236           
     237            // Attach click handler to submit button
     238            $form.find('button[type="submit"], input[type="submit"]').on('click.otppal', function(e) {
     239                const $button = $(this);
     240               
     241                if (!isFormEnabled($form)) {
     242                    return;
     243                }
     244                if (isVerifying) {
     245                    return;
     246                }
     247               
     248                e.preventDefault();
     249                e.stopImmediatePropagation();
     250                currentForm = $form;
     251               
     252                let phoneNumber = getEmbeddedPhoneNumber($form);
     253                if (!phoneNumber) {
     254                    phoneNumber = getPhoneFromTelField($form);
     255                }
     256               
     257                if (phoneNumber) {
     258                    lightbox.show(phoneNumber);
     259                } else {
     260                    lightbox.show();
     261                }
     262               
     263                return false;
     264            });
     265           
     266            // Attach submit handler directly to form
     267            $form.on('submit.otppal', function(e) {
     268                if (!isFormEnabled($form)) {
     269                    return;
     270                }
     271                if (isVerifying) {
     272                    return;
     273                }
     274               
     275                e.preventDefault();
     276                e.stopImmediatePropagation();
     277                currentForm = $form;
     278               
     279                let phoneNumber = getEmbeddedPhoneNumber($form);
     280                if (!phoneNumber) {
     281                    phoneNumber = getPhoneFromTelField($form);
     282                }
     283               
     284                if (phoneNumber) {
     285                    lightbox.show(phoneNumber);
     286                } else {
     287                    lightbox.show();
     288                }
     289               
     290                return false;
     291            });
     292        });
    152293    }
    153294
     
    188329                        $hiddenInput.val(phone);
    189330                    }
     331                   
     332                    // Also update the otppal_phone field's hidden input if it exists
     333                    const $phoneField = currentForm.find('.elementor-field-group.otppal_phone');
     334                    if ($phoneField.length > 0) {
     335                        const $phoneHiddenInput = $phoneField.find('input.otppal-original-phone-input, input[type="hidden"]').first();
     336                        if ($phoneHiddenInput.length > 0) {
     337                            $phoneHiddenInput.val(phone);
     338                        }
     339                    }
    190340                }
    191341               
     
    200350        // Initialize embedded phone fields
    201351        initializeEmbeddedPhoneFields();
     352       
     353        // Attach form handlers immediately
     354        attachFormHandlers();
    202355
    203356        // Re-initialize when Elementor forms are loaded dynamically
    204357        $(document).on('elementor/popup/show', function() {
    205             setTimeout(initializeEmbeddedPhoneFields, 100);
     358            setTimeout(function() {
     359                initializeEmbeddedPhoneFields();
     360                attachFormHandlers();
     361            }, 100);
    206362        });
    207363
    208364        // Also listen for Elementor's form render event
    209365        $(document).on('elementor/frontend/init', function() {
    210             setTimeout(initializeEmbeddedPhoneFields, 100);
    211         });
     366            setTimeout(function() {
     367                initializeEmbeddedPhoneFields();
     368                attachFormHandlers();
     369            }, 100);
     370        });
     371       
     372        // Also watch for dynamically added forms
     373        if (typeof MutationObserver !== 'undefined') {
     374            const observer = new MutationObserver(function(mutations) {
     375                attachFormHandlers();
     376            });
     377            observer.observe(document.body, {
     378                childList: true,
     379                subtree: true
     380            });
     381        }
    212382
    213383        /**
    214          * Intercept Elementor Pro form submission
     384         * Intercept Elementor Pro form button click
     385         * This runs before the submit event, so we can intercept before Elementor's handler
     386         */
     387        $(document).on('click.otppal', 'form.elementor-form button[type="submit"], form.elementor-form input[type="submit"]', function(e) {
     388            const $button = $(this);
     389            const $form = $button.closest('form.elementor-form');
     390           
     391            // Check if OTP is enabled for this form
     392            if (!isFormEnabled($form)) {
     393                return; // Let form submit normally
     394            }
     395
     396            // Don't intercept if we're already verifying or form is being submitted after verification
     397            if (isVerifying) {
     398                return; // Let form submit normally
     399            }
     400           
     401            // Prevent default submission
     402            e.preventDefault();
     403            e.stopImmediatePropagation();
     404           
     405            // Store form data
     406            currentForm = $form;
     407
     408            // Try to get phone number from embedded phone field first
     409            let phoneNumber = getEmbeddedPhoneNumber($form);
     410           
     411            // If no embedded phone field, try to get from tel field
     412            if (!phoneNumber) {
     413                phoneNumber = getPhoneFromTelField($form);
     414            }
     415           
     416            if (phoneNumber) {
     417                // Form has phone number - show modal in loading state and request OTP
     418                lightbox.show(phoneNumber);
     419            } else {
     420                // No phone number found - show modal with phone input
     421                lightbox.show();
     422            }
     423           
     424            return false; // Additional safety
     425        });
     426
     427        /**
     428         * Also intercept form submit event as backup
     429         * This uses stopImmediatePropagation to prevent Elementor's handler from running
    215430         */
    216431        $(document).on('submit.otppal', 'form.elementor-form', function(e) {
     
    226441                return; // Let form submit normally
    227442            }
    228 
    229             // Prevent default submission
     443           
     444            // Prevent default submission - this must happen BEFORE Elementor's handler
    230445            e.preventDefault();
    231             e.stopPropagation();
     446            e.stopImmediatePropagation(); // Stop other handlers from running
    232447
    233448            // Store form data
    234449            currentForm = $form;
    235450
    236             // Check if form has embedded phone field
    237             const embeddedPhone = getEmbeddedPhoneNumber($form);
    238             if (embeddedPhone) {
    239                 // Form has embedded phone field - show modal in loading state and request OTP
    240                 lightbox.show(embeddedPhone);
     451            // Try to get phone number from embedded phone field first
     452            let phoneNumber = getEmbeddedPhoneNumber($form);
     453           
     454            // If no embedded phone field, try to get from tel field
     455            if (!phoneNumber) {
     456                phoneNumber = getPhoneFromTelField($form);
     457            }
     458           
     459            if (phoneNumber) {
     460                // Form has phone number - show modal in loading state and request OTP
     461                lightbox.show(phoneNumber);
    241462            } else {
    242                 // No embedded phone field - show modal with phone input
     463                // No phone number found - show modal with phone input
    243464                lightbox.show();
    244465            }
     466           
     467            return false; // Additional safety
    245468        });
    246469
  • otppal/trunk/includes/class-otppal-user-lookup.php

    r3427655 r3429643  
    4646            // Load country codes file if not already loaded
    4747            if (self::$country_dial_codes === null) {
    48                 $codes_file = OTPPAL_PATH . 'data/country-dial-codes.php';
     48                $codes_file = OTPPAL_PATH . 'includes/data/country-dial-codes.php';
    4949                if (file_exists($codes_file)) {
    5050                    self::$country_dial_codes = require $codes_file;
  • otppal/trunk/otppal.php

    r3427662 r3429643  
    1919}
    2020
     21// Suppress all error output during activation to prevent "headers already sent" errors
     22// This is a workaround for ACF/Polylang bugs where they output notices during plugin activation
     23if (isset($_GET['action']) && $_GET['action'] === 'activate' && isset($_GET['plugin'])) {
     24    $plugin = sanitize_text_field($_GET['plugin']);
     25    if (strpos($plugin, 'otppal') !== false) {
     26        // Suppress all error output
     27        @ini_set('display_errors', 0);
     28        @ini_set('display_startup_errors', 0);
     29        error_reporting(0);
     30
     31        // Also start output buffering as backup
     32        if (ob_get_level() === 0) {
     33            ob_start();
     34            register_shutdown_function(function () {
     35                if (ob_get_level() > 0) {
     36                    ob_end_clean();
     37                }
     38            });
     39        }
     40    }
     41}
     42
    2143// Define plugin constants
    2244if (!defined('OTPPAL_VERSION')) {
    23     define('OTPPAL_VERSION', '2.3.9');
     45    define('OTPPAL_VERSION', '2.4.0');
    2446}
    2547if (!defined('OTPPAL_PATH')) {
     
    3254    define('OTPPAL_BASENAME', plugin_basename(__FILE__));
    3355}
    34 
    35 // Server URL - hardcoded (from docker-compose.yml: wordpress service port 8000:80)
    3656if (!defined('OTPPAL_SERVER_URL')) {
    37     define('OTPPAL_SERVER_URL', 'http://localhost:8000');
     57    define('OTPPAL_SERVER_URL', 'https://otppal.com');
    3858}
    3959
     
    7797        require_once OTPPAL_PATH . 'includes/class-otppal-user-lookup.php';
    7898
    79         // Load form integrations
    80         require_once OTPPAL_PATH . 'includes/integrations/class-otppal-cf7.php';
    81         require_once OTPPAL_PATH . 'includes/integrations/class-otppal-gravity-forms.php';
    82         require_once OTPPAL_PATH . 'includes/integrations/class-otppal-wpforms.php';
    83         require_once OTPPAL_PATH . 'includes/integrations/class-otppal-fluent-forms.php';
    84         require_once OTPPAL_PATH . 'includes/integrations/class-otppal-ninja-forms.php';
    85         require_once OTPPAL_PATH . 'includes/integrations/class-otppal-formidable-forms.php';
    86         require_once OTPPAL_PATH . 'includes/integrations/class-otppal-sureforms.php';
    87         require_once OTPPAL_PATH . 'includes/integrations/class-otppal-elementor-pro.php';
    88 
    89         // Initialize form integrations (each concrete class checks if its plugin is active)
    90         OtpPal_CF7::get_instance();
    91         OtpPal_Gravity_Forms::get_instance();
    92         OtpPal_WPForms::get_instance();
    93         OtpPal_Fluent_Forms::get_instance();
    94         OtpPal_Ninja_Forms::get_instance();
    95         OtpPal_Formidable_Forms::get_instance();
    96         OtpPal_SureForms::get_instance();
    97         OtpPal_Elementor_Pro::get_instance();
     99        // Load and initialize all form integrations via boot file
     100        require_once OTPPAL_PATH . 'includes/integrations/class-otppal-integrations.php';
    98101
    99102        // Load login integration
    100         require_once OTPPAL_PATH . 'includes/integrations/class-otppal-login.php';
     103        require_once OTPPAL_PATH . 'includes/integrations/wp-auth/class-otppal-login.php';
    101104        OtpPal_Login::get_instance();
    102105
    103106        // Load sign-up integration (only if OTP login is enabled)
    104107        if (get_option('otppal_enable_otp_login', '0') === '1') {
    105             require_once OTPPAL_PATH . 'includes/integrations/class-otppal-signup.php';
     108            require_once OTPPAL_PATH . 'includes/integrations/wp-auth/class-otppal-signup.php';
    106109            OtpPal_Signup::get_instance();
    107110        }
  • otppal/trunk/readme.txt

    r3428524 r3429643  
    66Tested up to: 6.9
    77Requires PHP: 7.4
    8 Stable tag: 2.3.9
     8Stable tag: 2.4.0
    99License: GPL2
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    105105
    106106== Changelog ==
     107= 2.4.0 =
     108* Elementor pro integration
     109* Fix plugin activation issue
     110* Improve plugin structure
     111
    107112= 2.3.9 =
    108113* Enhanced phone number lookup to support both billing and shipping phone fields
     
    165170
    166171== Upgrade Notice ==
     172= 2.4.0 =
     173Full support for Elementor pro forms, better stability.
     174
    167175= 2.3.9 =
    168176Enhanced phone number matching and improved error handling in OTP verification flow.
Note: See TracChangeset for help on using the changeset viewer.