Snapshot: MLS sync fixes, image refresh, plugin/theme updates

MLS plugin fixes from this session:
- Fix silent insert failures: location column NOT NULL was rejecting wpdb->insert calls,
  causing ~18k new properties since Dec 2025 to be lost. Inserts now build raw SQL
  with ST_PointFromText so the spatial column is populated atomically.
- Auto-refresh expired media URLs in MLS_Media_Handler::fetch_and_cache(), guarded by
  a property-level GET_LOCK so concurrent fetches share one API refresh.
- Normalize WP_Error to null in mls_get_property_image() so callers can rely on the
  documented string|null contract.
- Support comma-separated property_type filters in MLS_Query and MLS_Cluster so the
  homepage "View All Commercial" link (?property_type=Commercial+Sale,Land,Farm)
  actually filters correctly.
- Incremental sync now looks back 10 minutes past the latest modification timestamp
  as a safety margin against missed records.
- Smart sync exits silently (info-level, not warning) when a full sync is in progress.

Operational:
- New cron: weekly full sync Sundays at 3 AM (/usr/local/bin/mls-full-sync).
- New cron: hourly 2GB cap on mls-thumbnails/ and cache/transformed-images/
  (/usr/local/bin/mls-image-cache-cap).
- Logrotate config for wp-content/debug.log (2-day retention, daily rotation,
  delaycompress).

Repo policy:
- CLAUDE.md updated with explicit "commit everything except build artifacts" policy.
- .gitignore: untrack runtime image caches and debug.log rotations.

Other modifications in this snapshot are pre-existing in-flight theme/plugin/db_content_updates work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-04-29 15:32:23 +00:00
parent 57b752f54e
commit b6df4dbb92
5385 changed files with 838580 additions and 2416 deletions
@@ -0,0 +1,669 @@
/* global wpforms_builder, wpforms_builder_stripe, WPFormsBuilderPaymentsUtils */
// noinspection ES6ConvertVarToLetConst
/**
* Stripe builder function.
*
* @since 1.8.4
*/
// eslint-disable-next-line no-var
var WPFormsStripeModernBuilder = window.WPFormsStripeModernBuilder || ( function( document, window, $ ) {
/**
* Elements holder.
*
* @since 1.8.4
*
* @type {Object}
*/
let el = {};
/**
* Public functions and properties.
*
* @since 1.8.4
*
* @type {Object}
*/
const app = {
/**
* Start the engine.
*
* @since 1.8.4
*/
init() {
$( app.ready );
},
/**
* Initialized once the DOM is fully loaded.
*
* @since 1.8.4
*/
ready() {
app.customMetadataActions();
if ( app.isLegacySettings() ) {
return;
}
// Cache DOM elements.
el = {
$alert: $( '#wpforms-stripe-credit-card-alert' ),
$panelContent: $( '#wpforms-panel-content-section-payment-stripe' ),
$feeNotice: $( '.wpforms-stripe-notice-info' ),
};
app.bindUIActions();
app.bindPlanUIActions();
if ( ! wpforms_builder_stripe.is_pro ) {
const baseSelector = '.wpforms-panel-content-section-stripe',
toggleInput = `${ baseSelector } .wpforms-panel-content-section-payment-toggle input`,
planNameInput = `${ baseSelector } .wpforms-panel-content-section-payment-plan-name input`;
$( toggleInput ).each( WPFormsBuilderPaymentsUtils.toggleContent );
$( planNameInput ).each( WPFormsBuilderPaymentsUtils.checkPlanName );
$( '#wpforms-panel-payments' )
.on( 'click', toggleInput, WPFormsBuilderPaymentsUtils.toggleContent )
.on( 'click', `${ baseSelector } .wpforms-panel-content-section-payment-plan-head-buttons-toggle`, WPFormsBuilderPaymentsUtils.togglePlan )
.on( 'click', `${ baseSelector } .wpforms-panel-content-section-payment-plan-head-buttons-delete`, WPFormsBuilderPaymentsUtils.deletePlan )
.on( 'input', planNameInput, WPFormsBuilderPaymentsUtils.renamePlan )
.on( 'focusout', planNameInput, WPFormsBuilderPaymentsUtils.checkPlanName );
}
},
/**
* Process custom metadata actions.
*
* @since 1.9.6
*/
customMetadataActions() {
$( '#wpforms-panel-payments' )
.on( 'focusout', '.wpforms-panel-field-stripe-custom-metadata-meta-key', function() {
// Remove invalid characters for meta key.
$( this ).val( $( this ).val().replace( /[^\p{L}\p{N}_-]/gu, '' ) );
} )
// Add metadata row.
.on( 'click', '.wpforms-panel-content-section-stripe-custom-metadata-add', function( e ) {
e.preventDefault();
const $table = $( this ).parents( '.wpforms-panel-content-section-stripe-custom-metadata-table' ),
$lastRow = $table.find( 'tr' ).last(),
$clone = $lastRow.clone( true ),
lastID = $lastRow.data( 'key' ),
nextID = lastID + 1;
// Update row key ID.
$clone.attr( 'data-key', nextID );
// Increment the counter.
$clone.html( $clone.html().replaceAll( '[' + lastID + ']', '[' + nextID + ']' ).replaceAll( '-' + lastID + '-', '-' + nextID + '-' ) );
// Clear values.
$clone.find( 'select, input' ).val( '' );
// Re-enable "delete" button.
$clone.find( '.wpforms-panel-content-section-stripe-custom-metadata-delete' ).removeClass( 'hidden' );
// Remove error classes.
$clone.find( '.wpforms-required-field-error' ).removeClass( 'wpforms-required-field-error' );
// Put it back to the table.
$table.find( 'tbody' ).append( $clone.get( 0 ) );
} )
// Delete metadata row.
.on( 'click', '.wpforms-panel-content-section-stripe-custom-metadata-delete', function( e ) {
e.preventDefault();
$( this ).parents( '.wpforms-panel-content-section-stripe-custom-metadata-table tr' ).remove();
} );
},
/**
* Process various events as a response to UI interactions.
*
* @since 1.8.4
*/
bindUIActions() {
const $builder = $( '#wpforms-builder' );
$builder.on( 'wpformsFieldDelete', app.disableNotifications )
.on( 'wpformsSaved', app.requiredFieldsCheck )
.on( 'wpformsFieldAdd', app.fieldAdded )
.on( 'wpformsFieldDelete', app.fieldDeleted )
.on( 'wpformsPaymentsPlanCreated', app.toggleMultiplePlansWarning )
.on( 'wpformsPaymentsPlanCreated', app.bindPlanUIActions )
.on( 'wpformsPaymentsPlanDeleted', app.toggleMultiplePlansWarning );
el.$panelContent
.find( '.wpforms-panel-content-section-payment-one-time' )
.on( 'change', '.wpforms-panel-field-stripe-custom-metadata-meta-key', app.resetCustomMetaKeyErrorClass );
},
/**
* Bind plan UI actions.
*
* @since 1.9.5
*/
bindPlanUIActions() {
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan-body .wpforms-panel-field-select select[name*="email"]' ).on( 'change', app.resetEmailAlertErrorClass );
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan-body .wpforms-panel-field-stripe-custom-metadata-meta-key' ).on( 'change', app.resetCustomMetaKeyErrorClass );
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan-period select' ).on( 'change', app.resetCyclesValues );
},
/**
* On form save notify users about required fields.
*
* @since 1.8.4
*/
requiredFieldsCheck() {
const validationState = app.validateFields();
if ( ! validationState?.invalid ) {
return;
}
app.openAlert(
app.getAlertMessage( validationState.type ),
() => validationState.invalid.$element.focus()
);
},
/**
* Validates form fields based on their visibility and specific conditions.
*
* @since 1.9.8.3
*
* @return {Object|boolean} Returns an object representing the validation state or `true` if the panel content is hidden.
*/
validateFields() {
if ( el.$panelContent.hasClass( 'wpforms-hidden' ) ) {
return true;
}
// We want to determine which fields are not filled to display an appropriate error message.
// Also, it contains the type of the payment: one-time or recurring.
const validationState = {};
if ( $( '#wpforms-panel-field-stripe-enable_one_time' ).is( ':checked' ) ) {
app.validateOneTimeFields( validationState );
}
if ( $( '#wpforms-panel-field-stripe-enable_recurring' ).is( ':checked' ) ) {
app.validateRecurringFields( validationState );
}
return validationState;
},
/**
* Validates the one-time payment fields within the form's panel content.
*
* @since 1.9.8.3
*
* @param {Object} validationState The current validation state object.
*/
validateOneTimeFields( validationState ) {
const $oneTimeScope = el.$panelContent.find( '.wpforms-panel-content-section-payment-one-time' );
validationState.type = 'one-time';
app.validateCustomMetaTable( $oneTimeScope, validationState );
},
/**
* Validates recurring fields within the payment plan section of the panel content.
*
* @since 1.9.8.3
*
* @param {Object} validationState An object representing the current state of validation.
*/
validateRecurringFields( validationState ) {
validationState.type = validationState.type ? 'both' : 'recurring';
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan' ).each( function() {
const $plan = $( this );
app.validateEmailField( $plan, validationState );
app.validateCustomMetaTable( $plan, validationState );
} );
},
/**
* Validates the email field for a given plan and updates the validation state if the field is empty.
*
* @since 1.9.8.3
*
* @param {Object} $plan jQuery object representing the plan element.
* @param {Object} validationState Object representing the validation state that will be updated.
*/
validateEmailField( $plan, validationState ) {
const planId = $plan.data( 'plan-id' );
const $emailField = $( `#wpforms-panel-field-stripe-recurring-${ planId }-email` );
if ( $emailField.val() ) {
return;
}
$emailField.addClass( 'wpforms-required-field-error' );
validationState.invalid = validationState.invalid ?? {};
validationState.invalid.email = true;
if ( ! validationState.invalid.$element ) {
validationState.invalid.$element = $emailField;
}
},
/**
* Validates the custom metadata table rows within the specified scope.
*
* @since 1.9.8.3
*
* @param {Object} $scope The jQuery object representing the current scope containing the custom metadata table.
* @param {Object} validationState The object used to store the validation state for the custom metadata rows.
*
* @return {boolean|void} Returns `true` if no rows are present for validation, otherwise no return value.
*/
validateCustomMetaTable( $scope, validationState ) {
const $customMetaRows = $scope.find( '.wpforms-panel-content-section-stripe-custom-metadata-table tr[data-key]' );
if ( ! $customMetaRows.length ) {
return true;
}
$customMetaRows.each( function() {
const $row = $( this );
if ( app.isValidCustomMetaRow( $row ) ) {
return;
}
validationState.invalid = validationState.invalid ?? {};
validationState.invalid.customMeta = true;
if ( ! validationState.invalid.$element ) {
validationState.invalid.$element = $row.find( '.wpforms-panel-field-stripe-custom-metadata-meta-key' );
}
} );
},
/**
* Validates if a custom metadata row in the form is properly filled.
*
* @since 1.9.8.3
*
* @param {Object} $row jQuery object representing the custom metadata row to validate.
*
* @return {boolean} True if the custom metadata row is valid, otherwise false.
*/
isValidCustomMetaRow( $row ) {
const $metaKey = $row.find( '.wpforms-panel-field-stripe-custom-metadata-meta-key' );
const isValid = (
! $row.find( '.wpforms-panel-field-stripe-custom-metadata-object-type' ).val() ||
! $row.find( '.wpforms-panel-field-stripe-custom-metadata-meta-value' ).val() ||
$metaKey.val()
);
$metaKey.toggleClass( 'wpforms-required-field-error', ! isValid );
return isValid;
},
/**
* Maybe reset required email field error class.
*
* @since 1.9.5
*/
resetEmailAlertErrorClass() {
$( this ).toggleClass( 'wpforms-required-field-error', ! $( this ).val() );
},
/**
* Resets the error class for a custom meta key row.
*
* @since 1.9.8.3
*/
resetCustomMetaKeyErrorClass() {
const $row = $( this ).closest( 'tr[data-key]' );
app.isValidCustomMetaRow( $row );
},
/**
* Show alert for required recurring email field.
*
* @since 1.8.4
* @deprecated 1.9.8.3
*/
recurringEmailAlert() {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.recurringEmailAlert()" has been deprecated, please use the "WPFormsStripeModernBuilder.openAlert()" function instead!' );
app.openAlert( wpforms_builder.stripe_recurring_email );
},
/**
* Opens an alert dialog with a customized message and optional callback action.
*
* @since 1.9.8.3
*
* @param {string} alertMessage The message to be displayed in the alert dialog.
* @param {Function} onClose Optional callback function to execute when the alert is closed.
*/
openAlert( alertMessage, onClose = () => {} ) {
$.alert( {
title: wpforms_builder.stripe_recurring_heading,
content: alertMessage,
icon: 'fa fa-exclamation-circle',
type: 'red',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
action: onClose,
},
},
onOpen() {
$( '.wpforms-stripe-settings-redirect' ).on( 'click', app.settingsRedirect );
},
} );
},
/**
* Returns an alert message based on the specified payment type and Stripe visibility settings.
*
* @since 1.9.8.3
*
* @param {string} paymentType The payment type (e.g., 'one-time', 'recurring', or 'both') used to determine the alert message.
*
* @return {string} The appropriate alert message for the specified payment type.
*/
getAlertMessage( paymentType ) {
if ( ! $( '.wpforms-panel-content-section-stripe' ).is( ':visible' ) ) {
return wpforms_builder.stripe_recurring_settings;
}
const typesMap = {
'one-time': 'stripe_required_one_time_fields',
recurring: 'stripe_required_recurring_fields',
both: 'stripe_required_both_fields',
};
return wpforms_builder[ typesMap[ paymentType ] ?? typesMap.recurring ];
},
/**
* Redirect to the settings tab.
*
* @since 1.9.5
*/
settingsRedirect() {
// Open the Stripe settings tab.
$( '.wpforms-panel-payments-button' ).trigger( 'click' );
$( '.wpforms-panel-sidebar-section-stripe' ).trigger( 'click' );
// Scroll to the Stripe settings.
window.location.href = window.location.pathname + window.location.search + '#wpforms-panel-field-stripe-enable_recurring-wrap';
// Close the alert.
$( this ).closest( '.jconfirm-box' ).find( '.btn-confirm' ).trigger( 'click' );
},
/**
* Disable notifications.
*
* @since 1.8.4
*
* @param {Object} e Event object.
* @param {number} id Field ID.
* @param {string} type Field type.
*/
disableNotifications( e, id, type ) {
if ( ! app.isStripeField( type ) ) {
return;
}
if ( app.hasStripeCreditCardFieldInBuilder() ) {
return;
}
const $notificationWrap = $( '.wpforms-panel-content-section-notifications [id*="-stripe-wrap"]' );
$notificationWrap.find( 'input[id*="-stripe"]' ).prop( 'checked', false );
$notificationWrap.addClass( 'wpforms-hidden' );
},
/**
* Determine is legacy settings is loaded.
*
* @since 1.8.4
*
* @return {boolean} True is legacy settings loaded.
*/
isLegacySettings() {
return $( '#wpforms-panel-field-stripe-enable' ).length;
},
/**
* We have to do several actions when the "Stripe" field is added.
*
* @since 1.8.4
*
* @param {Object} e Event object.
* @param {number} id Field ID.
* @param {string} type Field type.
*/
fieldAdded( e, id, type ) {
if ( ! app.isStripeField( type ) ) {
return;
}
if ( ! app.hasStripeCreditCardFieldInBuilder() ) {
return;
}
app.settingsToggle( true );
el.$feeNotice.toggleClass( 'wpforms-hidden' );
},
/**
* We have to do several actions when the "Stripe" field is deleted.
*
* @since 1.8.4
*
* @param {Object} e Event object.
* @param {number} id Field ID.
* @param {string} type Field type.
*/
fieldDeleted( e, id, type ) {
if ( ! app.isStripeField( type ) ) {
return;
}
if ( app.hasStripeCreditCardFieldInBuilder() ) {
return;
}
app.settingsToggle( false );
app.disablePayments();
el.$feeNotice.toggleClass( 'wpforms-hidden' );
},
/**
* Determine if a field type is Stripe credit card.
*
* @since 1.8.4
*
* @param {string} type Field type.
*
* @return {boolean} True if Stripe field.
*/
isStripeField( type ) {
return type === wpforms_builder_stripe.field_slug;
},
/**
* Checks if the Stripe Credit Card field is in the form builder.
*
* @since 1.9.5
*
* @return {boolean} True if the Stripe Credit Card field is in the builder.
*/
hasStripeCreditCardFieldInBuilder() {
return $( `.wpforms-field.wpforms-field-${ wpforms_builder_stripe.field_slug }` ).length > 0;
},
/**
* Toggles visibility of multiple plans warning.
*
* @since 1.8.4
*/
toggleMultiplePlansWarning() {
el.$panelContent.find( '.wpforms-stripe-multiple-plans-warning' ).toggleClass( 'wpforms-hidden', el.$panelContent.find( '.wpforms-panel-content-section-payment-plan' ).length === 1 );
},
/**
* Toggles visibility of the Stripe addon settings.
*
* @since 1.8.4
*
* @param {boolean} display Show or hide settings.
*/
settingsToggle( display ) {
if (
! el.$alert.length &&
! el.$panelContent.length
) {
return;
}
el.$alert.toggleClass( 'wpforms-hidden', display );
el.$panelContent.toggleClass( 'wpforms-hidden', ! display );
},
/**
* Toggle payments content.
*
* @since 1.8.4
*/
toggleContent() {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.toggleContent()" has been deprecated, please use the new "WPFormsPaymentsUtils.toggleContent()" function instead!' );
WPFormsBuilderPaymentsUtils.toggleContent();
},
/**
* Toggle a plan content.
*
* @since 1.8.4
*/
togglePlan() {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.togglePlan()" has been deprecated, please use the new "WPFormsPaymentsUtils.togglePlan()" function instead!' );
WPFormsBuilderPaymentsUtils.togglePlan();
},
/**
* Delete a plan.
*
* @since 1.8.4
*/
deletePlan() {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.checkPlanName()" has been deprecated, please use the new "WPFormsPaymentsUtils.deletePlan()" function instead!' );
WPFormsBuilderPaymentsUtils.deletePlan();
},
/**
* Check a plan name on empty value.
*
* @since 1.8.4
*/
checkPlanName() {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.checkPlanName()" has been deprecated, please use the new "WPFormsPaymentsUtils.checkPlanName()" function instead!' );
WPFormsBuilderPaymentsUtils.checkPlanName();
},
/**
* Rename a plan.
*
* @since 1.8.4
*/
renamePlan() {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Function "WPFormsStripeModernBuilder.renamePlan()" has been deprecated, please use the new "WPFormsPaymentsUtils.renamePlan()" function instead!' );
WPFormsBuilderPaymentsUtils.renamePlan();
},
/**
* Make sure that "One-Time Payments" and "Recurring Payments" toggles are turned off.
*
* @since 1.9.5
*/
disablePayments() {
const toggleInput = $( '#wpforms-panel-field-stripe-enable_one_time, #wpforms-panel-field-stripe-enable_recurring' );
toggleInput.prop( 'checked', false ).trigger( 'change' ).each( WPFormsBuilderPaymentsUtils.toggleContent );
},
/**
* Reset cycles dropdown values based on the period selection.
*
* @since 1.9.8
*/
resetCyclesValues() {
const $el = $( this ),
$select = $el.closest( '.wpforms-panel-content-section-payment-plan-body' ).find( '.wpforms-panel-content-section-payment-plan-cycles select' ),
selectedValue = $select.val(); // Save current selected value
let max;
// Determine the maximum number of cycles based on the selected period.
// It has intentionally not been passed from PHP to avoid unnecessary complexity or unexpected behavior.
switch ( $el.val() ) {
case 'yearly':
max = 20;
break;
case 'semiyearly':
max = 40;
break;
case 'quarterly':
max = 80;
break;
default:
max = wpforms_builder_stripe.cycles_max;
}
const options = [
$( '<option>', { value: 'unlimited', text: wpforms_builder_stripe.i18n.cycles_default } ),
];
for ( let i = 1; i <= max; i++ ) {
options.push( $( '<option>', { value: i, text: i } ) );
}
// Populate select and re-apply selected value if still valid
$select.empty().append( options ).val( selectedValue );
// If selected value is no longer valid, default to 'unlimited'
if ( $select.val() !== selectedValue ) {
$select.val( 'unlimited' );
}
},
};
// Provide access to public functions/properties.
return app;
}( document, window, jQuery ) );
// Initialize.
WPFormsStripeModernBuilder.init();
File diff suppressed because one or more lines are too long
@@ -0,0 +1,222 @@
/* global wpforms_builder, wpforms_builder_stripe */
/**
* WPForms Stripe Card Field function.
*
* @since 1.8.2
*/
'use strict';
var WPFormsStripeCardField = window.WPFormsStripeCardField || ( function( document, window, $ ) {
/**
* Public functions and properties.
*
* @since 1.8.2
*
* @type {object}
*/
const app = {
/**
* Start the engine.
*
* @since 1.8.2
*/
init: function() {
app.bindUIActions();
},
/**
* Process various events as a response to UI interactions.
*
* @since 1.8.2
*/
bindUIActions: function() {
$( document ).on( 'wpformsSaved', app.ajaxRequiredCheck );
$( document ).on( 'wpformsSaved', app.paymentsEnabledCheck );
$( document ).on( 'click', '#wpforms-add-fields-' + wpforms_builder_stripe.field_slug, app.stripeKeysCheck );
$( document ).on( 'change', '.wpforms-field-option-stripe-credit-card .wpforms-field-option-row-sublabel_position select', app.sublabelPositionChange );
$( document ).on( 'change', '.wpforms-field-option-stripe-credit-card .wpforms-field-option-row-link_email select', app.linkEmailChange );
$( document ).on( 'wpformsFieldAdd', app.disableAddCardButton );
$( document ).on( 'wpformsFieldDelete', app.enableAddCardButton );
$( document ).on( 'wpformsFieldDelete', app.maybeResetLinkEmailField );
},
/**
* On form save notify users if AJAX submission is required.
*
* @since 1.8.2
*/
ajaxRequiredCheck: function() {
if ( ! $( '.wpforms-field.wpforms-field-' + wpforms_builder_stripe.field_slug ).length ||
$( '#wpforms-panel-field-settings-ajax_submit' ).is( ':checked' ) ) {
return;
}
$.alert( {
title: wpforms_builder.heads_up,
content: wpforms_builder.stripe_ajax_required,
icon: 'fa fa-exclamation-circle',
type: 'orange',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
} );
},
/**
* On form save notify users if Stripe payments are not enabled.
*
* @since 1.8.2
*/
paymentsEnabledCheck: function() {
if ( ! $( `.wpforms-field.wpforms-field-${ wpforms_builder_stripe.field_slug }:visible` ).length ||
$( '#wpforms-panel-field-stripe-enable' ).is( ':checked' ) ||
$( '#wpforms-panel-field-stripe-enable_one_time' ).is( ':checked' ) ||
$( '#wpforms-panel-field-stripe-enable_recurring' ).is( ':checked' )
) {
return;
}
$.alert( {
title: wpforms_builder.heads_up,
content: wpforms_builder.payments_enabled_required,
icon: 'fa fa-exclamation-circle',
type: 'red',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
} );
},
/**
* On adding Stripe Credit Card field notify users if Stripe keys are missing.
*
* @since 1.8.2
*/
stripeKeysCheck: function() {
if ( ! $( this ).hasClass( 'stripe-keys-required' ) ) {
return;
}
$.alert( {
title: wpforms_builder.heads_up,
content: wpforms_builder.stripe_keys_required,
icon: 'fa fa-exclamation-circle',
type: 'orange',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
} );
},
/**
* Disable "Add Card" button in the fields list.
*
* @since 1.8.2
*
* @param {object} e Event object.
* @param {number} id Field ID.
* @param {string} type Field type.
*/
disableAddCardButton: function( e, id, type ) {
if ( wpforms_builder_stripe.field_slug === type ) {
$( '#wpforms-add-fields-' + wpforms_builder_stripe.field_slug )
.prop( 'disabled', true );
app.paymentsEnabledCheck();
}
},
/**
* Enable "Add Card" button in the fields list.
*
* @since 1.8.2
*
* @param {object} e Event object.
* @param {number} id Field ID.
* @param {string} type Field type.
*/
enableAddCardButton: function( e, id, type ) {
if ( wpforms_builder_stripe.field_slug === type ) {
$( '#wpforms-add-fields-' + wpforms_builder_stripe.field_slug )
.prop( 'disabled', false );
}
},
/**
* Switch sublabels preview mode.
*
* @since 1.8.2
*/
sublabelPositionChange: function() {
const fieldId = $( this ).parent().data( 'field-id' ),
$fieldPreview = $( `#wpforms-field-${fieldId}` ).find( '.wpforms-stripe-payment-element' );
$fieldPreview.toggleClass( 'above' );
$fieldPreview.toggleClass( 'floating' );
$fieldPreview.find( 'select' ).val( $fieldPreview.hasClass( 'above' ) ? 'empty' : 'country' );
},
/**
* Switch Link Email Field mapping.
*
* @since 1.8.2
*/
linkEmailChange: function() {
const fieldId = $( this ).parent().data( 'field-id' );
$( `#wpforms-field-${fieldId}` ).find( '.wpforms-stripe-link-email' ).toggleClass( 'wpforms-hidden', $( this ).val() !== '' );
},
/**
* Maybe reset link email field if mapped email was removed.
*
* @since 1.8.2
*
* @param {object} e Event object.
* @param {number} id Field ID.
* @param {string} type Field type.
*/
maybeResetLinkEmailField: function( e, id, type ) {
if ( type !== 'email' ) {
return;
}
$( '.wpforms-field-option-stripe-credit-card .wpforms-field-option-row-link_email select' ).trigger( 'change' );
},
};
// Provide access to public functions/properties.
return app;
}( document, window, jQuery ) );
// Initialize.
WPFormsStripeCardField.init();
@@ -0,0 +1 @@
var WPFormsStripeCardField=window.WPFormsStripeCardField||((e,s)=>{let t={init:function(){t.bindUIActions()},bindUIActions:function(){s(e).on("wpformsSaved",t.ajaxRequiredCheck),s(e).on("wpformsSaved",t.paymentsEnabledCheck),s(e).on("click","#wpforms-add-fields-"+wpforms_builder_stripe.field_slug,t.stripeKeysCheck),s(e).on("change",".wpforms-field-option-stripe-credit-card .wpforms-field-option-row-sublabel_position select",t.sublabelPositionChange),s(e).on("change",".wpforms-field-option-stripe-credit-card .wpforms-field-option-row-link_email select",t.linkEmailChange),s(e).on("wpformsFieldAdd",t.disableAddCardButton),s(e).on("wpformsFieldDelete",t.enableAddCardButton),s(e).on("wpformsFieldDelete",t.maybeResetLinkEmailField)},ajaxRequiredCheck:function(){s(".wpforms-field.wpforms-field-"+wpforms_builder_stripe.field_slug).length&&!s("#wpforms-panel-field-settings-ajax_submit").is(":checked")&&s.alert({title:wpforms_builder.heads_up,content:wpforms_builder.stripe_ajax_required,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})},paymentsEnabledCheck:function(){!s(`.wpforms-field.wpforms-field-${wpforms_builder_stripe.field_slug}:visible`).length||s("#wpforms-panel-field-stripe-enable").is(":checked")||s("#wpforms-panel-field-stripe-enable_one_time").is(":checked")||s("#wpforms-panel-field-stripe-enable_recurring").is(":checked")||s.alert({title:wpforms_builder.heads_up,content:wpforms_builder.payments_enabled_required,icon:"fa fa-exclamation-circle",type:"red",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})},stripeKeysCheck:function(){s(this).hasClass("stripe-keys-required")&&s.alert({title:wpforms_builder.heads_up,content:wpforms_builder.stripe_keys_required,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}}})},disableAddCardButton:function(e,i,r){wpforms_builder_stripe.field_slug===r&&(s("#wpforms-add-fields-"+wpforms_builder_stripe.field_slug).prop("disabled",!0),t.paymentsEnabledCheck())},enableAddCardButton:function(e,i,r){wpforms_builder_stripe.field_slug===r&&s("#wpforms-add-fields-"+wpforms_builder_stripe.field_slug).prop("disabled",!1)},sublabelPositionChange:function(){var e=s(this).parent().data("field-id"),e=s("#wpforms-field-"+e).find(".wpforms-stripe-payment-element");e.toggleClass("above"),e.toggleClass("floating"),e.find("select").val(e.hasClass("above")?"empty":"country")},linkEmailChange:function(){var e=s(this).parent().data("field-id");s("#wpforms-field-"+e).find(".wpforms-stripe-link-email").toggleClass("wpforms-hidden",""!==s(this).val())},maybeResetLinkEmailField:function(e,i,r){"email"===r&&s(".wpforms-field-option-stripe-credit-card .wpforms-field-option-row-link_email select").trigger("change")}};return t})(document,(window,jQuery));WPFormsStripeCardField.init();
@@ -0,0 +1,242 @@
/* global wpforms_builder, wpforms_builder_stripe */
// noinspection ES6ConvertVarToLetConst
/**
* Stripe builder function.
*
* @since 1.8.2
*/
// eslint-disable-next-line no-var
var WPFormsStripe = window.WPFormsStripe || ( function( document, window, $ ) {
/**
* Public functions and properties.
*
* @since 1.8.2
*
* @type {Object}
*/
const app = {
/**
* Start the engine.
*
* @since 1.8.2
*/
init() {
$( app.ready );
},
/**
* Initialized once the DOM is fully loaded.
*
* @since 1.8.2
*/
ready() {
if ( ! app.isLegacySettings() ) {
return;
}
app.settingsDisplay();
app.settingsConditions();
app.bindUIActions();
},
/**
* Process various events as a response to UI interactions.
*
* @since 1.8.2
*/
bindUIActions() {
$( document )
.on( 'wpformsFieldDelete', app.disableNotifications )
.on( 'wpformsSaved', app.requiredFieldsCheck )
.on( 'wpformsFieldUpdate', app.settingsDisplay )
.on( 'wpformsFieldUpdate', app.settingsConditions );
$( '#wpforms-panel-field-stripe-recurring-email' ).on( 'change', app.resetEmailAlertErrorClass );
},
/**
* Toggles visibility of the Stripe settings.
*
* If a credit card field has been added, then reveal the settings.
* Otherwise, hide them.
*
* @since 1.8.2
*/
settingsDisplay() {
const $alert = $( '#wpforms-stripe-credit-card-alert' );
const $content = $( '#stripe-provider' );
// Check if any Credit Card fields were added to the form.
const ccFieldsAdded = wpforms_builder_stripe.field_slugs.filter( function( fieldSlug ) {
const $el = $( '.wpforms-field-option-' + fieldSlug );
return $el.length ? $el : null;
} );
if ( ccFieldsAdded.length ) {
$alert.hide();
$content.find( '#wpforms-stripe-new-interface-alert, .wpforms-stripe-notice-info, .wpforms-panel-field, .wpforms-conditional-block-panel, h2' ).show();
} else {
$alert.show();
$content.find( '#wpforms-stripe-new-interface-alert, .wpforms-stripe-notice-info, .wpforms-panel-field, .wpforms-conditional-block-panel, h2' ).hide();
$content.find( '#wpforms-panel-field-stripe-enable' ).prop( 'checked', false ).trigger( 'change' );
}
},
/**
* Toggles the visibility of the related settings.
*
* @since 1.8.2
*/
settingsConditions() {
$( '#wpforms-panel-field-stripe-enable' ).conditions( {
conditions: {
element: '#wpforms-panel-field-stripe-enable',
type: 'checked',
operator: 'is',
},
actions: {
if: {
element: '.wpforms-panel-content-section-stripe-body',
action: 'show',
},
else: {
element: '.wpforms-panel-content-section-stripe-body',
action: 'hide',
},
},
effect: 'appear',
} );
$( '#wpforms-panel-field-stripe-recurring-enable' ).conditions( {
conditions: {
element: '#wpforms-panel-field-stripe-recurring-enable',
type: 'checked',
operator: 'is',
},
actions: {
if: {
element: '#wpforms-panel-field-stripe-recurring-period-wrap,#wpforms-panel-field-stripe-recurring-conditional_logic-wrap,#wpforms-conditional-groups-payments-stripe-recurring,#wpforms-panel-field-stripe-recurring-email-wrap,#wpforms-panel-field-stripe-recurring-name-wrap',
action: 'show',
},
else: {
element: '#wpforms-panel-field-stripe-recurring-period-wrap,#wpforms-panel-field-stripe-recurring-conditional_logic-wrap,#wpforms-conditional-groups-payments-stripe-recurring,#wpforms-panel-field-stripe-recurring-email-wrap,#wpforms-panel-field-stripe-recurring-name-wrap',
action: 'hide',
},
},
effect: 'appear',
} );
},
/**
* On form save notify users about required fields.
*
* @since 1.8.2
*/
requiredFieldsCheck() {
if (
! $( '#wpforms-panel-field-stripe-enable' ).is( ':checked' ) ||
! $( '#wpforms-panel-field-stripe-recurring-enable' ).is( ':checked' )
) {
return;
}
const $emailField = $( '#wpforms-panel-field-stripe-recurring-email' );
if ( $emailField.val() ) {
return;
}
$emailField.addClass( 'wpforms-required-field-error' );
let alertMessage = wpforms_builder.stripe_recurring_email;
if ( ! $( '.wpforms-panel-content-section-stripe' ).is( ':visible' ) ) {
alertMessage += ' ' + wpforms_builder.stripe_recurring_settings;
}
$.alert( {
title: wpforms_builder.stripe_recurring_heading,
content: alertMessage,
icon: 'fa fa-exclamation-circle',
type: 'red',
buttons: {
confirm: {
text: wpforms_builder.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
onOpen() {
$( '.wpforms-stripe-settings-redirect' ).on( 'click', app.settingsRedirect );
},
} );
},
/**
* Redirect to the settings tab.
*
* @since 1.9.5
*/
settingsRedirect() {
// Open the Stripe settings tab.
$( '.wpforms-panel-payments-button' ).trigger( 'click' );
$( '.wpforms-panel-sidebar-section-stripe' ).trigger( 'click' );
// Scroll to the Stripe settings.
window.location.href = window.location.pathname + window.location.search + '#wpforms-panel-field-stripe-enable_recurring-wrap';
// Close the alert.
$( this ).closest( '.jconfirm-box' ).find( '.btn-confirm' ).trigger( 'click' );
},
/**
* Maybe reset required email field error class.
*
* @since 1.9.5
*/
resetEmailAlertErrorClass() {
$( this ).toggleClass( 'wpforms-required-field-error', ! $( this ).val() );
},
/**
* Disable notifications.
*
* @since 1.8.2
*
* @param {Object} e Event object.
* @param {number} id Field ID.
* @param {string} type Field type.
*/
disableNotifications( e, id, type ) {
if ( ! wpforms_builder_stripe.field_slugs.includes( type ) ) {
return;
}
const $notificationWrap = $( '.wpforms-panel-content-section-notifications [id*="-stripe-wrap"]' );
$notificationWrap.find( 'input[id*="-stripe"]' ).prop( 'checked', false );
$notificationWrap.addClass( 'wpforms-hidden' );
},
/**
* Determine is legacy settings is loaded.
*
* @since 1.8.4
*
* @return {boolean} True is legacy settings loaded.
*/
isLegacySettings() {
return $( '#wpforms-panel-field-stripe-enable' ).length;
},
};
// Provide access to public functions/properties.
return app;
}( document, window, jQuery ) );
// Initialize.
WPFormsStripe.init();
@@ -0,0 +1 @@
var WPFormsStripe=window.WPFormsStripe||((e,i,n)=>{let r={init(){n(r.ready)},ready(){r.isLegacySettings()&&(r.settingsDisplay(),r.settingsConditions(),r.bindUIActions())},bindUIActions(){n(e).on("wpformsFieldDelete",r.disableNotifications).on("wpformsSaved",r.requiredFieldsCheck).on("wpformsFieldUpdate",r.settingsDisplay).on("wpformsFieldUpdate",r.settingsConditions),n("#wpforms-panel-field-stripe-recurring-email").on("change",r.resetEmailAlertErrorClass)},settingsDisplay(){var e=n("#wpforms-stripe-credit-card-alert"),i=n("#stripe-provider");wpforms_builder_stripe.field_slugs.filter(function(e){e=n(".wpforms-field-option-"+e);return e.length?e:null}).length?(e.hide(),i.find("#wpforms-stripe-new-interface-alert, .wpforms-stripe-notice-info, .wpforms-panel-field, .wpforms-conditional-block-panel, h2").show()):(e.show(),i.find("#wpforms-stripe-new-interface-alert, .wpforms-stripe-notice-info, .wpforms-panel-field, .wpforms-conditional-block-panel, h2").hide(),i.find("#wpforms-panel-field-stripe-enable").prop("checked",!1).trigger("change"))},settingsConditions(){n("#wpforms-panel-field-stripe-enable").conditions({conditions:{element:"#wpforms-panel-field-stripe-enable",type:"checked",operator:"is"},actions:{if:{element:".wpforms-panel-content-section-stripe-body",action:"show"},else:{element:".wpforms-panel-content-section-stripe-body",action:"hide"}},effect:"appear"}),n("#wpforms-panel-field-stripe-recurring-enable").conditions({conditions:{element:"#wpforms-panel-field-stripe-recurring-enable",type:"checked",operator:"is"},actions:{if:{element:"#wpforms-panel-field-stripe-recurring-period-wrap,#wpforms-panel-field-stripe-recurring-conditional_logic-wrap,#wpforms-conditional-groups-payments-stripe-recurring,#wpforms-panel-field-stripe-recurring-email-wrap,#wpforms-panel-field-stripe-recurring-name-wrap",action:"show"},else:{element:"#wpforms-panel-field-stripe-recurring-period-wrap,#wpforms-panel-field-stripe-recurring-conditional_logic-wrap,#wpforms-conditional-groups-payments-stripe-recurring,#wpforms-panel-field-stripe-recurring-email-wrap,#wpforms-panel-field-stripe-recurring-name-wrap",action:"hide"}},effect:"appear"})},requiredFieldsCheck(){if(n("#wpforms-panel-field-stripe-enable").is(":checked")&&n("#wpforms-panel-field-stripe-recurring-enable").is(":checked")){var i=n("#wpforms-panel-field-stripe-recurring-email");if(!i.val()){i.addClass("wpforms-required-field-error");let e=wpforms_builder.stripe_recurring_email;n(".wpforms-panel-content-section-stripe").is(":visible")||(e+=" "+wpforms_builder.stripe_recurring_settings),n.alert({title:wpforms_builder.stripe_recurring_heading,content:e,icon:"fa fa-exclamation-circle",type:"red",buttons:{confirm:{text:wpforms_builder.ok,btnClass:"btn-confirm",keys:["enter"]}},onOpen(){n(".wpforms-stripe-settings-redirect").on("click",r.settingsRedirect)}})}}},settingsRedirect(){n(".wpforms-panel-payments-button").trigger("click"),n(".wpforms-panel-sidebar-section-stripe").trigger("click"),i.location.href=i.location.pathname+i.location.search+"#wpforms-panel-field-stripe-enable_recurring-wrap",n(this).closest(".jconfirm-box").find(".btn-confirm").trigger("click")},resetEmailAlertErrorClass(){n(this).toggleClass("wpforms-required-field-error",!n(this).val())},disableNotifications(e,i,r){wpforms_builder_stripe.field_slugs.includes(r)&&((r=n('.wpforms-panel-content-section-notifications [id*="-stripe-wrap"]')).find('input[id*="-stripe"]').prop("checked",!1),r.addClass("wpforms-hidden"))},isLegacySettings(){return n("#wpforms-panel-field-stripe-enable").length}};return r})(document,window,jQuery);WPFormsStripe.init();
@@ -0,0 +1,158 @@
/* global wpforms_admin_settings_stripe, wpforms_admin, wpf */
/**
* Stripe integration settings script.
*
* @since 1.8.2
*/
const WPFormsSettingsStripe = window.WPFormsSettingsStripe || ( function( document, window, $ ) {
/**
* Elements holder.
*
* @since 1.8.2
*
* @type {Object}
*/
const el = {};
/**
* Runtime variables.
*
* @since 1.8.2
*
* @type {Object}
*/
const vars = {
alertTitle: wpforms_admin.heads_up,
alertContent: wpforms_admin_settings_stripe.mode_update,
ok: wpforms_admin.ok,
hideClassName: 'wpforms-hide',
};
/**
* Public functions and properties.
*
* @since 1.8.2
*/
const app = {
/**
* Start the engine.
*
* @since 1.8.2
*/
init() {
$( app.ready );
},
/**
* Document ready.
*
* @since 1.8.2
*/
ready() {
app.setup();
app.bindEvents();
},
/**
* Setup. Prepare some variables.
*
* @since 1.8.2
*/
setup() {
// Cache DOM elements.
el.$wrapper = $( '.wpforms-admin-content-payments' );
el.$liveConnectionBlock = $( '.wpforms-stripe-connection-status-live' );
el.$testConnectionBlock = $( '.wpforms-stripe-connection-status-test' );
el.$testModeCheckbox = $( '#wpforms-setting-stripe-test-mode' );
el.copyButton = $( '#wpforms-setting-row-stripe-webhooks-endpoint-set .wpforms-copy-to-clipboard' );
el.webhookEndpointUrl = $( 'input#wpforms-stripe-webhook-endpoint-url' );
el.webhookMethod = $( 'input[name="stripe-webhooks-communication"]' );
},
/**
* Bind events.
*
* @since 1.8.2
*/
bindEvents() {
el.$wrapper
.on( 'change', '#wpforms-setting-stripe-test-mode', app.triggerModeSwitchAlert );
el.copyButton
.on( 'click', function( e ) {
wpf.copyValueToClipboard( e, $( this ), el.webhookEndpointUrl );
} );
el.webhookMethod
.on( 'change', app.onMethodChange );
},
/**
* Conditionally show Stripe mode switch warning.
*
* @since 1.8.2
*/
triggerModeSwitchAlert() {
if ( el.$testModeCheckbox.is( ':checked' ) ) {
el.$liveConnectionBlock.addClass( vars.hideClassName );
el.$testConnectionBlock.removeClass( vars.hideClassName );
} else {
el.$testConnectionBlock.addClass( vars.hideClassName );
el.$liveConnectionBlock.removeClass( vars.hideClassName );
}
if ( $( '#wpforms-setting-row-stripe-connection-status .wpforms-connected' ).is( ':visible' ) ) {
return;
}
$.alert( {
title: vars.alertTitle,
content: vars.alertContent,
icon: 'fa fa-exclamation-circle',
type: 'orange',
buttons: {
confirm: {
text: vars.ok,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
},
},
} );
},
/**
* Copy webhooks endpoint URL to clipboard.
*
* @since 1.8.4
*
* @deprecated 1.9.5 Changed to the wpf.copyWebhooksEndpoint().
*
* @param {Object} event Event object.
*/
copyWebhooksEndpoint( event ) {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Function "WPFormsSettingsStripe.copyWebhooksEndpoint()" has been deprecated! Use wpf.copyWebhooksEndpoint() instead.' );
wpf.copyValueToClipboard( event, $( this ), el.webhookEndpointUrl );
},
/**
* Update the endpoint URL.
*
* @since 1.8.4
*/
onMethodChange() {
const checked = el.webhookMethod.filter( ':checked' ).val(),
newUrl = wpforms_admin_settings_stripe.webhook_urls[ checked ];
el.webhookEndpointUrl.val( newUrl );
},
};
// Provide access to public functions/properties.
return app;
}( document, window, jQuery ) );
// Initialize.
WPFormsSettingsStripe.init();
@@ -0,0 +1 @@
let WPFormsSettingsStripe=window.WPFormsSettingsStripe||(o=>{let t={},e={alertTitle:wpforms_admin.heads_up,alertContent:wpforms_admin_settings_stripe.mode_update,ok:wpforms_admin.ok,hideClassName:"wpforms-hide"},n={init(){o(n.ready)},ready(){n.setup(),n.bindEvents()},setup(){t.$wrapper=o(".wpforms-admin-content-payments"),t.$liveConnectionBlock=o(".wpforms-stripe-connection-status-live"),t.$testConnectionBlock=o(".wpforms-stripe-connection-status-test"),t.$testModeCheckbox=o("#wpforms-setting-stripe-test-mode"),t.copyButton=o("#wpforms-setting-row-stripe-webhooks-endpoint-set .wpforms-copy-to-clipboard"),t.webhookEndpointUrl=o("input#wpforms-stripe-webhook-endpoint-url"),t.webhookMethod=o('input[name="stripe-webhooks-communication"]')},bindEvents(){t.$wrapper.on("change","#wpforms-setting-stripe-test-mode",n.triggerModeSwitchAlert),t.copyButton.on("click",function(e){wpf.copyValueToClipboard(e,o(this),t.webhookEndpointUrl)}),t.webhookMethod.on("change",n.onMethodChange)},triggerModeSwitchAlert(){(t.$testModeCheckbox.is(":checked")?(t.$liveConnectionBlock.addClass(e.hideClassName),t.$testConnectionBlock):(t.$testConnectionBlock.addClass(e.hideClassName),t.$liveConnectionBlock)).removeClass(e.hideClassName),o("#wpforms-setting-row-stripe-connection-status .wpforms-connected").is(":visible")||o.alert({title:e.alertTitle,content:e.alertContent,icon:"fa fa-exclamation-circle",type:"orange",buttons:{confirm:{text:e.ok,btnClass:"btn-confirm",keys:["enter"]}}})},copyWebhooksEndpoint(e){console.warn('WARNING! Function "WPFormsSettingsStripe.copyWebhooksEndpoint()" has been deprecated! Use wpf.copyWebhooksEndpoint() instead.'),wpf.copyValueToClipboard(e,o(this),t.webhookEndpointUrl)},onMethodChange(){var e=t.webhookMethod.filter(":checked").val(),e=wpforms_admin_settings_stripe.webhook_urls[e];t.webhookEndpointUrl.val(e)}};return n})((document,window,jQuery));WPFormsSettingsStripe.init();
@@ -0,0 +1,472 @@
/* global Stripe, wpforms, wpforms_settings, wpforms_stripe, WPForms */
'use strict';
/**
* WPForms Stripe Elements function.
*
* @since 1.8.2
*/
var WPFormsStripeElements = window.WPFormsStripeElements || ( function( document, window, $ ) {
/**
* Public functions and properties.
*
* @since 1.8.2
*
* @type {object}
*/
const app = {
stripe: null,
/**
* Number of page locked to switch.
*
* @since 1.8.2
*
* @type {int}
*/
lockedPageToSwitch: 0,
/**
* Start the engine.
*
* @since 1.8.2
*/
init: function() {
app.stripe = Stripe( // eslint-disable-line new-cap
wpforms_stripe.publishable_key,
{ 'locale': wpforms_stripe.data.element_locale }
);
$( document ).on( 'wpformsReady', function() {
$( '.wpforms-stripe form' )
.filter( ( _, form ) => typeof $( form ).data( 'formid' ) === 'number' ) // filter out forms which are locked (formid changed to 'locked-...').
.each( app.setupStripeForm );
} );
$( document ).on( 'wpformsBeforePageChange', app.pageChange );
},
/**
* Setup and configure a Stripe form.
*
* @since 1.8.2
*/
setupStripeForm: function() {
let $form = $( this );
app.updateFormSubmitHandler( $form );
$form.on( 'wpformsAjaxSubmitActionRequired', app.handleCardActionCallback );
app.updateCardElementStylesModern( $form );
},
/**
* Setup, mount and configure Stripe Card Element.
*
* @since 1.8.2
*
* @param {jQuery} $form Form element.
* @param {object} formValidator jQuery Validator object.
*
* @returns {card|void} Stripe Card element.
*/
setupCardElement: function( $form, formValidator ) {
const $hiddenInput = $form.find( '.wpforms-stripe-credit-card-hidden-input' );
if ( ! $hiddenInput || $hiddenInput.length === 0 ) {
return;
}
let cardElement = $hiddenInput.data( 'stripe-element' );
if ( cardElement ) {
return cardElement;
}
let style = wpforms_stripe.data.element_style;
if ( $.isEmptyObject( style ) ) {
style = app.getElementStyleDefault( $hiddenInput );
}
let cardSettings = {
classes : wpforms_stripe.data.element_classes,
hidePostalCode: true,
style : style,
};
cardElement = app.stripe.elements().create( 'card', cardSettings );
cardElement.mount( $form.find( '.wpforms-field-stripe-credit-card-cardnumber' ).get( 0 ) );
cardElement.on( 'change', function( e ) {
if ( ! e.error ) {
formValidator.hideThese( formValidator.errorsFor( $hiddenInput.get( 0 ) ) );
return;
}
let message = e.error.message;
if ( 'incomplete_number' === e.error.code || 'invalid_number' === e.error.code ) {
message = wpforms_settings.val_creditcard;
}
app.displayStripeError( $form, message );
} );
cardElement.on( 'focus', function() {
$( document ).trigger( 'wpformsStripePaymentElementFocus', [ $form ] );
} );
$hiddenInput.data( 'stripe-element', cardElement );
return cardElement;
},
/**
* Get default styles for card settings.
*
* @since 1.8.2
*
* @param {jQuery} $hiddenInput Input element.
*
* @returns {object|void} Base styles.
*/
getElementStyleDefault: function( $hiddenInput ) {
if ( ! $hiddenInput || $hiddenInput.length === 0 ) {
return;
}
const textColor = $hiddenInput.css( 'color' );
const fontSize = $hiddenInput.css( 'font-size' );
const style = {
base: {
fontSize,
color: textColor,
'::placeholder': {
color: textColor,
fontSize,
},
},
invalid: {
color: textColor,
},
};
let fontFamily = $hiddenInput.css( 'font-family' );
const regExp = /[“”<>!@$%^&*=~`|{}[\]]/;
if ( regExp.test( fontFamily ) || fontFamily.indexOf( 'MS Shell Dlg' ) !== -1 ) {
fontFamily = $( 'p' ).css( 'font-family' );
}
if ( ! regExp.test( fontFamily ) ) {
style.base.fontFamily = fontFamily;
style.base[ '::placeholder' ].fontFamily = fontFamily;
}
return style;
},
/**
* Update submitHandler for the forms containing Stripe.
*
* @since 1.8.2
*
* @param {jQuery} $form Form element.
*/
updateFormSubmitHandler: function( $form ) {
let formValidator = $form.validate(),
formSubmitHandler = formValidator.settings.submitHandler,
cardElement = app.setupCardElement( $form, formValidator ),
$stripeDiv = $form.find( '.wpforms-field-stripe-credit-card-cardnumber' );
// Replace the default submit handler.
formValidator.settings.submitHandler = function() {
let valid = $form.validate().form(),
ccEmpty = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.empty ),
ccRequired = $stripeDiv.data( 'required' ),
condHidden = $stripeDiv.closest( '.wpforms-field-stripe-credit-card' ).hasClass( 'wpforms-conditional-hide' ),
processCard = false;
if ( ! condHidden ) {
processCard = ccRequired || ( ! ccEmpty && ! ccRequired );
}
if ( valid && processCard ) {
$form.find( '.wpforms-submit' ).prop( 'disabled', true );
app.createPaymentMethod( $form, cardElement, ccRequired, formSubmitHandler );
} else if ( valid ) {
// Form is valid, however no credit card to process.
$form.find( '.wpforms-submit' ).prop( 'disabled', false );
return formSubmitHandler( $form );
} else {
$form.find( '.wpforms-submit' ).prop( 'disabled', false );
$form.validate().cancelSubmit = true;
}
};
},
/**
* Create a PaymentMethod out of card details provided.
*
* @since 1.8.2
*
* @param {jQuery} $form Form element.
* @param {card} cardElement Stripe Card element.
* @param {boolean} ccRequired Card field is required.
* @param {Function} formSubmitHandler jQuery Validation SubmitHandler function.
*/
createPaymentMethod: function( $form, cardElement, ccRequired, formSubmitHandler ) {
app.stripe.createPaymentMethod( 'card', cardElement, {
billing_details: {
name: $form.find( '.wpforms-field-stripe-credit-card-cardname' ).val(),
},
} ).then( function( result ) {
if ( result.error && ccRequired ) {
$form.find( '.wpforms-submit' ).prop( 'disabled', false );
app.displayStripeError( $form, result.error.message );
$form.validate().cancelSubmit = true;
return;
}
if ( ! result.error ) {
$form.find( '.wpforms-stripe-payment-method-id' ).remove();
if ( result.paymentMethod ) {
$form.append( '<input type="hidden" class="wpforms-stripe-payment-method-id" name="wpforms[payment_method_id]" value="' + result.paymentMethod.id + '">' );
}
}
formSubmitHandler( $form );
} );
},
/**
* Handle 'action_required' server response.
*
* @param {object} e Event object.
* @param {object} json Data returned form a server.
*
* @since 1.8.2
*/
handleCardActionCallback: function( e, json ) {
const $form = $( this );
if ( json.success && json.data.action_required ) {
app.stripe.handleCardPayment(
json.data.payment_intent_client_secret,
{
// eslint-disable-next-line camelcase
payment_method: json.data.payment_method_id,
}
).then( function( result ) {
app.handleCardPaymentCallback( $form, result );
} );
}
},
/**
* Callback for Stripe 'handleCardPayment' method.
*
* @param {jQuery} $form Form element.
* @param {Object} result Data returned by 'handleCardPayment'.
*
* @since 1.8.2
*/
handleCardPaymentCallback( $form, result ) {
if ( result.error ) {
wpforms.restoreSubmitButton( $form, $form.closest( '.wpforms-container' ) );
$form.find( '.wpforms-field-stripe-credit-card-cardnumber' ).addClass( wpforms_stripe.data.element_classes.invalid );
app.displayStripeError( $form, result.error.message );
} else if ( result.paymentIntent && 'succeeded' === result.paymentIntent.status ) {
$form.find( '.wpforms-stripe-payment-method-id' ).remove();
$form.find( '.wpforms-stripe-payment-intent-id' ).remove();
$form.append( '<input type="hidden" class="wpforms-stripe-payment-intent-id" name="wpforms[payment_intent_id]" value="' + result.paymentIntent.id + '">' );
wpforms.formSubmitAjax( $form );
} else {
wpforms.restoreSubmitButton( $form, $form.closest( '.wpforms-container' ) );
}
},
/**
* Display a field error using jQuery Validate library.
*
* @param {jQuery} $form Form element.
* @param {object} message Error message.
*
* @since 1.8.2
*/
displayStripeError: function( $form, message ) {
const fieldName = $form.find( '.wpforms-stripe-credit-card-hidden-input' ).attr( 'name' ),
$stripeDiv = $form.find( '.wpforms-field-stripe-credit-card-cardnumber' );
let errors = {};
errors[fieldName] = message;
wpforms.displayFormAjaxFieldErrors( $form, errors );
// Switch page for the multipage form.
if ( ! $stripeDiv.is( ':visible' ) && $form.find( '.wpforms-page-indicator-steps' ).length > 0 ) {
// Empty $json object needed to change the page to the first one.
wpforms.setCurrentPage( $form, {} );
}
wpforms.scrollToError( $stripeDiv );
},
/**
* Unblock the AJAX form.
*
* @since 1.8.2
* @deprecated 1.9.5
*
* @param {jQuery} $form Form element.
*/
formAjaxUnblock( $form ) {
// eslint-disable-next-line no-console
console.warn( 'WARNING! Function "WPFormsStripeElements.formAjaxUnblock()" has been deprecated, please use the new "wpforms.restoreSubmitButton()" function instead!' );
wpforms.restoreSubmitButton( $form, $form.closest( '.wpforms-container' ) );
},
/**
* Callback for a page changing.
*
* @since 1.8.2
*
* @param {Event} event Event.
* @param {int} currentPage Current page.
* @param {jQuery} $form Current form.
* @param {string} action The navigation action.
*/
pageChange: function( event, currentPage, $form, action ) {
const $stripeDiv = $form.find( '.wpforms-field-stripe-credit-card-cardnumber' ),
ccComplete = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.complete ),
ccEmpty = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.empty ),
ccInvalid = $stripeDiv.hasClass( wpforms_stripe.data.element_classes.invalid );
// Stop navigation through page break pages.
if (
! $stripeDiv.is( ':visible' ) ||
( ! $stripeDiv.data( 'required' ) && ccEmpty ) ||
( app.lockedPageToSwitch && app.lockedPageToSwitch !== currentPage ) ||
action === 'prev'
) {
return;
}
if ( ccComplete ) {
$stripeDiv.find( '.wpforms-error' ).remove();
return;
}
app.lockedPageToSwitch = currentPage;
event.preventDefault();
if ( ccInvalid ) {
return;
}
app.displayStripeError( $form, wpforms_stripe.i18n.empty_details );
},
/**
* Get CSS property value.
* In case of exception return empty string.
*
* @since 1.8.6
*
* @param {jQuery} $element Element.
* @param {string} property Property.
*
* @return {string} Property value.
*/
getCssPropertyValue( $element, property ) {
try {
return $element.css( property );
} catch ( e ) {
return '';
}
},
/**
* Update Card Element styles in Modern Markup mode.
*
* @since 1.8.2
*
* @param {jQuery} $form Form object.
*/
updateCardElementStylesModern( $form ) {
// Should work only in Modern Markup mode.
if ( ! window.WPForms || ! WPForms.FrontendModern || ! $.isEmptyObject( wpforms_stripe.data.element_style ) ) {
return;
}
if ( ! $form || $form.length === 0 ) {
return;
}
$form.find( '.wpforms-stripe-credit-card-hidden-input' ).each( function() {
const $hiddenInput = $( this );
const cardElement = $hiddenInput.data( 'stripe-element' );
const inputStyle = {
fontSize: app.getCssPropertyValue( $hiddenInput, 'font-size' ),
colorText: app.getCssPropertyValue( $hiddenInput, 'color' ),
};
if ( ! cardElement ) {
return;
}
const styles = {
base: {
color: inputStyle.colorText,
fontSize: inputStyle.fontSize,
'::placeholder': {
color: WPForms.FrontendModern.getColorWithOpacity( inputStyle.colorText, '0.5' ),
fontSize: inputStyle.fontSize,
},
},
invalid: {
color: inputStyle.colorText,
},
};
cardElement.update( { style: styles } );
} );
},
};
// Provide access to public functions/properties.
return app;
}( document, window, jQuery ) );
// Initialize.
WPFormsStripeElements.init();
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long