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:
@@ -0,0 +1,919 @@
|
||||
/* global wpforms_builder, WPFormsBuilder, WPFormsUtils */
|
||||
|
||||
/**
|
||||
* @param wpforms_builder.field_cannot_be_reordered
|
||||
*/
|
||||
|
||||
// noinspection ES6ConvertVarToLetConst
|
||||
/**
|
||||
* Form Builder Fields Drag-n-Drop module.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*/
|
||||
|
||||
var WPForms = window.WPForms || {}; // eslint-disable-line no-var
|
||||
|
||||
WPForms.Admin = WPForms.Admin || {};
|
||||
WPForms.Admin.Builder = WPForms.Admin.Builder || {};
|
||||
|
||||
WPForms.Admin.Builder.DragFields = WPForms.Admin.Builder.DragFields || ( function( document, window, $ ) {
|
||||
/**
|
||||
* Elements holder.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
let el = {};
|
||||
|
||||
/**
|
||||
* Runtime variables.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const vars = {};
|
||||
|
||||
/**
|
||||
* Layout field functions wrapper.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
let fieldLayout; // eslint-disable-line prefer-const
|
||||
|
||||
/**
|
||||
* Public functions and properties.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const app = {
|
||||
|
||||
/**
|
||||
* Start the engine.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*/
|
||||
init() {
|
||||
$( app.ready );
|
||||
},
|
||||
|
||||
/**
|
||||
* DOM is fully loaded.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*/
|
||||
ready() {
|
||||
app.setup();
|
||||
app.initSortableFields();
|
||||
|
||||
app.events();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup. Prepare some variables.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*/
|
||||
setup() {
|
||||
// Cache DOM elements.
|
||||
el = {
|
||||
$builder: $( '#wpforms-builder' ),
|
||||
$sortableFieldsWrap: $( '#wpforms-panel-fields .wpforms-field-wrap' ),
|
||||
$addFieldsButtons: $( '.wpforms-add-fields-button' ).not( '.not-draggable' ).not( '.warning-modal' ).not( '.education-modal' ),
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind events.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*/
|
||||
events() {
|
||||
el.$builder
|
||||
.on( 'wpformsFieldDragToggle', app.fieldDragToggleEvent )
|
||||
.on( 'wpformsFieldAdd', function( e, id, type ) {
|
||||
// If a layout field is added, initialize its columns.
|
||||
if ( type === 'layout' ) {
|
||||
setTimeout( function() {
|
||||
$( '#wpforms-field-' + id ).find( '.wpforms-layout-column' ).each( function() {
|
||||
app.initSortableHandler( $( this ) );
|
||||
$( this ).sortable( 'enable' );
|
||||
} );
|
||||
}, 100 );
|
||||
}
|
||||
} );
|
||||
|
||||
$( document ).on( 'wpformsLayoutPresetChanged', app.layoutPresetChanged );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable drag & drop.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @since 1.7.7 Moved from admin-builder.js.
|
||||
*/
|
||||
disableDragAndDrop() {
|
||||
el.$sortableFieldsWrap.trigger( 'initSortableImmediately' );
|
||||
|
||||
el.$addFieldsButtons.filter( '.ui-draggable' ).draggable( 'disable' );
|
||||
|
||||
el.$sortableFieldsWrap.sortable( 'disable' );
|
||||
|
||||
if ( el.$sortableFieldsWrap.find( '.wpforms-layout-column.ui-sortable' ).data( 'ui-sortable' ) ) {
|
||||
el.$sortableFieldsWrap.find( '.wpforms-layout-column.ui-sortable' ).sortable( 'disable' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable drag & drop.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @since 1.7.7 Moved from admin-builder.js.
|
||||
*/
|
||||
enableDragAndDrop() {
|
||||
el.$addFieldsButtons.filter( '.ui-draggable' ).draggable( 'enable' );
|
||||
el.$sortableFieldsWrap.sortable( 'enable' );
|
||||
el.$sortableFieldsWrap.find( '.wpforms-layout-column.ui-sortable' ).sortable( 'enable' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Show popup in case if field is not draggable, and cancel moving.
|
||||
*
|
||||
* @since 1.7.5
|
||||
* @since 1.7.7 Moved from admin-builder.js.
|
||||
*
|
||||
* @param {jQuery} $field A field or list of fields.
|
||||
* @param {boolean} showPopUp Whether the pop-up should be displayed on dragging attempt.
|
||||
*/
|
||||
fieldDragDisable( $field, showPopUp = true ) {
|
||||
if ( $field.hasClass( 'ui-draggable-disabled' ) ) {
|
||||
// noinspection JSUnresolvedReference
|
||||
$field.draggable( 'enable' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let startTopPosition;
|
||||
|
||||
// noinspection JSUnresolvedReference
|
||||
$field.draggable( {
|
||||
revert: true,
|
||||
axis: 'y',
|
||||
delay: 100,
|
||||
opacity: 1,
|
||||
cursor: 'move',
|
||||
start( event, ui ) {
|
||||
startTopPosition = ui.position.top;
|
||||
},
|
||||
drag( event, ui ) {
|
||||
if ( Math.abs( ui.position.top ) - Math.abs( startTopPosition ) > 15 ) {
|
||||
if ( showPopUp ) {
|
||||
app.youCantReorderFieldPopup();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Allow field dragging.
|
||||
*
|
||||
* @since 1.7.5
|
||||
* @since 1.7.7 Moved from admin-builder.js.
|
||||
*
|
||||
* @param {jQuery} $field A field or list of fields.
|
||||
*/
|
||||
fieldDragEnable( $field ) {
|
||||
if ( $field.hasClass( 'ui-draggable' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// noinspection JSUnresolvedReference
|
||||
$field.draggable( 'disable' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the error message in the popup that you cannot reorder the field.
|
||||
*
|
||||
* @since 1.7.1
|
||||
* @since 1.7.7 Moved from admin-builder.js.
|
||||
*/
|
||||
youCantReorderFieldPopup() {
|
||||
$.confirm( {
|
||||
title: wpforms_builder.heads_up,
|
||||
content: wpforms_builder.field_cannot_be_reordered,
|
||||
icon: 'fa fa-exclamation-circle',
|
||||
type: 'red',
|
||||
buttons: {
|
||||
confirm: {
|
||||
text: wpforms_builder.ok,
|
||||
btnClass: 'btn-confirm',
|
||||
keys: [ 'enter' ],
|
||||
},
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for `wpformsFieldDragToggle` event.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @param {Object} e Event object.
|
||||
* @param {number|string} id Field ID.
|
||||
*/
|
||||
fieldDragToggleEvent( e, id ) {
|
||||
const $field = $( `#wpforms-field-${ id }` );
|
||||
|
||||
if (
|
||||
$field.hasClass( 'wpforms-field-not-draggable' ) ||
|
||||
$field.hasClass( 'wpforms-field-stick' )
|
||||
) {
|
||||
app.fieldDragDisable( $field );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
app.fieldDragEnable( $field );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize sortable fields in the builder form preview area.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*/
|
||||
initSortableFields() {
|
||||
app.initSortableContainer( el.$sortableFieldsWrap );
|
||||
|
||||
// Function to initialize all layout columns.
|
||||
const initAllLayoutColumns = function() {
|
||||
el.$builder.find( '.wpforms-layout-column' ).each( function() {
|
||||
app.initSortableHandler( $( this ) );
|
||||
$( this ).sortable( 'enable' );
|
||||
} );
|
||||
};
|
||||
|
||||
// Initialize immediately.
|
||||
initAllLayoutColumns();
|
||||
|
||||
// And again after a short delay to ensure all DOM elements are loaded.
|
||||
setTimeout( initAllLayoutColumns, 500 );
|
||||
|
||||
app.fieldDragDisable( $( '.wpforms-field-not-draggable, .wpforms-field-stick' ) );
|
||||
app.initDraggableFields();
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize sortable container with fields.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @param {jQuery} $sortable Container to make sortable.
|
||||
*/
|
||||
async initSortableContainer( $sortable ) {
|
||||
app.initSortableHandler( $sortable );
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for `wpformsLayoutPresetChanged` event.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {Object} event Event object.
|
||||
* @param {Object} fieldOptions Field options.
|
||||
*/
|
||||
async layoutPresetChanged( event, fieldOptions ) { // eslint-disable-line no-unused-vars
|
||||
const $fieldOptions = $( fieldOptions ),
|
||||
fieldId = $fieldOptions.data( 'field-id' ),
|
||||
$sortable = $( `#wpforms-field-${ fieldId } .wpforms-layout-column` );
|
||||
|
||||
// Immediately initialize all columns in this layout.
|
||||
$sortable.each( function() {
|
||||
app.initSortableHandler( $( this ) );
|
||||
|
||||
// Make sure sortable is not disabled, prevents from double initialization.
|
||||
$( this ).sortable( 'enable' );
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize sortable handler.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {jQuery} $sortable Sortable container.
|
||||
*/
|
||||
initSortableHandler( $sortable ) { // eslint-disable-line max-lines-per-function
|
||||
const $fieldOptions = $( '#wpforms-field-options' );
|
||||
const $scrollContainer = $( '#wpforms-panel-fields .wpforms-panel-content-wrap' );
|
||||
|
||||
let fieldId,
|
||||
fieldType,
|
||||
isNewField,
|
||||
$fieldOption,
|
||||
$prevFieldOption,
|
||||
prevFieldId,
|
||||
currentlyScrolling = false;
|
||||
|
||||
$sortable.sortable( {
|
||||
items: '> .wpforms-field:not(.wpforms-field-stick):not(.no-fields-preview)',
|
||||
connectWith: '.wpforms-field-wrap, .wpforms-layout-column',
|
||||
delay: 100,
|
||||
opacity: 1,
|
||||
cursor: 'move',
|
||||
cancel: '.wpforms-field-not-draggable',
|
||||
placeholder: 'wpforms-field-drag-placeholder',
|
||||
appendTo: '#wpforms-panel-fields',
|
||||
zindex: 10000,
|
||||
tolerance: 'pointer',
|
||||
distance: 1,
|
||||
start( e, ui ) {
|
||||
fieldId = ui.item.data( 'field-id' );
|
||||
fieldType = ui.item.data( 'field-type' );
|
||||
isNewField = typeof fieldId === 'undefined';
|
||||
$fieldOption = $( '#wpforms-field-option-' + fieldId );
|
||||
|
||||
vars.fieldReceived = false;
|
||||
vars.fieldRejected = false;
|
||||
vars.$sortableStart = $sortable;
|
||||
vars.startPosition = ui.item.first().index();
|
||||
|
||||
el.$builder.trigger( 'wpformsFieldDragStart', [ fieldId ] );
|
||||
},
|
||||
beforeStop( e, ui ) {
|
||||
if ( ! vars.glitchChange ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Before processing in the `stop` method, we need to perform the last check.
|
||||
if ( ! fieldLayout.isFieldAllowedInColum( fieldType, ui.item.first().parent() ) ) {
|
||||
vars.fieldRejected = true;
|
||||
}
|
||||
},
|
||||
stop( e, ui ) { // eslint-disable-line complexity
|
||||
const $field = ui.item.first();
|
||||
const $parent = $field.parent();
|
||||
|
||||
// If this is a layout field, initialize its columns.
|
||||
if ( $field.hasClass( 'wpforms-field-layout' ) ) {
|
||||
$field.find( '.wpforms-layout-column' ).each( function() {
|
||||
app.initSortableHandler( $( this ) );
|
||||
$( this ).sortable( 'enable' );
|
||||
} );
|
||||
}
|
||||
|
||||
// If the field is in the main container but was attempted to be added to a column, move it to the column.
|
||||
if (
|
||||
$parent.hasClass( 'wpforms-field-wrap' ) &&
|
||||
window.wpformsLastReceive &&
|
||||
window.wpformsLastReceive.isColumn &&
|
||||
! $field.hasClass( 'wpforms-field-layout' ) &&
|
||||
! $field.hasClass( 'wpforms-field-repeater' )
|
||||
) {
|
||||
// Move the field to the column that was last trying to receive it.
|
||||
$field.detach();
|
||||
window.wpformsLastReceive.sortable.append( $field );
|
||||
|
||||
// Mark the field as rejected to prevent further processing in the main container.
|
||||
vars.fieldRejected = true;
|
||||
|
||||
// Reset tracking variables.
|
||||
window.wpformsLastReceive = null;
|
||||
window.wpformsLastReceiveForMainWrap = null;
|
||||
}
|
||||
|
||||
ui.placeholder.removeClass( 'wpforms-field-drag-not-allowed' );
|
||||
$field.removeClass( 'wpforms-field-drag-not-allowed' );
|
||||
|
||||
// Reject not allowed fields.
|
||||
if ( vars.fieldRejected ) {
|
||||
const $targetColumn = isNewField ? $sortable : $field.parent();
|
||||
|
||||
app.revertMoveFieldToColumn( $field );
|
||||
el.$builder.trigger( 'wpformsFieldMoveRejected', [ $field, ui, $targetColumn ] );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
prevFieldId = $field.prev( '.wpforms-field, .wpforms-alert' ).data( 'field-id' );
|
||||
$prevFieldOption = $( `#wpforms-field-option-${ prevFieldId }` );
|
||||
|
||||
if ( $prevFieldOption.length > 0 ) {
|
||||
$prevFieldOption.after( $fieldOption );
|
||||
} else {
|
||||
$fieldOptions.prepend( $fieldOption );
|
||||
}
|
||||
|
||||
// In the case of changing fields' order inside the same column,
|
||||
// we just need to change the position of the field.
|
||||
if ( ! isNewField && $field.closest( '.wpforms-layout-column' ).is( $sortable ) ) {
|
||||
fieldLayout.positionFieldInColumn(
|
||||
fieldId,
|
||||
$field.index() - 1,
|
||||
$sortable
|
||||
);
|
||||
}
|
||||
|
||||
const $layoutField = $field.closest( '.wpforms-field-layout, .wpforms-field-repeater' );
|
||||
|
||||
fieldLayout.fieldOptionsUpdate( null, fieldId );
|
||||
fieldLayout.reorderLayoutFieldsOptions( $layoutField );
|
||||
|
||||
if ( ! isNewField ) {
|
||||
$field
|
||||
.removeClass( 'wpforms-field-dragging' )
|
||||
.removeClass( 'wpforms-field-drag-over' );
|
||||
}
|
||||
|
||||
$field.attr( 'style', '' );
|
||||
|
||||
el.$builder.trigger( 'wpformsFieldMove', ui );
|
||||
|
||||
vars.fieldReceived = false;
|
||||
},
|
||||
over( e, ui ) { // eslint-disable-line complexity
|
||||
const $field = ui.item.first(),
|
||||
$target = $( e.target ),
|
||||
$placeholder = $target.find( '.wpforms-field-drag-placeholder' ),
|
||||
isColumn = $target.hasClass( 'wpforms-layout-column' ),
|
||||
helper = {
|
||||
width: $target.outerWidth(),
|
||||
height: $field.outerHeight(),
|
||||
};
|
||||
|
||||
let targetClass = isColumn ? ' wpforms-field-drag-to-column' : '';
|
||||
|
||||
if ( isColumn ) {
|
||||
const columnSize = $target.attr( 'class' ).match( /wpforms-layout-column-(\d+)/ )[ 1 ];
|
||||
|
||||
targetClass += ` wpforms-field-drag-to-column-${ columnSize }`;
|
||||
targetClass += ` wpforms-field-drag-to-${ $target.parents( '.wpforms-field' ).data( 'field-type' ) }`;
|
||||
}
|
||||
|
||||
fieldId = $field.data( 'field-id' );
|
||||
fieldType = $field.data( 'field-type' ) || vars.fieldType;
|
||||
isNewField = typeof fieldId === 'undefined';
|
||||
|
||||
// Adjust helper size according to the placeholder size.
|
||||
$field
|
||||
.addClass( 'wpforms-field-dragging' + targetClass );
|
||||
|
||||
if ( ! isColumn || ! fieldLayout.isLayoutBasedField( fieldType ) ) {
|
||||
$field
|
||||
.css( {
|
||||
width: isColumn ? helper.width - 5 : helper.width,
|
||||
height: 'auto',
|
||||
} );
|
||||
}
|
||||
|
||||
const placeholderHeight = isColumn ? 90 : helper.height;
|
||||
|
||||
// Adjust placeholder height according to the height of the helper.
|
||||
$placeholder
|
||||
.removeClass( 'wpforms-field-drag-not-allowed' )
|
||||
.css( {
|
||||
height: isNewField ? placeholderHeight + 18 : helper.height,
|
||||
} );
|
||||
|
||||
// Drop to this place is not allowed.
|
||||
if ( isColumn && ! fieldLayout.isFieldAllowedInColum( fieldType, $target ) ) {
|
||||
$placeholder.addClass( 'wpforms-field-drag-not-allowed' );
|
||||
$field.addClass( 'wpforms-field-drag-not-allowed' );
|
||||
}
|
||||
|
||||
el.$builder.trigger( 'wpformsFieldDragOver', [ fieldId, $target ] );
|
||||
|
||||
// Skip if it is the existing field.
|
||||
if ( ! isNewField ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$field
|
||||
.addClass( 'wpforms-field-drag-over' )
|
||||
.removeClass( 'wpforms-field-drag-out' );
|
||||
},
|
||||
out( e, ui ) {
|
||||
const $field = ui.item.first(),
|
||||
// eslint-disable-next-line no-shadow
|
||||
fieldId = $field.data( 'field-id' ),
|
||||
// eslint-disable-next-line no-shadow
|
||||
isNewField = typeof fieldId === 'undefined';
|
||||
|
||||
$field
|
||||
.removeClass( 'wpforms-field-drag-not-allowed' )
|
||||
.removeClass( 'wpforms-field-drag-to-repeater' )
|
||||
.removeClass( 'wpforms-field-drag-to-layout' )
|
||||
.removeClass( app.getDragColumnClasses( $field.attr( 'class' ) ) );
|
||||
|
||||
if ( vars.fieldReceived ) {
|
||||
$field.attr( 'style', '' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if it is the existing field.
|
||||
if ( ! isNewField ) {
|
||||
// Remove extra class from the parent layout field.
|
||||
// Fixes disappearing of duplicate/delete field icons
|
||||
// after moving the field outside the layout field.
|
||||
$( ui.sender )
|
||||
.closest( '.wpforms-field-layout, .wpforms-field-repeater' )
|
||||
.removeClass( 'wpforms-field-child-hovered' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$field
|
||||
.addClass( 'wpforms-field-drag-out' )
|
||||
.removeClass( 'wpforms-field-drag-over' );
|
||||
},
|
||||
receive( e, ui ) { // eslint-disable-line complexity
|
||||
const $field = $( ui.helper || ui.item );
|
||||
const isColumn = $sortable.hasClass( 'wpforms-layout-column' );
|
||||
const isMainWrap = $sortable.hasClass( 'wpforms-field-wrap' );
|
||||
|
||||
// Save current field receive as the last one.
|
||||
window.wpformsLastReceive = {
|
||||
isColumn,
|
||||
isMainWrap,
|
||||
sender: ui.sender ? $( ui.sender ).attr( 'class' ) : null,
|
||||
sortable: $sortable,
|
||||
time: new Date().getTime(),
|
||||
};
|
||||
|
||||
// Check if this is a second receive for a field that was already handled by the main container.
|
||||
if (
|
||||
isColumn &&
|
||||
window.wpformsLastReceiveForMainWrap &&
|
||||
( new Date().getTime() - window.wpformsLastReceiveForMainWrap.time < 100 )
|
||||
) {
|
||||
// We need to stop this receive and cancel the operation for the main container.
|
||||
// Mark the field as rejected, which will cause it to be removed from the main container.
|
||||
vars.fieldRejected = true;
|
||||
window.wpformsLastReceiveForMainWrap = null;
|
||||
window.wpformsLastReceive = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is the main container, remember this event.
|
||||
if ( isMainWrap ) {
|
||||
window.wpformsLastReceiveForMainWrap = window.wpformsLastReceive;
|
||||
}
|
||||
|
||||
fieldId = $field.data( 'field-id' );
|
||||
fieldType = $field.data( 'field-type' ) || vars.fieldType;
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
const isNewField = typeof fieldId === 'undefined';
|
||||
|
||||
// Drop to this place is not allowed.
|
||||
if (
|
||||
isColumn &&
|
||||
! fieldLayout.isFieldAllowedInColum( fieldType, $sortable )
|
||||
) {
|
||||
vars.fieldRejected = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
vars.fieldReceived = true;
|
||||
|
||||
$field.removeClass( 'wpforms-field-drag-over' );
|
||||
|
||||
// Move existing field.
|
||||
if ( ! isNewField ) {
|
||||
fieldLayout.receiveFieldToColumn(
|
||||
fieldId,
|
||||
ui.item.index() - 1,
|
||||
$field.parent()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Add new field.
|
||||
const position = $sortable.data( 'ui-sortable' )?.currentItem?.index() || 0;
|
||||
|
||||
$field
|
||||
.addClass( 'wpforms-field-drag-over wpforms-field-drag-pending' )
|
||||
.removeClass( 'wpforms-field-drag-out' )
|
||||
.append( WPFormsBuilder.settings.spinnerInline )
|
||||
.css( 'width', '100%' );
|
||||
|
||||
el.$builder.find( '.no-fields-preview' ).remove();
|
||||
|
||||
WPFormsBuilder.fieldAdd(
|
||||
vars.fieldType,
|
||||
{
|
||||
position: isColumn ? position - 1 : position,
|
||||
placeholder: $field,
|
||||
$sortable,
|
||||
}
|
||||
);
|
||||
|
||||
vars.fieldType = undefined;
|
||||
},
|
||||
change( e, ui ) {
|
||||
const $placeholderSortable = ui.placeholder.parent();
|
||||
const $targetSortable = $( e.target );
|
||||
|
||||
vars.glitchChange = false;
|
||||
|
||||
// In some cases sortable widget display placeholder in wrong sortable instance.
|
||||
// It's happens when you drag the field over/out the last column of the last Layout field.
|
||||
if (
|
||||
! $sortable.is( $placeholderSortable ) &&
|
||||
$sortable.hasClass( 'wpforms-field-wrap' ) &&
|
||||
$placeholderSortable.hasClass( 'wpforms-layout-column' )
|
||||
) {
|
||||
vars.glitchChange = true;
|
||||
}
|
||||
|
||||
el.$builder.trigger( 'wpformsFieldDragChange', [ fieldId, $targetSortable ] );
|
||||
},
|
||||
sort( e ) {
|
||||
if ( currentlyScrolling ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollAreaHeight = 50,
|
||||
mouseYPosition = e.clientY,
|
||||
containerOffset = $scrollContainer.offset(),
|
||||
containerHeight = $scrollContainer.height(),
|
||||
containerBottom = containerOffset.top + containerHeight;
|
||||
|
||||
let operator;
|
||||
|
||||
if (
|
||||
mouseYPosition > containerOffset.top &&
|
||||
mouseYPosition < ( containerOffset.top + scrollAreaHeight )
|
||||
) {
|
||||
operator = '-=';
|
||||
} else if (
|
||||
mouseYPosition > ( containerBottom - scrollAreaHeight ) &&
|
||||
mouseYPosition < containerBottom
|
||||
) {
|
||||
operator = '+=';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
currentlyScrolling = true;
|
||||
|
||||
$scrollContainer.animate(
|
||||
{
|
||||
scrollTop: operator + ( containerHeight / 3 ) + 'px',
|
||||
},
|
||||
800,
|
||||
function() {
|
||||
currentlyScrolling = false;
|
||||
}
|
||||
);
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all classes starting with `wpforms-field-drag-to-column`.
|
||||
*
|
||||
* @since 1.9.6
|
||||
*
|
||||
* @param {string} className The class name of the field.
|
||||
*
|
||||
* @return {string} The class name of the field.
|
||||
*/
|
||||
getDragColumnClasses( className ) {
|
||||
return ( className.match( /wpforms-field-drag-to-column(-\d+|)/g ) || [] ).join( ' ' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize draggable fields buttons.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*/
|
||||
initDraggableFields() {
|
||||
el.$addFieldsButtons.draggable( {
|
||||
connectToSortable: '.wpforms-field-wrap, .wpforms-layout-column',
|
||||
delay: 200,
|
||||
cancel: false,
|
||||
scroll: false,
|
||||
opacity: 1,
|
||||
appendTo: '#wpforms-panel-fields',
|
||||
zindex: 10000,
|
||||
|
||||
helper() {
|
||||
const $this = $( this );
|
||||
const $el = $( '<div class="wpforms-field-drag-out wpforms-field-drag">' );
|
||||
|
||||
vars.fieldType = $this.data( 'field-type' );
|
||||
|
||||
return $el.html( $this.html() );
|
||||
},
|
||||
|
||||
start( e, ui ) {
|
||||
const event = WPFormsUtils.triggerEvent(
|
||||
el.$builder,
|
||||
'wpformsFieldAddDragStart',
|
||||
[ vars.fieldType, ui ]
|
||||
);
|
||||
|
||||
// Allow callbacks on `wpformsFieldAddDragStart` to cancel dragging the field
|
||||
// by triggering `event.preventDefault()`.
|
||||
if ( event.isDefaultPrevented() ) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
stop( e, ui ) {
|
||||
const event = WPFormsUtils.triggerEvent(
|
||||
el.$builder,
|
||||
'wpformsFieldAddDragStop',
|
||||
[ vars.fieldType, ui ]
|
||||
);
|
||||
|
||||
// Allow callbacks on `wpformsFieldAddDragStop` to cancel dragging the field
|
||||
// by triggering `event.preventDefault()`.
|
||||
if ( event.isDefaultPrevented() ) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Revert moving the field to the column.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @param {jQuery} $field Field object.
|
||||
*/
|
||||
revertMoveFieldToColumn( $field ) {
|
||||
const isNewField = $field.data( 'field-id' ) === undefined;
|
||||
|
||||
if ( isNewField ) {
|
||||
// Remove the field.
|
||||
$field.remove();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore existing field on the previous position.
|
||||
$field = $field.detach();
|
||||
|
||||
const $fieldInStartPosition = vars.$sortableStart
|
||||
.find( '> .wpforms-field' )
|
||||
.eq( vars.startPosition );
|
||||
|
||||
$field
|
||||
.removeClass( 'wpforms-field-dragging' )
|
||||
.removeClass( 'wpforms-field-drag-over' )
|
||||
.attr( 'style', '' );
|
||||
|
||||
if ( $fieldInStartPosition.length ) {
|
||||
$fieldInStartPosition.before( $field );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
vars.$sortableStart.append( $field );
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Layout field functions holder.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
fieldLayout = {
|
||||
|
||||
/**
|
||||
* Position field in the column inside the Layout Field.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @param {number} fieldId Field ID.
|
||||
* @param {number} position The new position of the field inside the column.
|
||||
* @param {jQuery} $sortable Sortable column container.
|
||||
*/
|
||||
positionFieldInColumn( fieldId, position, $sortable ) {
|
||||
if ( ! WPForms.Admin.Builder.FieldLayout ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WPForms.Admin.Builder.FieldLayout.positionFieldInColumn( fieldId, position, $sortable );
|
||||
},
|
||||
|
||||
/**
|
||||
* Receive field to column inside the Layout Field.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @param {number} fieldId Field ID.
|
||||
* @param {number} position Field position inside the column.
|
||||
* @param {jQuery} $sortable Sortable column container.
|
||||
*/
|
||||
receiveFieldToColumn( fieldId, position, $sortable ) {
|
||||
if ( ! WPForms.Admin.Builder.FieldLayout ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WPForms.Admin.Builder.FieldLayout.receiveFieldToColumn( fieldId, position, $sortable );
|
||||
},
|
||||
|
||||
/**
|
||||
* Update field options according to the position of the field.
|
||||
* Event `wpformsFieldOptionTabToggle` handler.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @param {Event} e Event.
|
||||
* @param {number} fieldId Field id.
|
||||
*/
|
||||
fieldOptionsUpdate( e, fieldId ) {
|
||||
if ( ! WPForms.Admin.Builder.FieldLayout ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WPForms.Admin.Builder.FieldLayout.fieldOptionsUpdate( e, fieldId );
|
||||
},
|
||||
|
||||
/**
|
||||
* Reorder fields options of the fields in columns.
|
||||
* It is not critical, but it's better to keep some order in the `fields` data array.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @param {jQuery} $layoutField Layout field object.
|
||||
*/
|
||||
reorderLayoutFieldsOptions( $layoutField ) {
|
||||
if ( ! WPForms.Admin.Builder.FieldLayout ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WPForms.Admin.Builder.FieldLayout.reorderLayoutFieldsOptions( $layoutField );
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether the field type is allowed to be in column.
|
||||
*
|
||||
* @since 1.7.7
|
||||
*
|
||||
* @param {string} fieldType Field type to check.
|
||||
* @param {jQuery} $targetColumn Target column element.
|
||||
*
|
||||
* @return {boolean} True if allowed.
|
||||
*/
|
||||
isFieldAllowedInColum( fieldType, $targetColumn ) {
|
||||
if ( ! WPForms.Admin.Builder.FieldLayout ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const isAllowed = WPForms.Admin.Builder.FieldLayout.isFieldAllowedInColum( fieldType, $targetColumn );
|
||||
|
||||
/**
|
||||
* Allows developers to determine whether the field is allowed to be dragged in column.
|
||||
*
|
||||
* @since 1.8.9
|
||||
*
|
||||
* @param {boolean} isAllowed Whether the field is allowed to be placed in the column.
|
||||
* @param {string} fieldType Field type.
|
||||
* @param {jQuery} $targetColumn Target column element.
|
||||
*
|
||||
* @return {boolean} True if allowed.
|
||||
*/
|
||||
return wp.hooks.applyFilters( 'wpforms.LayoutField.isFieldAllowedDragInColumn', isAllowed, fieldType, $targetColumn );
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether the field type is a layout-based field.
|
||||
*
|
||||
* @since 1.8.9
|
||||
*
|
||||
* @param {string} fieldType Field type to check.
|
||||
*
|
||||
* @return {boolean} True if it is the Layout-based field.
|
||||
*/
|
||||
isLayoutBasedField( fieldType ) {
|
||||
if ( ! WPForms.Admin.Builder.FieldLayout ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return WPForms.Admin.Builder.FieldLayout.isLayoutBasedField( fieldType );
|
||||
},
|
||||
};
|
||||
|
||||
// Provide access to public functions/properties.
|
||||
return app;
|
||||
}( document, window, jQuery ) );
|
||||
|
||||
// Initialize.
|
||||
WPForms.Admin.Builder.DragFields.init();
|
||||
Reference in New Issue
Block a user