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,164 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
/**
* @param strings.custom_css
* @param strings.custom_css_notice
* @param strings.copy_paste_settings
* @param strings.copy_paste_notice
*/
/**
* Gutenberg editor block.
*
* Advanced Settings module.
*
* @since 1.8.8
*/
export default ( function( $ ) {
/**
* WP core components.
*
* @since 1.8.8
*/
const { addFilter } = wp.hooks;
const { createHigherOrderComponent } = wp.compose;
const { Fragment } = wp.element;
const { InspectorAdvancedControls } = wp.blockEditor || wp.editor;
const { TextareaControl } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { strings } = wpforms_gutenberg_form_selector;
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Initialize module.
*
* @since 1.8.8
*
* @param {Object} commonModule Common module.
*/
init( commonModule ) {
app.common = commonModule;
app.hooks();
app.events();
},
/**
* Hooks.
*
* @since 1.8.8
*/
hooks() {
addFilter(
'editor.BlockEdit',
'editorskit/custom-advanced-control',
app.withAdvancedControls
);
},
/**
* Events.
*
* @since 1.8.8
*/
events() {
$( document )
.on( 'focus click', 'textarea', app.copyPasteFocus );
},
/**
* Copy / Paste Style Settings textarea focus event.
*
* @since 1.8.8
*/
copyPasteFocus() {
const $input = $( this );
if ( $input.siblings( 'label' ).text() === strings.copy_paste_settings ) {
// Select all text, so it's easier to copy and paste value.
$input.select();
}
},
/**
* Get fields.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
*
* @return {Object} Inspector advanced controls JSX code.
*/
getFields( props ) {
// Proceed only for WPForms block and when form ID is set.
if ( props?.name !== 'wpforms/form-selector' || ! props?.attributes?.formId ) {
return null;
}
// Common event handlers.
const handlers = app.common.getSettingsFieldsHandlers( props );
return (
<InspectorAdvancedControls>
<div className={ app.common.getPanelClass( props ) + ' advanced' }>
<TextareaControl
className="wpforms-gutenberg-form-selector-custom-css"
label={ strings.custom_css }
rows="5"
spellCheck="false"
value={ props.attributes.customCss }
onChange={ ( value ) => handlers.attrChange( 'customCss', value ) }
/>
<div className="wpforms-gutenberg-form-selector-legend" dangerouslySetInnerHTML={ { __html: strings.custom_css_notice } }></div>
<TextareaControl
className="wpforms-gutenberg-form-selector-copy-paste-settings"
label={ strings.copy_paste_settings }
rows="4"
spellCheck="false"
value={ props.attributes.copyPasteJsonValue }
onChange={ ( value ) => handlers.pasteSettings( value ) }
/>
<div className="wpforms-gutenberg-form-selector-legend" dangerouslySetInnerHTML={ { __html: strings.copy_paste_notice } }></div>
</div>
</InspectorAdvancedControls>
);
},
/**
* Add controls on Advanced Settings Panel.
*
* @param {Function} BlockEdit Block edit component.
*
* @return {Function} BlockEdit Modified block edit component.
*/
withAdvancedControls: createHigherOrderComponent(
( BlockEdit ) => {
return ( props ) => {
return (
<Fragment>
<BlockEdit { ...props } />
{ app.getFields( props ) }
</Fragment>
);
};
},
'withAdvancedControls'
),
};
// Provide access to public functions/properties.
return app;
}( jQuery ) );
@@ -0,0 +1,66 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
import PropTypes from 'prop-types';
/**
* @param strings.remove_image
*/
/**
* React component for the background preview.
*
* @since 1.8.8
*
* @param {Object} props Component props.
* @param {Object} props.attributes Block attributes.
* @param {Function} props.onRemoveBackground Function to remove the background.
* @param {Function} props.onPreviewClicked Function to handle the preview click.
*
* @return {Object} React component.
*/
const BackgroundPreview = ( { attributes, onRemoveBackground, onPreviewClicked } ) => {
const { Button } = wp.components;
const { strings } = wpforms_gutenberg_form_selector;
return (
<div className="wpforms-gutenberg-form-selector-background-preview">
<style>
{ `
.wpforms-gutenberg-form-selector-background-preview-image {
--wpforms-background-url: ${ attributes.backgroundUrl };
}
` }
</style>
<input
className="wpforms-gutenberg-form-selector-background-preview-image"
onClick={ onPreviewClicked }
tabIndex={ 0 }
type="button"
onKeyDown={
( event ) => {
if ( event.key === 'Enter' || event.key === ' ' ) {
onPreviewClicked();
}
}
}
>
</input>
<Button
isSecondary
className="is-destructive"
onClick={ onRemoveBackground }
>
{ strings.remove_image }
</Button>
</div>
);
};
BackgroundPreview.propTypes = {
attributes: PropTypes.object.isRequired,
onRemoveBackground: PropTypes.func.isRequired,
onPreviewClicked: PropTypes.func.isRequired,
};
export default BackgroundPreview;
@@ -0,0 +1,607 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
import BackgroundPreview from './background-preview.js';
/**
* @param strings.background_styles
* @param strings.bottom_center
* @param strings.bottom_left
* @param strings.bottom_right
* @param strings.center_center
* @param strings.center_left
* @param strings.center_right
* @param strings.choose_image
* @param strings.image_url
* @param strings.media_library
* @param strings.no_repeat
* @param strings.repeat_x
* @param strings.repeat_y
* @param strings.select_background_image
* @param strings.select_image
* @param strings.stock_photo
* @param strings.tile
* @param strings.top_center
* @param strings.top_left
* @param strings.top_right
*/
/**
* Gutenberg editor block.
*
* Background styles panel module.
*
* @since 1.8.8
*/
export default ( function() {
/**
* WP core components.
*
* @since 1.8.8
*/
const { PanelColorSettings } = wp.blockEditor || wp.editor;
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl, TextControl, Button } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { strings, defaults } = wpforms_gutenberg_form_selector;
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Get block attributes.
*
* @since 1.8.8
*
* @return {Object} Block attributes.
*/
getBlockAttributes() {
return {
backgroundImage: {
type: 'string',
default: defaults.backgroundImage,
},
backgroundPosition: {
type: 'string',
default: defaults.backgroundPosition,
},
backgroundRepeat: {
type: 'string',
default: defaults.backgroundRepeat,
},
backgroundSizeMode: {
type: 'string',
default: defaults.backgroundSizeMode,
},
backgroundSize: {
type: 'string',
default: defaults.backgroundSize,
},
backgroundWidth: {
type: 'string',
default: defaults.backgroundWidth,
},
backgroundHeight: {
type: 'string',
default: defaults.backgroundHeight,
},
backgroundColor: {
type: 'string',
default: defaults.backgroundColor,
},
backgroundUrl: {
type: 'string',
default: defaults.backgroundUrl,
},
};
},
/**
* Get Background Styles panel JSX code.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {Object} formSelectorCommon Block properties.
* @param {Object} stockPhotos Stock Photos module.
* @param {Object} uiState UI state.
*
* @return {Object} Field styles JSX code.
*/
getBackgroundStyles( props, handlers, formSelectorCommon, stockPhotos, uiState ) { // eslint-disable-line max-lines-per-function, complexity
const isNotDisabled = uiState.isNotDisabled;
const isProEnabled = uiState.isProEnabled;
const showBackgroundPreview = uiState.showBackgroundPreview;
const setShowBackgroundPreview = uiState.setShowBackgroundPreview;
const lastBgImage = uiState.lastBgImage;
const setLastBgImage = uiState.setLastBgImage;
const tabIndex = isNotDisabled ? 0 : -1;
const cssClass = formSelectorCommon.getPanelClass( props ) + ( isNotDisabled ? '' : ' wpforms-gutenberg-panel-disabled' );
return (
<PanelBody className={ cssClass } title={ strings.background_styles }>
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
className="wpforms-gutenberg-form-selector-panel-body"
onClick={ ( event ) => {
if ( isNotDisabled ) {
return;
}
event.stopPropagation();
if ( ! isProEnabled ) {
return formSelectorCommon.education.showProModal( 'background', strings.background_styles );
}
formSelectorCommon.education.showLicenseModal( 'background', strings.background_styles, 'background-styles' );
} }
onKeyDown={ ( event ) => {
if ( isNotDisabled ) {
return;
}
event.stopPropagation();
if ( ! isProEnabled ) {
return formSelectorCommon.education.showProModal( 'background', strings.background_styles );
}
formSelectorCommon.education.showLicenseModal( 'background', strings.background_styles, 'background-styles' );
} }
>
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<SelectControl
label={ strings.image }
tabIndex={ tabIndex }
value={ props.attributes.backgroundImage }
options={ [
{ label: strings.none, value: 'none' },
{ label: strings.media_library, value: 'library' },
{ label: strings.stock_photo, value: 'stock' },
] }
onChange={ ( value ) => app.setContainerBackgroundImageWrapper( props, handlers, value, lastBgImage, setLastBgImage ) }
/>
</FlexBlock>
<FlexBlock>
{ ( props.attributes.backgroundImage !== 'none' || ! isNotDisabled ) && (
<SelectControl
label={ strings.position }
value={ props.attributes.backgroundPosition }
tabIndex={ tabIndex }
options={ [
{ label: strings.top_left, value: 'top left' },
{ label: strings.top_center, value: 'top center' },
{ label: strings.top_right, value: 'top right' },
{ label: strings.center_left, value: 'center left' },
{ label: strings.center_center, value: 'center center' },
{ label: strings.center_right, value: 'center right' },
{ label: strings.bottom_left, value: 'bottom left' },
{ label: strings.bottom_center, value: 'bottom center' },
{ label: strings.bottom_right, value: 'bottom right' },
] }
disabled={ ( props.attributes.backgroundImage === 'none' && isNotDisabled ) }
onChange={ ( value ) => handlers.styleAttrChange( 'backgroundPosition', value ) }
/>
) }
</FlexBlock>
</Flex>
{ ( props.attributes.backgroundImage !== 'none' || ! isNotDisabled ) && (
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<SelectControl
label={ strings.repeat }
tabIndex={ tabIndex }
value={ props.attributes.backgroundRepeat }
options={ [
{ label: strings.no_repeat, value: 'no-repeat' },
{ label: strings.tile, value: 'repeat' },
{ label: strings.repeat_x, value: 'repeat-x' },
{ label: strings.repeat_y, value: 'repeat-y' },
] }
disabled={ ( props.attributes.backgroundImage === 'none' && isNotDisabled ) }
onChange={ ( value ) => handlers.styleAttrChange( 'backgroundRepeat', value ) }
/>
</FlexBlock>
<FlexBlock>
<SelectControl
label={ strings.size }
tabIndex={ tabIndex }
value={ props.attributes.backgroundSizeMode }
options={ [
{ label: strings.dimensions, value: 'dimensions' },
{ label: strings.cover, value: 'cover' },
] }
disabled={ ( props.attributes.backgroundImage === 'none' && isNotDisabled ) }
onChange={ ( value ) => app.handleSizeFromDimensions( props, handlers, value ) }
/>
</FlexBlock>
</Flex>
) }
{ ( ( props.attributes.backgroundSizeMode === 'dimensions' && props.attributes.backgroundImage !== 'none' ) || ! isNotDisabled ) && (
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<__experimentalUnitControl
label={ strings.width }
tabIndex={ tabIndex }
value={ props.attributes.backgroundWidth }
isUnitSelectTabbable={ isNotDisabled }
onChange={ ( value ) => app.handleSizeFromWidth( props, handlers, value ) }
/>
</FlexBlock>
<FlexBlock>
<__experimentalUnitControl
label={ strings.height }
tabIndex={ tabIndex }
value={ props.attributes.backgroundHeight }
isUnitSelectTabbable={ isNotDisabled }
onChange={ ( value ) => app.handleSizeFromHeight( props, handlers, value ) }
/>
</FlexBlock>
</Flex>
) }
{ ( ! showBackgroundPreview || props.attributes.backgroundUrl === 'url()' ) && (
( props.attributes.backgroundImage === 'library' && (
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<Button
isSecondary
tabIndex={ tabIndex }
className={ 'wpforms-gutenberg-form-selector-media-library-button' }
onClick={ app.openMediaLibrary.bind( null, props, handlers, setShowBackgroundPreview ) }
>
{ strings.choose_image }
</Button>
</FlexBlock>
</Flex>
) ) || ( props.attributes.backgroundImage === 'stock' && (
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<Button
isSecondary
tabIndex={ tabIndex }
className={ 'wpforms-gutenberg-form-selector-media-library-button' }
onClick={ stockPhotos?.openModal.bind( null, props, handlers, 'bg-styles', setShowBackgroundPreview ) }
>
{ strings.choose_image }
</Button>
</FlexBlock>
</Flex>
) )
) }
{ ( ( showBackgroundPreview && props.attributes.backgroundImage !== 'none' ) || props.attributes.backgroundUrl !== 'url()' ) && (
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<div>
<BackgroundPreview
attributes={ props.attributes }
onRemoveBackground={
() => {
app.onRemoveBackground( setShowBackgroundPreview, handlers, setLastBgImage );
}
}
onPreviewClicked={ () => {
if ( props.attributes.backgroundImage === 'library' ) {
return app.openMediaLibrary( props, handlers, setShowBackgroundPreview );
}
return stockPhotos?.openModal( props, handlers, 'bg-styles', setShowBackgroundPreview );
} }
/>
</div>
<TextControl
label={ strings.image_url }
tabIndex={ tabIndex }
value={ props.attributes.backgroundImage !== 'none' && props.attributes.backgroundUrl }
className={ 'wpforms-gutenberg-form-selector-image-url' }
onChange={ ( value ) => handlers.styleAttrChange( 'backgroundUrl', value ) }
onLoad={ ( value ) => props.attributes.backgroundImage !== 'none' && handlers.styleAttrChange( 'backgroundUrl', value ) }
/>
</FlexBlock>
</Flex>
) }
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
<PanelColorSettings
__experimentalIsRenderedInSidebar
enableAlpha
showTitle={ false }
tabIndex={ tabIndex }
className="wpforms-gutenberg-form-selector-color-panel"
colorSettings={ [
{
value: props.attributes.backgroundColor,
onChange: ( value ) => {
if ( ! isNotDisabled ) {
return;
}
handlers.styleAttrChange( 'backgroundColor', value );
},
label: strings.background,
},
] }
/>
</FlexBlock>
</Flex>
</div>
</PanelBody>
);
},
/**
* Open media library modal and handle image selection.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {Function} setShowBackgroundPreview Set show background preview.
*/
openMediaLibrary( props, handlers, setShowBackgroundPreview ) {
const frame = wp.media( {
title: strings.select_background_image,
multiple: false,
library: {
type: 'image',
},
button: {
text: strings.select_image,
},
} );
frame.on( 'select', () => {
const attachment = frame.state().get( 'selection' ).first().toJSON();
const setAttr = {};
const attribute = 'backgroundUrl';
if ( attachment.url ) {
const value = `url(${ attachment.url })`;
setAttr[ attribute ] = value;
props.setAttributes( setAttr );
handlers.styleAttrChange( 'backgroundUrl', value );
setShowBackgroundPreview( true );
}
} );
frame.open();
},
/**
* Set container background image.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setContainerBackgroundImage( container, value ) {
if ( value === 'none' ) {
container.style.setProperty( `--wpforms-background-url`, 'url()' );
}
return true;
},
/**
* Set container background image.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block event handlers.
* @param {string} value Value.
* @param {string} lastBgImage Last background image.
* @param {Function} setLastBgImage Set last background image.
*/
setContainerBackgroundImageWrapper( props, handlers, value, lastBgImage, setLastBgImage ) {
if ( value === 'none' ) {
setLastBgImage( props.attributes.backgroundUrl );
props.attributes.backgroundUrl = 'url()';
handlers.styleAttrChange( 'backgroundUrl', 'url()' );
} else if ( lastBgImage ) {
props.attributes.backgroundUrl = lastBgImage;
handlers.styleAttrChange( 'backgroundUrl', lastBgImage );
}
handlers.styleAttrChange( 'backgroundImage', value );
},
/**
* Set container background position.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setContainerBackgroundPosition( container, value ) {
container.style.setProperty( `--wpforms-background-position`, value );
return true;
},
/**
* Set container background repeat.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setContainerBackgroundRepeat( container, value ) {
container.style.setProperty( `--wpforms-background-repeat`, value );
return true;
},
/**
* Handle real size from dimensions.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {string} value Value.
*/
handleSizeFromDimensions( props, handlers, value ) {
if ( value === 'cover' ) {
props.attributes.backgroundSize = 'cover';
handlers.styleAttrChange( 'backgroundWidth', props.attributes.backgroundWidth );
handlers.styleAttrChange( 'backgroundHeight', props.attributes.backgroundHeight );
handlers.styleAttrChange( 'backgroundSizeMode', 'cover' );
handlers.styleAttrChange( 'backgroundSize', 'cover' );
} else {
props.attributes.backgroundSize = 'dimensions';
handlers.styleAttrChange( 'backgroundSizeMode', 'dimensions' );
handlers.styleAttrChange( 'backgroundSize', props.attributes.backgroundWidth + ' ' + props.attributes.backgroundHeight );
}
},
/**
* Handle real size from width.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {string} value Value.
*/
handleSizeFromWidth( props, handlers, value ) {
props.attributes.backgroundSize = value + ' ' + props.attributes.backgroundHeight;
props.attributes.backgroundWidth = value;
handlers.styleAttrChange( 'backgroundSize', value + ' ' + props.attributes.backgroundHeight );
handlers.styleAttrChange( 'backgroundWidth', value );
},
/**
* Handle real size from height.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {string} value Value.
*/
handleSizeFromHeight( props, handlers, value ) {
props.attributes.backgroundSize = props.attributes.backgroundWidth + ' ' + value;
props.attributes.backgroundHeight = value;
handlers.styleAttrChange( 'backgroundSize', props.attributes.backgroundWidth + ' ' + value );
handlers.styleAttrChange( 'backgroundHeight', value );
},
/**
* Set container background width.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setContainerBackgroundWidth( container, value ) {
container.style.setProperty( `--wpforms-background-width`, value );
return true;
},
/**
* Set container background height.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setContainerBackgroundHeight( container, value ) {
container.style.setProperty( `--wpforms-background-height`, value );
return true;
},
/**
* Set container background url.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setBackgroundUrl( container, value ) {
container.style.setProperty( `--wpforms-background-url`, value );
return true;
},
/**
* Set container background color.
*
* @since 1.8.8
*
* @param {HTMLElement} container Container element.
* @param {string} value Value.
*
* @return {boolean} True if the value was set, false otherwise.
*/
setBackgroundColor( container, value ) {
container.style.setProperty( `--wpforms-background-color`, value );
return true;
},
_showBackgroundPreview( props ) {
return props.attributes.backgroundImage !== 'none' &&
props.attributes.backgroundUrl &&
props.attributes.backgroundUrl !== 'url()';
},
/**
* Remove background image.
*
* @since 1.8.8
*
* @param {Function} setShowBackgroundPreview Set show background preview.
* @param {Object} handlers Block handlers.
* @param {Function} setLastBgImage Set last background image.
*/
onRemoveBackground( setShowBackgroundPreview, handlers, setLastBgImage ) {
setShowBackgroundPreview( false );
handlers.styleAttrChange( 'backgroundUrl', 'url()' );
setLastBgImage( '' );
},
};
return app;
}() );
@@ -0,0 +1,181 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
/**
* @param strings.border_radius
* @param strings.border_size
* @param strings.button_color_notice
* @param strings.button_styles
* @param strings.dashed
* @param strings.solid
*/
/**
* Gutenberg editor block.
*
* Button styles panel module.
*
* @since 1.8.8
*/
export default ( ( function() {
/**
* WP core components.
*
* @since 1.8.8
*/
const { PanelColorSettings } = wp.blockEditor || wp.editor;
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { strings, defaults } = wpforms_gutenberg_form_selector;
// noinspection UnnecessaryLocalVariableJS
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Get block attributes.
*
* @since 1.8.8
*
* @return {Object} Block attributes.
*/
getBlockAttributes() {
return {
buttonSize: {
type: 'string',
default: defaults.buttonSize,
},
buttonBorderStyle: {
type: 'string',
default: defaults.buttonBorderStyle,
},
buttonBorderSize: {
type: 'string',
default: defaults.buttonBorderSize,
},
buttonBorderRadius: {
type: 'string',
default: defaults.buttonBorderRadius,
},
buttonBackgroundColor: {
type: 'string',
default: defaults.buttonBackgroundColor,
},
buttonTextColor: {
type: 'string',
default: defaults.buttonTextColor,
},
buttonBorderColor: {
type: 'string',
default: defaults.buttonBorderColor,
},
};
},
/**
* Get Button styles JSX code.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block event handlers.
* @param {Object} sizeOptions Size selector options.
* @param {Object} formSelectorCommon Form selector common object.
*
* @return {Object} Button styles JSX code.
*/
getButtonStyles( props, handlers, sizeOptions, formSelectorCommon ) { // eslint-disable-line max-lines-per-function
return (
<PanelBody className={ formSelectorCommon.getPanelClass( props ) } title={ strings.button_styles }>
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<SelectControl
label={ strings.size }
value={ props.attributes.buttonSize }
options={ sizeOptions }
onChange={ ( value ) => handlers.styleAttrChange( 'buttonSize', value ) }
/>
</FlexBlock>
<FlexBlock>
<SelectControl
label={ strings.border }
value={ props.attributes.buttonBorderStyle }
options={
[
{ label: strings.none, value: 'none' },
{ label: strings.solid, value: 'solid' },
{ label: strings.dashed, value: 'dashed' },
{ label: strings.dotted, value: 'dotted' },
]
}
onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderStyle', value ) }
/>
</FlexBlock>
</Flex>
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<__experimentalUnitControl
label={ strings.border_size }
value={ props.attributes.buttonBorderStyle === 'none' ? '' : props.attributes.buttonBorderSize }
min={ 0 }
disabled={ props.attributes.buttonBorderStyle === 'none' }
onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderSize', value ) }
isUnitSelectTabbable
/>
</FlexBlock>
<FlexBlock>
<__experimentalUnitControl
onChange={ ( value ) => handlers.styleAttrChange( 'buttonBorderRadius', value ) }
label={ strings.border_radius }
min={ 0 }
isUnitSelectTabbable
value={ props.attributes.buttonBorderRadius } />
</FlexBlock>
</Flex>
<div className="wpforms-gutenberg-form-selector-color-picker">
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
<PanelColorSettings
__experimentalIsRenderedInSidebar
enableAlpha
showTitle={ false }
className={ formSelectorCommon.getColorPanelClass( props.attributes.buttonBorderStyle ) }
colorSettings={ [
{
value: props.attributes.buttonBackgroundColor,
onChange: ( value ) => handlers.styleAttrChange( 'buttonBackgroundColor', value ),
label: strings.background,
},
{
value: props.attributes.buttonBorderColor,
onChange: ( value ) => handlers.styleAttrChange( 'buttonBorderColor', value ),
label: strings.border,
},
{
value: props.attributes.buttonTextColor,
onChange: ( value ) => handlers.styleAttrChange( 'buttonTextColor', value ),
label: strings.text,
},
] } />
<div className="wpforms-gutenberg-form-selector-legend wpforms-button-color-notice">
{ strings.button_color_notice }
</div>
</div>
</PanelBody>
);
},
};
return app;
} )() );
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,255 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
/**
* @param strings.border_color
* @param strings.border_style
* @param strings.border_width
* @param strings.container_styles
* @param strings.shadow_size
*/
/**
* Gutenberg editor block.
*
* Container styles panel module.
*
* @since 1.8.8
*/
export default ( ( $ ) => {
/**
* WP core components.
*
* @since 1.8.8
*/
const { PanelColorSettings } = wp.blockEditor || wp.editor;
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { strings, defaults } = wpforms_gutenberg_form_selector;
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Start the engine.
*
* @since 1.8.8
*/
init() {
$( app.ready );
},
/**
* Document ready.
*
* @since 1.8.8
*/
ready() {
app.events();
},
/**
* Events.
*
* @since 1.8.8
*/
events() {
},
/**
* Get block attributes.
*
* @since 1.8.8
*
* @return {Object} Block attributes.
*/
getBlockAttributes() {
return {
containerPadding: {
type: 'string',
default: defaults.containerPadding,
},
containerBorderStyle: {
type: 'string',
default: defaults.containerBorderStyle,
},
containerBorderWidth: {
type: 'string',
default: defaults.containerBorderWidth,
},
containerBorderColor: {
type: 'string',
default: defaults.containerBorderColor,
},
containerBorderRadius: {
type: 'string',
default: defaults.containerBorderRadius,
},
containerShadowSize: {
type: 'string',
default: defaults.containerShadowSize,
},
};
},
/**
* Get Container Styles panel JSX code.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block handlers.
* @param {Object} formSelectorCommon Common form selector functions.
*
* @param {Object} uiState UI state.
*
* @return {Object} Field styles JSX code.
*/
getContainerStyles( props, handlers, formSelectorCommon, uiState ) { // eslint-disable-line max-lines-per-function, complexity
let cssClass = formSelectorCommon.getPanelClass( props );
const isNotDisabled = uiState.isNotDisabled;
const isProEnabled = uiState.isProEnabled;
if ( ! isNotDisabled ) {
cssClass += ' wpforms-gutenberg-panel-disabled';
}
return (
<PanelBody className={ cssClass } title={ strings.container_styles }>
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
className="wpforms-gutenberg-form-selector-panel-body"
onClick={ ( event ) => {
if ( isNotDisabled ) {
return;
}
event.stopPropagation();
if ( ! isProEnabled ) {
return formSelectorCommon.education.showProModal( 'container', strings.container_styles );
}
formSelectorCommon.education.showLicenseModal( 'container', strings.container_styles, 'container-styles' );
} }
onKeyDown={ ( event ) => {
if ( isNotDisabled ) {
return;
}
event.stopPropagation();
if ( ! isProEnabled ) {
return formSelectorCommon.education.showProModal( 'container', strings.container_styles );
}
formSelectorCommon.education.showLicenseModal( 'container', strings.container_styles, 'container-styles' );
} }
>
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
<FlexBlock>
<__experimentalUnitControl
label={ strings.padding }
tabIndex={ isNotDisabled ? 0 : -1 }
value={ props.attributes.containerPadding }
min={ 0 }
isUnitSelectTabbable={ isNotDisabled }
onChange={ ( value ) => handlers.styleAttrChange( 'containerPadding', value ) }
/>
</FlexBlock>
<FlexBlock>
<SelectControl
label={ strings.border_style }
tabIndex={ isNotDisabled ? 0 : -1 }
value={ props.attributes.containerBorderStyle }
options={ [
{ label: strings.none, value: 'none' },
{ label: strings.solid, value: 'solid' },
{ label: strings.dotted, value: 'dotted' },
{ label: strings.dashed, value: 'dashed' },
{ label: strings.double, value: 'double' },
] }
onChange={ ( value ) => handlers.styleAttrChange( 'containerBorderStyle', value ) }
/>
</FlexBlock>
</Flex>
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
<FlexBlock>
<__experimentalUnitControl
label={ strings.border_width }
tabIndex={ isNotDisabled ? 0 : -1 }
value={ props.attributes.containerBorderStyle === 'none' ? '' : props.attributes.containerBorderWidth }
min={ 0 }
disabled={ props.attributes.containerBorderStyle === 'none' }
isUnitSelectTabbable={ isNotDisabled }
onChange={ ( value ) => handlers.styleAttrChange( 'containerBorderWidth', value ) }
/>
</FlexBlock>
<FlexBlock>
<__experimentalUnitControl
label={ strings.border_radius }
tabIndex={ isNotDisabled ? 0 : -1 }
value={ props.attributes.containerBorderRadius }
min={ 0 }
isUnitSelectTabbable={ isNotDisabled }
onChange={ ( value ) => handlers.styleAttrChange( 'containerBorderRadius', value ) }
/>
</FlexBlock>
</Flex>
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
<FlexBlock>
<SelectControl
label={ strings.shadow_size }
tabIndex={ isNotDisabled ? 0 : -1 }
value={ props.attributes.containerShadowSize }
options={ [
{ label: strings.none, value: 'none' },
{ label: strings.small, value: 'small' },
{ label: strings.medium, value: 'medium' },
{ label: strings.large, value: 'large' },
] }
onChange={ ( value ) => handlers.styleAttrChange( 'containerShadowSize', value ) }
/>
</FlexBlock>
</Flex>
<Flex gap={ 4 } align="flex-start" className="wpforms-gutenberg-form-selector-flex" justify="space-between">
<FlexBlock>
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
<PanelColorSettings
__experimentalIsRenderedInSidebar
enableAlpha
showTitle={ false }
tabIndex={ isNotDisabled ? 0 : -1 }
className={ props.attributes.containerBorderStyle === 'none' ? 'wpforms-gutenberg-form-selector-color-panel wpforms-gutenberg-form-selector-color-panel-disabled' : 'wpforms-gutenberg-form-selector-color-panel' }
colorSettings={ [
{
value: props.attributes.containerBorderColor,
onChange: ( value ) => {
if ( ! isNotDisabled ) {
return;
}
handlers.styleAttrChange( 'containerBorderColor', value );
},
label: strings.border_color,
},
] }
/>
</FlexBlock>
</Flex>
</div>
</PanelBody>
);
},
};
return app;
} )( jQuery );
@@ -0,0 +1,76 @@
/* global wpforms_education, WPFormsEducation */
/**
* WPForms Education Modal module.
*
* @since 1.8.8
*/
export default ( ( $ ) => {
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Open educational popup for users with no Pro license.
*
* @since 1.8.8
*
* @param {string} panel Panel slug.
* @param {string} feature Feature name.
*/
showProModal( panel, feature ) {
const type = 'pro';
const message = wpforms_education.upgrade[ type ].message_plural.replace( /%name%/g, feature );
const utmContent = {
container: 'Upgrade to Pro - Container Styles',
background: 'Upgrade to Pro - Background Styles',
themes: 'Upgrade to Pro - Themes',
};
$.alert( {
backgroundDismiss: true,
title: feature + ' ' + wpforms_education.upgrade[ type ].title_plural,
icon: 'fa fa-lock',
content: message,
boxWidth: '550px',
theme: 'modern,wpforms-education',
closeIcon: true,
onOpenBefore: function() { // eslint-disable-line object-shorthand
this.$btnc.after( '<div class="discount-note">' + wpforms_education.upgrade_bonus + '</div>' );
this.$btnc.after( wpforms_education.upgrade[ type ].doc.replace( /%25name%25/g, 'AP - ' + feature ) );
this.$body.find( '.jconfirm-content' ).addClass( 'lite-upgrade' );
},
buttons: {
confirm: {
text: wpforms_education.upgrade[ type ].button,
btnClass: 'btn-confirm',
keys: [ 'enter' ],
action: () => {
window.open( WPFormsEducation.core.getUpgradeURL( utmContent[ panel ], type ), '_blank' );
WPFormsEducation.core.upgradeModalThankYou( type );
},
},
},
} );
},
/**
* Open license modal.
*
* @since 1.8.8
*
* @param {string} feature Feature name.
* @param {string} fieldName Field name.
* @param {string} utmContent UTM content.
*/
showLicenseModal( feature, fieldName, utmContent ) {
WPFormsEducation.proCore.licenseModal( feature, fieldName, utmContent );
},
};
return app;
} )( jQuery );
@@ -0,0 +1,189 @@
/* global wpforms_gutenberg_form_selector */
/* jshint es3: false, esversion: 6 */
/**
* @param strings.field_styles
* @param strings.lead_forms_panel_notice_head
* @param strings.lead_forms_panel_notice_text
* @param strings.learn_more
* @param strings.use_modern_notice_head
* @param strings.use_modern_notice_link
* @param strings.use_modern_notice_text
*/
/**
* Gutenberg editor block.
*
* Field styles panel module.
*
* @since 1.8.8
*/
export default ( ( function() {
/**
* WP core components.
*
* @since 1.8.8
*/
const { PanelColorSettings } = wp.blockEditor || wp.editor;
const { SelectControl, PanelBody, Flex, FlexBlock, __experimentalUnitControl } = wp.components;
/**
* Localized data aliases.
*
* @since 1.8.8
*/
const { strings, defaults } = wpforms_gutenberg_form_selector;
// noinspection UnnecessaryLocalVariableJS
/**
* Public functions and properties.
*
* @since 1.8.8
*
* @type {Object}
*/
const app = {
/**
* Get block attributes.
*
* @since 1.8.8
*
* @return {Object} Block attributes.
*/
getBlockAttributes() {
return {
fieldSize: {
type: 'string',
default: defaults.fieldSize,
},
fieldBorderStyle: {
type: 'string',
default: defaults.fieldBorderStyle,
},
fieldBorderSize: {
type: 'string',
default: defaults.fieldBorderSize,
},
fieldBorderRadius: {
type: 'string',
default: defaults.fieldBorderRadius,
},
fieldBackgroundColor: {
type: 'string',
default: defaults.fieldBackgroundColor,
},
fieldBorderColor: {
type: 'string',
default: defaults.fieldBorderColor,
},
fieldTextColor: {
type: 'string',
default: defaults.fieldTextColor,
},
fieldMenuColor: {
type: 'string',
default: defaults.fieldMenuColor,
},
};
},
/**
* Get Field styles JSX code.
*
* @since 1.8.8
*
* @param {Object} props Block properties.
* @param {Object} handlers Block event handlers.
* @param {Object} sizeOptions Size selector options.
* @param {Object} formSelectorCommon Form selector common object.
*
* @return {Object} Field styles JSX code.
*/
getFieldStyles( props, handlers, sizeOptions, formSelectorCommon ) { // eslint-disable-line max-lines-per-function
return (
<PanelBody className={ formSelectorCommon.getPanelClass( props ) } title={ strings.field_styles }>
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<SelectControl
label={ strings.size }
value={ props.attributes.fieldSize }
options={ sizeOptions }
onChange={ ( value ) => handlers.styleAttrChange( 'fieldSize', value ) }
/>
</FlexBlock>
<FlexBlock>
<SelectControl
label={ strings.border }
value={ props.attributes.fieldBorderStyle }
options={
[
{ label: strings.none, value: 'none' },
{ label: strings.solid, value: 'solid' },
{ label: strings.dashed, value: 'dashed' },
{ label: strings.dotted, value: 'dotted' },
]
}
onChange={ ( value ) => handlers.styleAttrChange( 'fieldBorderStyle', value ) }
/>
</FlexBlock>
</Flex>
<Flex gap={ 4 } align="flex-start" className={ 'wpforms-gutenberg-form-selector-flex' } justify="space-between">
<FlexBlock>
<__experimentalUnitControl
label={ strings.border_size }
value={ props.attributes.fieldBorderStyle === 'none' ? '' : props.attributes.fieldBorderSize }
min={ 0 }
disabled={ props.attributes.fieldBorderStyle === 'none' }
onChange={ ( value ) => handlers.styleAttrChange( 'fieldBorderSize', value ) }
isUnitSelectTabbable
/>
</FlexBlock>
<FlexBlock>
<__experimentalUnitControl
label={ strings.border_radius }
value={ props.attributes.fieldBorderRadius }
min={ 0 }
isUnitSelectTabbable
onChange={ ( value ) => handlers.styleAttrChange( 'fieldBorderRadius', value ) }
/>
</FlexBlock>
</Flex>
<div className="wpforms-gutenberg-form-selector-color-picker">
<div className="wpforms-gutenberg-form-selector-control-label">{ strings.colors }</div>
<PanelColorSettings
__experimentalIsRenderedInSidebar
enableAlpha
showTitle={ false }
className={ formSelectorCommon.getColorPanelClass( props.attributes.fieldBorderStyle ) }
colorSettings={ [
{
value: props.attributes.fieldBackgroundColor,
onChange: ( value ) => handlers.styleAttrChange( 'fieldBackgroundColor', value ),
label: strings.background,
},
{
value: props.attributes.fieldBorderColor,
onChange: ( value ) => handlers.styleAttrChange( 'fieldBorderColor', value ),
label: strings.border,
},
{
value: props.attributes.fieldTextColor,
onChange: ( value ) => handlers.styleAttrChange( 'fieldTextColor', value ),
label: strings.text,
},
{
value: props.attributes.fieldMenuColor,
onChange: ( value ) => handlers.styleAttrChange( 'fieldMenuColor', value ),
label: strings.menu,
},
] }
/>
</div>
</PanelBody>
);
},
};
return app;
} )() );
File diff suppressed because it is too large Load Diff