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,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();
File diff suppressed because one or more lines are too long
@@ -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();
File diff suppressed because one or more lines are too long