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:
+416
@@ -0,0 +1,416 @@
|
||||
/* global wpforms_builder, WPFormsBuilderPaymentsUtils */
|
||||
|
||||
/**
|
||||
* WPForms Square builder function.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
const WPFormsBuilderSquare = window.WPFormsBuilderSquare || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const el = {};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialized once the DOM and Providers are fully loaded.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
ready() {
|
||||
// Cache DOM elements.
|
||||
el.$singlePaymentControl = $( '#wpforms-panel-field-square-enable_one_time' );
|
||||
el.$recurringPaymentControl = $( '#wpforms-panel-field-square-enable_recurring' );
|
||||
el.$panelContent = $( '#wpforms-panel-content-section-payment-square' );
|
||||
el.$AJAXSubmitOption = $( '#wpforms-panel-field-settings-ajax_submit' );
|
||||
el.$cardButton = $( '#wpforms-add-fields-square' );
|
||||
el.$alert = $( '#wpforms-square-credit-card-alert' );
|
||||
el.$feeNotice = $( '.wpforms-square-notice-info' );
|
||||
|
||||
app.bindUIActions();
|
||||
app.bindPlanUIActions();
|
||||
|
||||
if ( ! wpforms_builder.square_is_pro ) {
|
||||
const baseSelector = '.wpforms-panel-content-section-square',
|
||||
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 various events.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
bindUIActions() {
|
||||
$( document ).on( 'wpformsSaved', app.ajaxRequiredCheck )
|
||||
.on( 'wpformsSaved', app.paymentsEnabledCheck )
|
||||
.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.$cardButton.on( 'click', app.connectionCheck );
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind plan UI actions.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
bindPlanUIActions() {
|
||||
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan-body .wpforms-panel-field-select select' ).on( 'change', app.resetRequiredPlanFieldError );
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify user if AJAX submission is not required.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
ajaxRequiredCheck() {
|
||||
if ( ! $( '#wpforms-panel-fields .wpforms-field.wpforms-field-square' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( app.isAJAXSubmitEnabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.square_ajax_required,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify user if Square Payments are not enabled.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
paymentsEnabledCheck() {
|
||||
if ( ! $( '#wpforms-panel-fields .wpforms-field.wpforms-field-square' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( app.isPaymentsEnabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.square_payments_enabled_required,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* On form save notify users about required fields.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
requiredFieldsCheck() {
|
||||
if ( ! el.$recurringPaymentControl.is( ':checked' ) || el.$panelContent.hasClass( 'wpforms-hidden' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let showAlert = false;
|
||||
|
||||
el.$panelContent.find( '.wpforms-panel-content-section-payment-plan' ).each( function() {
|
||||
const $plan = $( this ),
|
||||
planId = $plan.data( 'plan-id' ),
|
||||
$emailField = $( `#wpforms-panel-field-square-recurring-${ planId }-customer_email` ),
|
||||
$nameField = $( `#wpforms-panel-field-square-recurring-${ planId }-customer_name` );
|
||||
|
||||
if (
|
||||
! $emailField.val()
|
||||
) {
|
||||
$emailField.addClass( 'wpforms-required-field-error' );
|
||||
showAlert = true;
|
||||
}
|
||||
|
||||
if (
|
||||
! $nameField.val()
|
||||
) {
|
||||
$nameField.addClass( 'wpforms-required-field-error' );
|
||||
showAlert = true;
|
||||
}
|
||||
} );
|
||||
|
||||
if ( ! showAlert ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let alertMessage = wpforms_builder.square_recurring_payments_fields_required;
|
||||
|
||||
if ( ! $( '.wpforms-panel-content-section-square' ).is( ':visible' ) ) {
|
||||
alertMessage += ' ' + wpforms_builder.square_recurring_payments_fields_settings;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.square_recurring_payments_fields_heading,
|
||||
content: alertMessage,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
onOpen() {
|
||||
$( '.wpforms-square-settings-redirect' ).on( 'click', app.settingsRedirect );
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Redirect to the settings tab.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
settingsRedirect() {
|
||||
// Open the Square settings tab.
|
||||
$( '.wpforms-panel-payments-button' ).trigger( 'click' );
|
||||
$( '.wpforms-panel-sidebar-section-square' ).trigger( 'click' );
|
||||
|
||||
// Scroll to the Stripe settings.
|
||||
window.location.href = window.location.pathname + window.location.search + '#wpforms-panel-field-square-enable_recurring-wrap';
|
||||
|
||||
// Close the alert.
|
||||
$( this ).closest( '.jconfirm-box' ).find( '.btn-confirm' ).trigger( 'click' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Maybe reset required recurring field error class.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
resetRequiredPlanFieldError() {
|
||||
const $nameAttr = $( this ).attr( 'name' );
|
||||
|
||||
if ( ! $nameAttr.includes( 'customer_email' ) && ! $nameAttr.includes( 'customer_name' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$( this ).toggleClass( 'wpforms-required-field-error', ! $( this ).val() );
|
||||
},
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-returns-check
|
||||
/**
|
||||
* Notify user if Square connection are missing.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} False if button clicks should be prevented.
|
||||
*/
|
||||
connectionCheck() {
|
||||
if ( $( this ).hasClass( 'wpforms-add-fields-button-disabled' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $( this ).hasClass( 'square-connection-required' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$.alert( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.square_connection_required,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* We have to do several actions when the "Square" field is added.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
fieldAdded( e, id, type ) {
|
||||
if ( type === 'square' ) {
|
||||
app.cardButtonToggle( true );
|
||||
app.settingsToggle( true );
|
||||
app.paymentsEnabledCheck();
|
||||
el.$feeNotice.toggleClass( 'wpforms-hidden' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* We have to do several actions for UI when the "Square" credit card field is deleted.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
* @param {number} id Field ID.
|
||||
* @param {string} type Field type.
|
||||
*/
|
||||
fieldDeleted( e, id, type ) {
|
||||
if ( type === 'square' ) {
|
||||
app.cardButtonToggle( false );
|
||||
app.settingsToggle( false );
|
||||
app.disablePayments();
|
||||
app.disableNotifications();
|
||||
el.$feeNotice.toggleClass( 'wpforms-hidden' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles visibility of multiple plans warning.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
toggleMultiplePlansWarning() {
|
||||
el.$panelContent.find( '.wpforms-square-multiple-plans-warning' ).toggleClass( 'wpforms-hidden', el.$panelContent.find( '.wpforms-panel-content-section-payment-plan' ).length === 1 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable or disable the "Square" field in the fields list.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {boolean} isDisabled If true then a card button will be disabled.
|
||||
*/
|
||||
cardButtonToggle( isDisabled ) {
|
||||
el.$cardButton
|
||||
.prop( 'disabled', isDisabled )
|
||||
.toggleClass( 'wpforms-add-fields-button-disabled', isDisabled );
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle visibility of the Square payment settings.
|
||||
*
|
||||
* If the "Square" field has been added then reveal the settings,
|
||||
* otherwise hide them.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {boolean} display Show or hide settings.
|
||||
*/
|
||||
settingsToggle( display ) {
|
||||
if ( ! el.$alert.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
el.$alert.toggleClass( 'wpforms-hidden', display );
|
||||
$( '#wpforms-panel-content-section-payment-square' ).toggleClass( 'wpforms-hidden', ! display );
|
||||
|
||||
// Uncheck the Payments > Square > Enable Square Payments setting.
|
||||
if ( ! display ) {
|
||||
el.$singlePaymentControl.prop( 'checked', false ).trigger( 'change' );
|
||||
el.$recurringPaymentControl.prop( 'checked', false ).trigger( 'change' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Make sure that "One-Time Payments" and "Recurring Payments" toggles are turned off.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
disablePayments() {
|
||||
const toggleInput = $( '#wpforms-panel-field-square-enable_one_time, #wpforms-panel-field-square-enable_recurring' );
|
||||
|
||||
toggleInput.prop( 'checked', false ).trigger( 'change' ).each( WPFormsBuilderPaymentsUtils.toggleContent );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable notifications.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
disableNotifications() {
|
||||
const $notificationWrap = $( '.wpforms-panel-content-section-notifications [id*="-square-wrap"]' );
|
||||
|
||||
$notificationWrap.find( 'input[id*="-square"]' ).prop( 'checked', false );
|
||||
$notificationWrap.addClass( 'wpforms-hidden' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether payments are enabled in the Payments > Square panel.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} Payments are enabled.
|
||||
*/
|
||||
isPaymentsEnabled() {
|
||||
return el.$singlePaymentControl.is( ':checked' ) || el.$recurringPaymentControl.is( ':checked' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether AJAX form submission is enabled in the Settings > General.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} AJAX form submission is enabled.
|
||||
*/
|
||||
isAJAXSubmitEnabled() {
|
||||
return el.$AJAXSubmitOption.is( ':checked' );
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsBuilderSquare.init();
|
||||
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
+375
@@ -0,0 +1,375 @@
|
||||
/* global wpforms_admin, WPFormsAdmin, wpf */
|
||||
|
||||
/**
|
||||
* WPForms Square settings function.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
const WPFormsSettingsSquare = window.WPFormsSettingsSquare || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Elements.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const $el = {
|
||||
sandboxModeCheckbox: $( '#wpforms-setting-square-sandbox-mode' ),
|
||||
sandboxConnectionStatusBlock: $( '#wpforms-setting-row-square-connection-status-sandbox' ),
|
||||
productionConnectionStatusBlock: $( '#wpforms-setting-row-square-connection-status-production' ),
|
||||
sandboxLocationBlock: $( '#wpforms-setting-row-square-location-id-sandbox' ),
|
||||
sandboxLocationStatusBlock: $( '#wpforms-setting-row-square-location-status-sandbox' ),
|
||||
productionLocationBlock: $( '#wpforms-setting-row-square-location-id-production' ),
|
||||
productionLocationStatusBlock: $( '#wpforms-setting-row-square-location-status-production' ),
|
||||
refreshBtn: $( '.wpforms-square-refresh-btn' ),
|
||||
copyButton: $( '#wpforms-setting-row-square-webhooks-endpoint-set .wpforms-copy-to-clipboard' ),
|
||||
webhooksEnableCheckbox: $( '#wpforms-setting-square-webhooks-enabled' ),
|
||||
webhookEndpointUrl: $( 'input#wpforms-square-webhook-endpoint-url' ),
|
||||
webhookMethod: $( 'input[name="square-webhooks-communication"]' ),
|
||||
webhookCommunicationStatusNotice: $( '#wpforms-setting-row-square-webhooks-communication-status' ),
|
||||
webhookConnectBtn: $( '#wpforms-setting-square-webhooks-connect' ),
|
||||
webhookConnectRow: $( '#wpforms-setting-row-square-webhooks-connect' ),
|
||||
webhookConnectStatusRow: $( '#wpforms-setting-row-square-webhooks-connect-status-production, #wpforms-setting-row-square-webhooks-connect-status-sandbox' ),
|
||||
};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* Document ready.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
ready() {
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Register JS events.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
events() {
|
||||
$el.sandboxModeCheckbox.on( 'change', app.credentialsFieldsDisplay );
|
||||
$el.refreshBtn.on( 'click', app.refreshTokensCallback );
|
||||
$el.webhooksEnableCheckbox.on( 'change', app.webhooksEnableCallback );
|
||||
$el.webhookConnectBtn.on( 'click', app.modals.displayWebhookConfigPopup );
|
||||
$el.webhookMethod.on( 'change', app.updateWebhookEndpointUrl );
|
||||
$el.copyButton.on( 'click', function( e ) {
|
||||
wpf.copyValueToClipboard( e, $( this ), $el.webhookEndpointUrl );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the endpoint URL.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
updateWebhookEndpointUrl() {
|
||||
const checked = $el.webhookMethod.filter( ':checked' ).val(),
|
||||
newUrl = wpforms_admin.square.webhook_urls[ checked ];
|
||||
|
||||
$el.webhookEndpointUrl.val( newUrl );
|
||||
$el.webhookCommunicationStatusNotice.removeClass( 'wpforms-hide' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable webhooks.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
webhooksEnableCallback() {
|
||||
$el.webhookConnectRow.toggleClass( 'wpforms-hide', ! $( this ).is( ':checked' ) );
|
||||
$el.webhookConnectStatusRow.toggleClass( 'wpforms-hide', ! $( this ).is( ':checked' ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a webhook.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {string} token Personal access token.
|
||||
*
|
||||
* @return {Promise} Promise an object.
|
||||
*/
|
||||
createWebhook( token ) {
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
$.ajax( {
|
||||
url: wpforms_admin.ajax_url,
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
action: 'wpforms_square_create_webhook',
|
||||
nonce: wpforms_admin.nonce,
|
||||
token,
|
||||
},
|
||||
success( response ) {
|
||||
if ( response.success ) {
|
||||
resolve( response );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
reject( response );
|
||||
},
|
||||
error() {
|
||||
reject( { success: false, message: 'An error occurred.' } );
|
||||
},
|
||||
} );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh tokens.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
refreshTokensCallback() {
|
||||
const $btn = $( this );
|
||||
const buttonWidth = $btn.outerWidth();
|
||||
const buttonLabel = $btn.text();
|
||||
const settings = {
|
||||
url: wpforms_admin.ajax_url,
|
||||
type: 'post',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
action: 'wpforms_square_refresh_connection',
|
||||
nonce: wpforms_admin.nonce,
|
||||
mode: $btn.data( 'mode' ),
|
||||
},
|
||||
beforeSend() {
|
||||
$btn.css( 'width', buttonWidth ).html( WPFormsAdmin.settings.iconSpinner ).prop( 'disabled', true );
|
||||
},
|
||||
};
|
||||
|
||||
let errorMessage = wpforms_admin.square.refresh_error;
|
||||
|
||||
// Perform an Ajax request.
|
||||
$.ajax( settings )
|
||||
.done( function( response ) {
|
||||
if ( response.success ) {
|
||||
$btn
|
||||
.css( 'pointerEvents', 'none' )
|
||||
.removeClass( 'wpforms-btn-light-grey' )
|
||||
.addClass( 'wpforms-btn-grey' )
|
||||
.html( 'Refreshed!' );
|
||||
|
||||
$btn.closest( 'form' ).css( 'cursor', 'wait' );
|
||||
|
||||
window.location = $btn.data( 'url' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call( response, 'data' ) &&
|
||||
response.data !== ''
|
||||
) {
|
||||
errorMessage = response.data;
|
||||
}
|
||||
|
||||
$btn
|
||||
.css( 'width', 'auto' )
|
||||
.html( buttonLabel )
|
||||
.prop( 'disabled', false );
|
||||
app.modals.refreshTokensError( errorMessage );
|
||||
} )
|
||||
.fail( function() {
|
||||
$btn
|
||||
.css( 'width', 'auto' )
|
||||
.html( buttonLabel )
|
||||
.prop( 'disabled', false );
|
||||
app.modals.refreshTokensError( errorMessage );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Conditionally show Square mode switch warning.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
credentialsFieldsDisplay() {
|
||||
const sandboxModeEnabled = $el.sandboxModeCheckbox.is( ':checked' );
|
||||
|
||||
if ( sandboxModeEnabled ) {
|
||||
$el.sandboxConnectionStatusBlock.show();
|
||||
$el.sandboxLocationBlock.show();
|
||||
$el.sandboxLocationStatusBlock.show();
|
||||
|
||||
$el.productionConnectionStatusBlock.hide();
|
||||
$el.productionLocationBlock.hide();
|
||||
$el.productionLocationStatusBlock.hide();
|
||||
} else {
|
||||
$el.sandboxConnectionStatusBlock.hide();
|
||||
$el.sandboxLocationBlock.hide();
|
||||
$el.sandboxLocationStatusBlock.hide();
|
||||
|
||||
$el.productionConnectionStatusBlock.show();
|
||||
$el.productionLocationBlock.show();
|
||||
$el.productionLocationStatusBlock.show();
|
||||
}
|
||||
|
||||
if ( sandboxModeEnabled && $el.sandboxConnectionStatusBlock.find( '.wpforms-square-connected' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! sandboxModeEnabled && $el.productionConnectionStatusBlock.find( '.wpforms-square-connected' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
app.modals.modeChangedWarning();
|
||||
},
|
||||
|
||||
/**
|
||||
* Modals.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
modals: {
|
||||
|
||||
/**
|
||||
* Show the warning modal when Square mode is changed.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
modeChangedWarning() {
|
||||
$.alert( {
|
||||
title: wpforms_admin.heads_up,
|
||||
content: wpforms_admin.square.mode_update,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_admin.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh tokens error handling.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {string} error Error message.
|
||||
*/
|
||||
refreshTokensError( error ) {
|
||||
$.alert( {
|
||||
title: false,
|
||||
content: error,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'orange',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_admin.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Show popup with the ability to register a new webhook route or retrieve existing one.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
displayWebhookConfigPopup() {
|
||||
$.confirm( {
|
||||
title: wpforms_admin.square.webhook_create_title,
|
||||
content: wpforms_admin.square.webhook_create_description +
|
||||
'<input type="text" id="wpforms-square-personal-access-token" placeholder="' + wpforms_admin.square.webhook_token_placeholder + '" value="">' +
|
||||
'<p class="wpforms-square-webhooks-connect-error error" style="display:none;">' + wpforms_admin.square.token_is_required + '</p>',
|
||||
icon: 'fa fa-info-circle',
|
||||
type: 'blue',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_admin.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
action() {
|
||||
const modal = this;
|
||||
const tokenField = modal.$content.find( '#wpforms-square-personal-access-token' );
|
||||
const errorMsg = modal.$content.find( '.error' );
|
||||
const token = tokenField.val().trim();
|
||||
const title = modal.$title;
|
||||
|
||||
// Disable the button to prevent multiple clicks.
|
||||
$el.webhookConnectBtn.addClass( 'inactive' );
|
||||
|
||||
// Reset error message before validation
|
||||
errorMsg.hide().text( '' );
|
||||
|
||||
if ( token === '' ) {
|
||||
errorMsg.text( wpforms_admin.square.token_is_required ).show();
|
||||
return false; // Prevent modal from closing.
|
||||
}
|
||||
|
||||
// Show loading indicator.
|
||||
modal.buttons.confirm.setText( wpforms_admin.loading );
|
||||
modal.buttons.confirm.disable();
|
||||
|
||||
// Call API.
|
||||
app.createWebhook( token )
|
||||
.then( ( response ) => {
|
||||
modal.setContent( '<p>' + response.data.message + '</p>' );
|
||||
// Hide OK button and rename Cancel to Close.
|
||||
modal.buttons.confirm.hide();
|
||||
title.text( '' ).hide();
|
||||
modal.buttons.cancel.setText( wpforms_admin.close );
|
||||
|
||||
// Ensure user can manually close the modal.
|
||||
modal.buttons.cancel.action = function() {
|
||||
window.location.reload();
|
||||
};
|
||||
} )
|
||||
.catch( ( responseError ) => {
|
||||
errorMsg.text( responseError.data.message ).show();
|
||||
|
||||
// Re-enable confirm button for retrying.
|
||||
modal.buttons.confirm.setText( wpforms_admin.ok );
|
||||
modal.buttons.confirm.enable();
|
||||
} );
|
||||
|
||||
return false; // Prevent modal from closing immediately.
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
text: wpforms_admin.cancel,
|
||||
action() {
|
||||
// Re-enable the button.
|
||||
$el.webhookConnectBtn.removeClass( 'inactive' );
|
||||
|
||||
this.close();
|
||||
},
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsSettingsSquare.init();
|
||||
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
+807
@@ -0,0 +1,807 @@
|
||||
/* global Square, wpforms, wpforms_settings, wpforms_square, WPForms, WPFormsUtils */
|
||||
|
||||
/**
|
||||
* WPForms Square function.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
const WPFormsSquare = window.WPFormsSquare || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Holder for original form submit handler.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Function}
|
||||
*/
|
||||
let originalSubmitHandler;
|
||||
|
||||
/**
|
||||
* Credit card data.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const cardData = {
|
||||
cardNumber: {
|
||||
empty: true,
|
||||
valid: false,
|
||||
},
|
||||
expirationDate: {
|
||||
empty: true,
|
||||
valid: false,
|
||||
},
|
||||
cvv: {
|
||||
empty: true,
|
||||
valid: false,
|
||||
},
|
||||
postalCode: {
|
||||
empty: true,
|
||||
valid: false,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Square payments object.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
payments: null,
|
||||
|
||||
/**
|
||||
* Number of page locked to switch.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
lockedPageToSwitch: 0,
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
init() {
|
||||
app.payments = app.getPaymentsInstance();
|
||||
|
||||
// Bail if a Square payments object isn't initialized.
|
||||
if ( app.payments === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$( document )
|
||||
.on( 'wpformsReady', app.setupForms )
|
||||
.on( 'wpformsBeforePageChange', app.pageChange )
|
||||
.on( 'wpformsPageChange', app.afterPageChange )
|
||||
.on( 'wpformsProcessConditionalsField', app.conditionalLogicHandler );
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup and configure Square forms.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
setupForms() {
|
||||
if ( typeof $.fn.validate === 'undefined' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$( '.wpforms-square form' )
|
||||
.filter( ( _, form ) => typeof $( form ).data( 'formid' ) === 'number' ) // filter out forms which are locked (formid changed to 'locked-...').
|
||||
.each( app.updateSubmitHandler );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update submitHandler for the forms containing Square.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
async updateSubmitHandler() {
|
||||
const $form = $( this );
|
||||
const validator = $form.data( 'validator' );
|
||||
|
||||
if ( ! validator || $form.hasClass( 'wpforms-square-initialization' ) || $form.hasClass( 'wpforms-square-initialized' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the form is inside the "raw" elementor popup, we should not initialize the Square and wait for the popup to be opened.
|
||||
if ( $form.closest( '.elementor-location-popup' ).length && ! $form.closest( '.elementor-popup-modal' ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$form.addClass( 'wpforms-square-initialization' );
|
||||
|
||||
// Store the original submitHandler.
|
||||
originalSubmitHandler = validator.settings.submitHandler;
|
||||
|
||||
// Replace the default submit handler.
|
||||
validator.settings.submitHandler = app.submitHandler;
|
||||
|
||||
// Get a new Card object.
|
||||
await app.getCardInstance( $form );
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger resize event if Square field has been conditionally unhidden.
|
||||
*
|
||||
* Allows Square Card field to resize itself (fixes the issue with doubled field height on some screen resolutions).
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Event} e Event.
|
||||
* @param {number} formID Form ID.
|
||||
* @param {number} fieldID Field ID.
|
||||
* @param {boolean} pass Pass condition logic.
|
||||
* @param {string} action Action name.
|
||||
*/
|
||||
conditionalLogicHandler( e, formID, fieldID, pass, action ) {
|
||||
if ( ! app.isVisibleField( pass, action ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const el = document.getElementById( 'wpforms-' + formID + '-field_' + fieldID );
|
||||
|
||||
if ( ! el || ! el.classList.contains( 'wpforms-field-square-cardnumber' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.dispatchEvent( new Event( 'resize' ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if the field is visible after being triggered by Conditional Logic.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {boolean} pass Pass condition logic.
|
||||
* @param {string} action Action name.
|
||||
*
|
||||
* @return {boolean} The field is visible.
|
||||
*/
|
||||
isVisibleField( pass, action ) {
|
||||
return ( action === 'show' && pass ) || ( action === 'hide' && ! pass );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update submitHandler for forms containing Square.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Object} form JS form element.
|
||||
*/
|
||||
submitHandler( form ) {
|
||||
const $form = $( form );
|
||||
const validator = $form.data( 'validator' );
|
||||
const validForm = validator.form();
|
||||
const card = $form.find( '.wpforms-square-credit-card-hidden-input' ).data( 'square-card' );
|
||||
|
||||
if ( ! validForm || typeof card === 'undefined' || ! app.isProcessedCard( $form ) ) {
|
||||
originalSubmitHandler( $form );
|
||||
return;
|
||||
}
|
||||
|
||||
app.tokenize( $form, card );
|
||||
},
|
||||
|
||||
/**
|
||||
* Tokenize a card payment.
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
* @param {Object} card Square Card object.
|
||||
*/
|
||||
async tokenize( $form, card ) {
|
||||
app.disableSubmitBtn( $form );
|
||||
|
||||
const sourceId = await app.getSourceId( $form, card );
|
||||
|
||||
if ( sourceId === null ) {
|
||||
app.enableSubmitBtn( $form );
|
||||
return;
|
||||
}
|
||||
|
||||
app.submitForm( $form );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize a Square payments object and retrieve it.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {Object|null} Square payments object or null.
|
||||
*/
|
||||
getPaymentsInstance() {
|
||||
if ( ! window.Square ) {
|
||||
app.displaySdkError( $( '.wpforms-square form' ), wpforms_square.i18n.missing_sdk_script );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
return Square.payments( wpforms_square.client_id, wpforms_square.location_id );
|
||||
} catch ( e ) {
|
||||
const message = ( typeof e === 'object' && Object.prototype.hasOwnProperty.call( e, 'message' ) ) ? e.message : wpforms_square.i18n.missing_creds;
|
||||
|
||||
app.displaySdkError( $( '.wpforms-square form' ), message );
|
||||
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Try to retrieve a Square Card object.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @return {Object|null} Square Card object or null.
|
||||
*/
|
||||
async getCardInstance( $form ) {
|
||||
// Applying the modern styles to the card config if needed.
|
||||
// eslint-disable-next-line prefer-const
|
||||
let cardConfig = {};
|
||||
|
||||
cardConfig.style = wpforms_square.card_config.style ? wpforms_square.card_config.style : app.getModernMarkupCardStyles( $form );
|
||||
|
||||
try {
|
||||
const card = await app.payments.card( cardConfig );
|
||||
|
||||
// Attach the Card form to the page.
|
||||
await card.attach( $form.find( '.wpforms-field-square-cardnumber' ).get( 0 ) );
|
||||
|
||||
const eventsList = [ 'focusClassAdded', 'focusClassRemoved' ];
|
||||
const eventsLength = eventsList.length;
|
||||
let counter = 0;
|
||||
|
||||
// Bind the Card events.
|
||||
for ( ; counter < eventsLength; counter++ ) {
|
||||
card.addEventListener( eventsList[ counter ], function( e ) {
|
||||
// Card field is filled.
|
||||
cardData[ e.detail.field ].empty = e.detail.currentState.isEmpty;
|
||||
cardData[ e.detail.field ].valid = e.detail.currentState.isCompletelyValid;
|
||||
|
||||
if ( cardData[ e.detail.field ].valid ) {
|
||||
app.removeFieldError( $form );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
$form.find( '.wpforms-square-credit-card-hidden-input' ).data( 'square-card', card );
|
||||
|
||||
$form.removeClass( 'wpforms-square-initialization' );
|
||||
$form.addClass( 'wpforms-square-initialized' );
|
||||
|
||||
return card;
|
||||
} catch ( e ) {
|
||||
app.displaySdkError( $form, wpforms_square.i18n.card_init_error );
|
||||
|
||||
$form.removeClass( 'wpforms-square-initialization' );
|
||||
|
||||
console.log( 'Error:', e ); // eslint-disable-line no-console
|
||||
console.log( 'Config', cardConfig ); // eslint-disable-line no-console
|
||||
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve a source ID (card nonce).
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
* @param {Object} card Square Card object.
|
||||
*
|
||||
* @return {string|null} The source ID or null.
|
||||
*/
|
||||
async getSourceId( $form, card ) {
|
||||
try {
|
||||
const response = await card.tokenize( app.getChargeVerifyBuyerDetails( $form ) );
|
||||
|
||||
$form.find( '.wpforms-square-payment-source-id' ).remove();
|
||||
|
||||
if ( response.status !== 'OK' || ! response.token ) {
|
||||
app.displayFormError( app.getCreditCardInput( $form ), app.getResponseError( response ) );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$form.append( '<input type="hidden" name="wpforms[square][source_id]" class="wpforms-square-payment-source-id" value="' + app.escapeTextString( response.token ) + '">' );
|
||||
|
||||
return response.token;
|
||||
} catch ( e ) {
|
||||
app.displayFormError( app.getCreditCardInput( $form ), wpforms_square.i18n.token_process_fail );
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve a response error message.
|
||||
*
|
||||
* @param {Object} response The response received from a tokenization call.
|
||||
*
|
||||
* @return {string} The response error message.
|
||||
*/
|
||||
getResponseError( response ) {
|
||||
const hasErrors = response.errors && Array.isArray( response.errors ) && response.errors.length;
|
||||
|
||||
return hasErrors ? response.errors[ 0 ].message : wpforms_square.i18n.token_status_error + ' ' + response.status;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve details about the buyer for a charge.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @return {Object} Buyer details.
|
||||
*/
|
||||
getChargeVerifyBuyerDetails( $form ) {
|
||||
return {
|
||||
amount: app.getTotalInMinorUnits( wpforms.amountTotalCalc( $form ) ),
|
||||
billingContact: app.getBillingContactDetails( $form ),
|
||||
currencyCode: wpforms_settings.currency_code,
|
||||
intent: 'CHARGE',
|
||||
customerInitiated: true,
|
||||
sellerKeyedIn: false,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the total amount in minor units.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {number} total Total amount.
|
||||
*
|
||||
* @return {string} Total amount in minor units.
|
||||
*/
|
||||
getTotalInMinorUnits( total ) {
|
||||
return parseInt( wpforms.numberFormat( total, wpforms_settings.currency_decimal, '', '' ), 10 ).toString();
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve billing contact details.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @return {Object} Billing contact details.
|
||||
*/
|
||||
getBillingContactDetails( $form ) { // eslint-disable-line complexity
|
||||
// Get the form ID and billing mapping for this form, if available.
|
||||
const formId = $form.data( 'formid' );
|
||||
const mapping = ( wpforms_square.billing_details && wpforms_square.billing_details[ formId ] ) || {};
|
||||
const result = {};
|
||||
|
||||
// Use mapped selectors if provided.
|
||||
const $emailField = mapping.buyer_email ? $( `.wpforms-field-email[data-field-id="${ mapping.buyer_email }"]` ) : '';
|
||||
const $nameField = mapping.billing_name ? $( `.wpforms-field-name[data-field-id="${ mapping.billing_name }"]` ) : '';
|
||||
const $addressField = mapping.billing_address ? $( `.wpforms-field-address[data-field-id="${ mapping.billing_address }"]` ) : '';
|
||||
|
||||
if ( $emailField.length ) {
|
||||
const emailValue = $emailField.find( 'input' ).first().val(); // Use the first input field knowing there could be confirmation email input as well.
|
||||
if ( emailValue && emailValue.trim() !== '' ) {
|
||||
result.email = emailValue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $nameField.length ) {
|
||||
jQuery.extend( result, app.getBillingNameDetails( $nameField ) );
|
||||
}
|
||||
|
||||
if ( $addressField.length ) {
|
||||
jQuery.extend( result, app.getBillingAddressDetails( $addressField ) );
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve billing name details.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $field Field element.
|
||||
*
|
||||
* @return {Object} Billing name details.
|
||||
*/
|
||||
getBillingNameDetails( $field ) { // eslint-disable-line complexity
|
||||
const result = {};
|
||||
|
||||
let givenName = '';
|
||||
let familyName = '';
|
||||
|
||||
// Try to find separate first and last name fields.
|
||||
const $firstNameField = $field.find( '.wpforms-field-name-first' );
|
||||
const $lastNameField = $field.find( '.wpforms-field-name-last' );
|
||||
|
||||
if ( $firstNameField.length && $lastNameField.length ) {
|
||||
// Use separate fields if both are present.
|
||||
givenName = $firstNameField.val() || '';
|
||||
familyName = $lastNameField.val() || '';
|
||||
|
||||
if ( givenName && givenName.trim() !== '' ) {
|
||||
result.givenName = givenName;
|
||||
}
|
||||
|
||||
if ( familyName && familyName.trim() !== '' ) {
|
||||
result.familyName = familyName;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Otherwise, fall back to a single name input field.
|
||||
const $nameField = $field.find( 'input' );
|
||||
|
||||
if ( ! $nameField.length ) {
|
||||
return result;
|
||||
}
|
||||
const fullName = $nameField.val().trim();
|
||||
|
||||
if ( ! fullName.length ) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Split full name by space; the first part is givenName,
|
||||
// the rest (if any) are combined as familyName.
|
||||
const nameParts = fullName.split( ' ' );
|
||||
givenName = nameParts.shift() || '';
|
||||
familyName = nameParts.join( ' ' ) || '';
|
||||
|
||||
if ( givenName && givenName.trim() !== '' ) {
|
||||
result.givenName = givenName;
|
||||
}
|
||||
|
||||
if ( familyName && familyName.trim() !== '' ) {
|
||||
result.familyName = familyName;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve billing address details.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $addressField Field element.
|
||||
*
|
||||
* @return {Object} Billing address details.
|
||||
*/
|
||||
getBillingAddressDetails( $addressField ) { // eslint-disable-line complexity
|
||||
const result = {};
|
||||
|
||||
// For address fields, use the closest wrapper.
|
||||
const $addressWrapper = $addressField.closest( '.wpforms-field' );
|
||||
|
||||
// Retrieve address components, defaulting to empty strings if not found.
|
||||
const addressLine1 = $addressWrapper.find( '.wpforms-field-address-address1' ).val() || '';
|
||||
const addressLine2 = $addressWrapper.find( '.wpforms-field-address-address2' ).val() || '';
|
||||
const city = $addressWrapper.find( '.wpforms-field-address-city' ).val() || '';
|
||||
const state = $addressWrapper.find( '.wpforms-field-address-state' ).val() || '';
|
||||
const country = $addressWrapper.find( '.wpforms-field-address-country' ).val() || 'US';
|
||||
const addressLines = [ addressLine1, addressLine2 ].filter( ( line ) => line && line.trim() !== '' );
|
||||
|
||||
if ( addressLines.length ) {
|
||||
result.addressLines = addressLines;
|
||||
}
|
||||
|
||||
if ( city && city.trim() !== '' ) {
|
||||
result.city = city;
|
||||
}
|
||||
if ( state && state.trim() !== '' ) {
|
||||
result.state = state;
|
||||
}
|
||||
if ( country && country.trim() !== '' ) {
|
||||
result.countryCode = country;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve a jQuery selector for Credit Card hidden input.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @return {jQuery} Credit Card hidden input.
|
||||
*/
|
||||
getCreditCardInput( $form ) {
|
||||
return $form.find( '.wpforms-square-credit-card-hidden-input' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Submit the form using the original submitHandler.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*/
|
||||
submitForm( $form ) {
|
||||
const validator = $form.data( 'validator' );
|
||||
|
||||
if ( validator ) {
|
||||
originalSubmitHandler( $form );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if a credit card should be processed.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @return {boolean} True if a credit card should be processed.
|
||||
*/
|
||||
isProcessedCard( $form ) {
|
||||
const $squareDiv = $form.find( '.wpforms-field-square-cardnumber' );
|
||||
const condHidden = $squareDiv.closest( '.wpforms-field-square' ).hasClass( 'wpforms-conditional-hide' );
|
||||
const ccRequired = !! $squareDiv.data( 'required' );
|
||||
|
||||
if ( condHidden ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ccRequired || app.isCardDataNotEmpty();
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if card data not empty.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} True if at least one credit card sub-field is filled.
|
||||
*/
|
||||
isCardDataNotEmpty() {
|
||||
return ! cardData.cardNumber.empty || ! cardData.expirationDate.empty || ! cardData.cvv.empty || ! cardData.postalCode.empty;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if card data is completely valid.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} True if at least one credit card sub-field is filled.
|
||||
*/
|
||||
isCardDataValid() {
|
||||
return cardData.cardNumber.valid && cardData.expirationDate.valid && cardData.cvv.valid && cardData.postalCode.valid;
|
||||
},
|
||||
|
||||
/**
|
||||
* Display a SDK error.
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
* @param {string} message Error messages.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
displaySdkError( $form, message ) {
|
||||
$form
|
||||
.find( '.wpforms-square-credit-card-hidden-input' )
|
||||
.closest( '.wpforms-field-square-number' )
|
||||
.append( $( '<label></label>', {
|
||||
text: message,
|
||||
class: 'wpforms-error',
|
||||
} ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove field error.
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*
|
||||
* @since 1.9.7
|
||||
*/
|
||||
removeFieldError( $form ) {
|
||||
$form.find( '.wpforms-field-square-number .wpforms-error' ).remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Display a field error using jQuery Validate library.
|
||||
*
|
||||
* @param {jQuery} $field Form element.
|
||||
* @param {string} message Error messages.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
displayFormError( $field, message ) {
|
||||
const fieldName = $field.attr( 'name' );
|
||||
const $form = $field.closest( 'form' );
|
||||
const error = {};
|
||||
|
||||
error[ fieldName ] = message;
|
||||
|
||||
wpforms.displayFormAjaxFieldErrors( $form, error );
|
||||
wpforms.scrollToError( $field );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable submit button for the form.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*/
|
||||
disableSubmitBtn( $form ) {
|
||||
$form.find( '.wpforms-submit' ).prop( 'disabled', true );
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable submit button for the form.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Form element.
|
||||
*/
|
||||
enableSubmitBtn( $form ) {
|
||||
$form.find( '.wpforms-submit' ).prop( 'disabled', false );
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces &, <, >, ", `, and ' with their escaped counterparts.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {string} string String to escape.
|
||||
*
|
||||
* @return {string} Escaped string.
|
||||
*/
|
||||
escapeTextString( string ) {
|
||||
return $( '<span></span>' ).text( string ).html();
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for a page changing.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {Event} event Event.
|
||||
* @param {number} currentPage Current page.
|
||||
* @param {jQuery} $form Current form.
|
||||
* @param {string} action The navigation action.
|
||||
*/
|
||||
pageChange( event, currentPage, $form, action ) { // eslint-disable-line complexity
|
||||
const $squareDiv = $form.find( '.wpforms-field-square-cardnumber' );
|
||||
|
||||
// Stop navigation through page break pages.
|
||||
if (
|
||||
! $squareDiv.is( ':visible' ) ||
|
||||
( ! $squareDiv.data( 'required' ) && ! app.isCardDataNotEmpty() ) ||
|
||||
( app.lockedPageToSwitch && app.lockedPageToSwitch !== currentPage ) ||
|
||||
action === 'prev'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( app.isCardDataValid() ) {
|
||||
app.removeFieldError( $form );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
app.lockedPageToSwitch = currentPage;
|
||||
|
||||
app.displayFormError( app.getCreditCardInput( $form ), wpforms_square.i18n.empty_details );
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback for a after page changing.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*/
|
||||
afterPageChange() {
|
||||
window.dispatchEvent( new Event( 'resize' ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get CSS property value.
|
||||
* In case of exception return empty string.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $element Element.
|
||||
* @param {string} property Property.
|
||||
*
|
||||
* @return {string} Property value.
|
||||
*/
|
||||
getCssPropertyValue( $element, property ) {
|
||||
try {
|
||||
return $element.css( property );
|
||||
} catch ( e ) {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether modern style are needed.
|
||||
*
|
||||
* Force run on the classic markup if it is conversational or lead form.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @return {boolean} True if the form needs styles.
|
||||
*/
|
||||
needsStyles() {
|
||||
// Styles are not needed if the classic markup is used
|
||||
// and it's neither conversational nor lead form.
|
||||
if (
|
||||
( ! window.WPForms || ! WPForms.FrontendModern ) &&
|
||||
! $( '#wpforms-conversational-form-page' ).length &&
|
||||
! $( '.wpforms-lead-forms-container' ).length
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get modern card styles.
|
||||
*
|
||||
* @since 1.9.5
|
||||
*
|
||||
* @param {jQuery} $form Current form.
|
||||
*
|
||||
* @return {Object} Card styles object.
|
||||
*/
|
||||
getModernMarkupCardStyles( $form ) {
|
||||
if ( ! app.needsStyles() ) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const $hiddenInput = app.getCreditCardInput( $form ),
|
||||
hiddenInputColor = app.getCssPropertyValue( $hiddenInput, 'color' ),
|
||||
inputStyle = {
|
||||
fontSize: app.getCssPropertyValue( $hiddenInput, 'font-size' ),
|
||||
colorText: hiddenInputColor,
|
||||
colorTextPlaceholder: hiddenInputColor,
|
||||
};
|
||||
|
||||
// Check if WPFormsUtils.cssColorsUtils object is available.
|
||||
if ( WPFormsUtils.hasOwnProperty( 'cssColorsUtils' ) &&
|
||||
typeof WPFormsUtils.cssColorsUtils.getColorWithOpacity === 'function' ) {
|
||||
inputStyle.colorText = WPFormsUtils.cssColorsUtils.getColorWithOpacity( hiddenInputColor );
|
||||
inputStyle.colorTextPlaceholder = WPFormsUtils.cssColorsUtils.getColorWithOpacity( hiddenInputColor, '0.5' );
|
||||
}
|
||||
|
||||
return {
|
||||
input: {
|
||||
color: inputStyle.colorText,
|
||||
fontSize: inputStyle.fontSize,
|
||||
},
|
||||
'input::placeholder': {
|
||||
color: inputStyle.colorTextPlaceholder,
|
||||
},
|
||||
'input.is-error': {
|
||||
color: inputStyle.colorText,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPFormsSquare.init();
|
||||
Vendored
Executable
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user