diff --git a/assets/src/js/_acf-validation.js b/assets/src/js/_acf-validation.js index 9129c7e6..91216437 100644 --- a/assets/src/js/_acf-validation.js +++ b/assets/src/js/_acf-validation.js @@ -746,13 +746,20 @@ * * @date 20/10/2021 * @since ACF 5.11.0 + * + * @param {jQuery} [$form] - Optional form element to scope the input search to. If omitted, searches the entire document. */ - var ensureInvalidFieldVisibility = function () { - // Load each ACF input field and check it's browser validation state. - var $inputs = $( '.acf-field input' ); + const ensureInvalidFieldVisibility = function ( $form ) { + // Load each ACF input field and check its browser validation state. + // Scope to the given form if provided, otherwise search the entire document. + const $inputs = $form + ? $form.find( '.acf-field input' ) + : $( '.acf-field input' ); $inputs.each( function () { - if ( ! this.checkValidity() ) { - // Field is invalid, so we need to make sure it's metabox is visible. + // Use validity.valid (a property) instead of checkValidity() (a method) to avoid + // dispatching the native 'invalid' event, which can trigger ACF validation prematurely. + if ( ! this.validity.valid ) { + // Field is invalid, so we need to make sure its metabox is visible. ensureFieldPostBoxIsVisible( $( this ) ); } } ); @@ -924,19 +931,37 @@ * * Callback when clicking submit. * + * Only acts on submit buttons that are associated with a form containing ACF fields. + * This prevents submit buttons in unrelated UI (e.g. the WP link inserter modal) from + * triggering premature validation of ACF fields on the page behind the modal. + * + * Uses the native HTMLButtonElement.form property to resolve the associated form, + * which correctly handles both DOM-ancestor forms and the HTML form="id" attribute. + * * @date 4/9/18 * @since ACF 5.7.5 * * @param object e The event object. - * @param jQuery $el The input element. + * @param jQuery $el The submit button element. * @return void */ onClickSubmit: function ( e, $el ) { + const form = $el.length ? $el[ 0 ].form : null; + + if ( ! form ) { + return; + } + + const $form = $( form ); + + if ( ! $form.find( '.acf-field' ).length ) { + return; + } + // Some browsers (safari) force their browser validation before our AJAX validation, - // so we need to make sure fields are visible earlier than showErrors() - ensureInvalidFieldVisibility(); + // so we need to make sure fields are visible earlier than showErrors(). + ensureInvalidFieldVisibility( $form ); - // store the "click event" for later use in this.onSubmit() this.set( 'originalEvent', e ); }, diff --git a/assets/src/js/pro/_acf-blocks.js b/assets/src/js/pro/_acf-blocks.js index 7da87973..292c27c8 100644 --- a/assets/src/js/pro/_acf-blocks.js +++ b/assets/src/js/pro/_acf-blocks.js @@ -740,10 +740,14 @@ const md5 = require( 'md5' ); // Replace names for JSX counterparts. name = getJSXName( name ); - // Convert JSON values. + // Convert JSON values. Ignore values that merely start with a + // JSON-like character but aren't valid JSON (e.g. an oEmbed + // title beginning with "[" or "{"). const c1 = value.charAt( 0 ); if ( c1 === '[' || c1 === '{' ) { - value = JSON.parse( value ); + try { + value = JSON.parse( value ); + } catch ( err ) {} } // Convert bool values. diff --git a/assets/src/js/pro/blocks-v3/components/block-edit.js b/assets/src/js/pro/blocks-v3/components/block-edit.js index f33d80e8..d05727e4 100644 --- a/assets/src/js/pro/blocks-v3/components/block-edit.js +++ b/assets/src/js/pro/blocks-v3/components/block-edit.js @@ -93,6 +93,63 @@ const useBlockEditorInspectorSidebarOpen = () => { return isOpen; }; +/** + * Converts serialized Google Map field values from JSON strings back into + * objects so they aren't double-encoded when the block's data is stored as + * a JSON block attribute. Google Map fields keep their value as a JSON + * string in a hidden input, which acf.serialize() would otherwise pass + * through as a string. + * + * @param {Object} $form The block form jQuery element that was serialized. + * @param {Object} serializedData The data returned by acf.serialize(); modified in place. + * @param {string} clientId The block client ID. + * @return {void} + */ +function deserializeGoogleMapValues( $form, serializedData, clientId ) { + const inputPrefix = `acf-block_${ clientId }`; + + $form + .find( '.acf-field-google-map input[type="hidden"][name]' ) + .each( function () { + if ( this.name.indexOf( inputPrefix ) !== 0 ) { + return; + } + + // Extract the bracketed key path, e.g. "[field_abc][field_def]" -> [ 'field_abc', 'field_def' ]. + const keyPath = this.name + .slice( inputPrefix.length ) + .match( /([^\[\]])+/g ); + if ( ! keyPath ) { + return; + } + + // Walk the serialized data down to the parent of the leaf key. + let parent = serializedData; + for ( let i = 0; i < keyPath.length - 1; i++ ) { + if ( parent === null || typeof parent !== 'object' ) { + return; + } + parent = parent[ keyPath[ i ] ]; + } + if ( parent === null || typeof parent !== 'object' ) { + return; + } + + const leafKey = keyPath[ keyPath.length - 1 ]; + if ( typeof parent[ leafKey ] === 'string' ) { + try { + const parsedValue = JSON.parse( parent[ leafKey ] ); + if ( + typeof parsedValue === 'object' && + parsedValue !== null + ) { + parent[ leafKey ] = parsedValue; + } + } catch ( err ) {} + } + } ); +} + /** * Main BlockEdit component wrapper * Manages block data fetching and initial setup @@ -925,6 +982,7 @@ function BlockEditInner( props ) { `acf-block_${ clientId }` ); if ( serializedData ) { + deserializeGoogleMapValues( $form, serializedData, clientId ); setTheSerializedAcfData( JSON.stringify( serializedData ) ); } else { setUserHasInteractedWithForm( false ); @@ -1167,6 +1225,11 @@ function BlockEditInner( props ) { `acf-block_${ clientId }` ); if ( serializedData ) { + deserializeGoogleMapValues( + $form, + serializedData, + clientId + ); setTheSerializedAcfData( JSON.stringify( serializedData ) ); diff --git a/assets/src/js/pro/blocks-v3/components/jsx-parser.js b/assets/src/js/pro/blocks-v3/components/jsx-parser.js index 2af38fee..89890f8b 100644 --- a/assets/src/js/pro/blocks-v3/components/jsx-parser.js +++ b/assets/src/js/pro/blocks-v3/components/jsx-parser.js @@ -137,9 +137,13 @@ function parseAttribute( attribute ) { } attrName = getJSXNameReplacement( attrName ); + // Ignore values that merely start with a JSON-like character but + // aren't valid JSON (e.g. an oEmbed title beginning with "[" or "{"). const firstChar = attrValue.charAt( 0 ); if ( firstChar === '[' || firstChar === '{' ) { - attrValue = JSON.parse( attrValue ); + try { + attrValue = JSON.parse( attrValue ); + } catch ( err ) {} } if ( attrValue === 'true' || attrValue === 'false' ) { attrValue = attrValue === 'true'; diff --git a/assets/src/sass/acf-input.scss b/assets/src/sass/acf-input.scss index c2a875ef..52b1e34a 100644 --- a/assets/src/sass/acf-input.scss +++ b/assets/src/sass/acf-input.scss @@ -1,4 +1,5 @@ @charset "UTF-8"; +@use "mixins" as *; /*-------------------------------------------------------------------------------------------- * * Vars @@ -586,6 +587,14 @@ body.acf-browser-firefox .acf-field select { border-color: #dddddd; min-height: 28px; } +.acf-input-prepend, +.acf-input-append { + // WP 7.0+ + @include wp-admin("7-0") { + min-height: 40px; + padding: 9px 8px; + } +} .acf-input-prepend { float: left; @@ -738,6 +747,12 @@ html[dir=rtl] input.acf-is-prepended.acf-is-appended { box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07) inset; min-height: 31px; } +.select2-container.-acf .select2-choices { + // WP 7.0+ + @include wp-admin("7-0") { + min-height: 40px; + } +} .select2-container.-acf .select2-choices .select2-search-choice { margin: 5px 0 5px 5px; padding: 3px 5px 3px 18px; @@ -840,6 +855,41 @@ html[dir=rtl] .select2-container.-acf .select2-choice .select2-arrow { .acf-admin-3-8 .select2-container.-acf .select2-selection { border-color: #aaa; } +.select2-container.-acf { + // WP 7.0+ + @include wp-admin("7-0") { + .select2-selection--single { + height: 40px; + + .select2-selection__rendered { + line-height: 38px; + } + } + + .select2-selection--multiple { + min-height: 40px; + line-height: 0; + + .select2-selection__rendered { + line-height: normal; + display: flex; + align-items: center; + flex-wrap: wrap; + min-height: 38px; + } + + .select2-search__field { + margin-top: 0; + } + + .select2-selection__choice { + margin-top: 0; + padding-top: 4px; + padding-bottom: 4px; + } + } + } +} .select2-container.-acf .select2-selection--multiple .select2-search--inline:first-child { float: none; } diff --git a/includes/admin/admin.php b/includes/admin/admin.php index 445908e5..ced8c300 100644 --- a/includes/admin/admin.php +++ b/includes/admin/admin.php @@ -82,6 +82,10 @@ public function admin_body_class( $classes ) { // Add body class. $classes .= ' acf-admin-5-3'; + if ( version_compare( $wp_version, '7.0', '>=' ) ) { + $classes .= ' acf-admin-7-0'; + } + // Add browser for specific CSS. $classes .= ' acf-browser-' . esc_attr( acf_get_browser() ); diff --git a/includes/blocks-auto-inline-editing.php b/includes/blocks-auto-inline-editing.php index 50b5dbbb..38a7f942 100644 --- a/includes/blocks-auto-inline-editing.php +++ b/includes/blocks-auto-inline-editing.php @@ -48,12 +48,20 @@ function get_non_auto_inline_editing_fields(): array { */ function populate_auto_inline_editing_values( $field_value, $post_id, $field ) { - global $acf_fields_used_in_block_render_template, $acf_blocks_doing_auto_inline_editing; + global $acf_fields_used_in_block_render_template, $acf_blocks_doing_auto_inline_editing, $acf_blocks_doing_auto_inline_editing_block_id; if ( ! $acf_blocks_doing_auto_inline_editing || ! empty( $field['parent_repeater'] ) ) { return $field_value; } + // Only modify values for fields fetched against the current block's own post id. + // Calls like get_field( 'foo', $other_post_id ) target a different post and must be returned unmodified + // so block templates can rely on real values (e.g. for conditional logic) instead of placeholder strings. + if ( ! empty( $acf_blocks_doing_auto_inline_editing_block_id ) + && (string) $post_id !== (string) $acf_blocks_doing_auto_inline_editing_block_id ) { + return $field_value; + } + // Add this field and its value to the global variable so we can grab it when rendering later in apply_inline_editing_attributes_to_render_template. if ( ! is_array( $field_value ) ) { if ( empty( $field_value ) ) { @@ -90,21 +98,33 @@ function populate_auto_inline_editing_values( $field_value, $post_id, $field ) { * @return string */ function apply_inline_editing_attributes_to_render_template( $path, $block, $content, $is_preview, $post_id, $wp_block, $context ): string { - global $acf_fields_used_in_block_render_template, $acf_blocks_doing_auto_inline_editing; + global $acf_fields_used_in_block_render_template, $acf_blocks_doing_auto_inline_editing, $acf_blocks_doing_auto_inline_editing_block_id; unset( $content, $is_preview, $post_id, $wp_block, $context ); - $acf_fields_used_in_block_render_template = array(); + // Save the entire previous render context (fields collected, doing-flag, block id) so a + // nested SCF block render does not clobber the parent's tracked fields or active block id. + $previous_fields = is_array( $acf_fields_used_in_block_render_template ) ? $acf_fields_used_in_block_render_template : array(); + $previous_doing_auto_inline_editing = ! empty( $acf_blocks_doing_auto_inline_editing ); + $previous_block_id = $acf_blocks_doing_auto_inline_editing_block_id ?? null; - $acf_blocks_doing_auto_inline_editing = true; + $acf_fields_used_in_block_render_template = array(); + $acf_blocks_doing_auto_inline_editing = true; + $acf_blocks_doing_auto_inline_editing_block_id = $block['id'] ?? null; ob_start(); include $path; $html = ob_get_clean(); - $acf_blocks_doing_auto_inline_editing = false; + // Process the HTML before restoring the parent context - apply_inline_editing_attributes_to_html_string() + // reads $acf_fields_used_in_block_render_template via global, so it must see THIS block's field list. + $processed_html = apply_inline_editing_attributes_to_html_string( $html, $block ); - return apply_inline_editing_attributes_to_html_string( $html, $block ); + $acf_fields_used_in_block_render_template = $previous_fields; + $acf_blocks_doing_auto_inline_editing = $previous_doing_auto_inline_editing; + $acf_blocks_doing_auto_inline_editing_block_id = $previous_block_id; + + return $processed_html; } /** @@ -120,19 +140,31 @@ function apply_inline_editing_attributes_to_render_template( $path, $block, $con * @return string */ function apply_inline_editing_attributes_to_render_callback( $render_callback, $block, $content, $is_preview, $post_id, $wp_block, $context ): string { - global $acf_fields_used_in_block_render_template, $acf_blocks_doing_auto_inline_editing; + global $acf_fields_used_in_block_render_template, $acf_blocks_doing_auto_inline_editing, $acf_blocks_doing_auto_inline_editing_block_id; - $acf_fields_used_in_block_render_template = array(); + // Save the entire previous render context (fields collected, doing-flag, block id) so a + // nested SCF block render does not clobber the parent's tracked fields or active block id. + $previous_fields = is_array( $acf_fields_used_in_block_render_template ) ? $acf_fields_used_in_block_render_template : array(); + $previous_doing_auto_inline_editing = ! empty( $acf_blocks_doing_auto_inline_editing ); + $previous_block_id = $acf_blocks_doing_auto_inline_editing_block_id ?? null; - $acf_blocks_doing_auto_inline_editing = true; + $acf_fields_used_in_block_render_template = array(); + $acf_blocks_doing_auto_inline_editing = true; + $acf_blocks_doing_auto_inline_editing_block_id = $block['id'] ?? null; ob_start(); call_user_func( $render_callback, $block, $content, $is_preview, $post_id, $wp_block, $context ); $html = ob_get_clean(); - $acf_blocks_doing_auto_inline_editing = false; + // Process the HTML before restoring the parent context - apply_inline_editing_attributes_to_html_string() + // reads $acf_fields_used_in_block_render_template via global, so it must see THIS block's field list. + $processed_html = apply_inline_editing_attributes_to_html_string( $html, $block ); + + $acf_fields_used_in_block_render_template = $previous_fields; + $acf_blocks_doing_auto_inline_editing = $previous_doing_auto_inline_editing; + $acf_blocks_doing_auto_inline_editing_block_id = $previous_block_id; - return apply_inline_editing_attributes_to_html_string( $html, $block ); + return $processed_html; } /** diff --git a/includes/forms/WC_Order.php b/includes/forms/WC_Order.php index 9092ab23..253bb22d 100644 --- a/includes/forms/WC_Order.php +++ b/includes/forms/WC_Order.php @@ -39,6 +39,31 @@ public function initialize() { add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 10, 2 ); // Only attach the save handler on order edit screens. add_action( 'woocommerce_update_order', array( $this, 'save_order' ), 10, 1 ); + add_filter( 'acf/form-post/skip_save', array( $this, 'skip_post_save_for_orders' ), 10, 3 ); + } + + /** + * Prevents ACF_Form_Post from saving order posts so that save_order() + * remains the single save entry-point. Without this, the backfill + * triggered by WooCommerce Compatibility Mode fires save_post before + * woocommerce_update_order, consuming the ACF nonce and causing + * save_order() to bail. + * + * @since ACF 6.8.6 + * + * @param boolean $skip Whether to skip the save. + * @param integer $post_id The post ID being saved. + * @param \WP_Post $post The post being saved. + * @return boolean + */ + public function skip_post_save_for_orders( $skip, $post_id, $post ) { + $order_types = function_exists( 'wc_get_order_types' ) ? wc_get_order_types( 'view-orders' ) : array( 'shop_order', 'shop_subscription' ); + + if ( $post instanceof \WP_Post && in_array( $post->post_type, $order_types, true ) ) { + return true; + } + + return $skip; } /** diff --git a/includes/locations/abstract-acf-location.php b/includes/locations/abstract-acf-location.php index 1e395c93..dd08131d 100644 --- a/includes/locations/abstract-acf-location.php +++ b/includes/locations/abstract-acf-location.php @@ -164,20 +164,22 @@ public function match( $rule, $screen, $field_group ) { * @date 17/9/19 * @since ACF 5.8.1 * - * @param array $rule The location rule data. * @param mixed $value The value to compare against. + * @param array $rule The location rule data. * @return boolean */ public function compare_to_rule( $value, $rule ) { - $result = ( $value == $rule['value'] ); + $rule_value = $rule['value'] ?? ''; + // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual, WordPress.PHP.StrictComparisons.LooseComparison -- Values may be mixed int/string types; loose comparison is intentional for upstream parity. + $result = ( $value == $rule_value ); // Allow "all" to match any value. - if ( $rule['value'] === 'all' ) { + if ( 'all' === $rule_value ) { $result = true; } // Reverse result for "!=" operator. - if ( $rule['operator'] === '!=' ) { + if ( ( $rule['operator'] ?? '' ) === '!=' ) { return ! $result; } return $result; diff --git a/includes/locations/class-acf-location-attachment.php b/includes/locations/class-acf-location-attachment.php index 09744ea6..56ef45b5 100644 --- a/includes/locations/class-acf-location-attachment.php +++ b/includes/locations/class-acf-location-attachment.php @@ -48,12 +48,13 @@ public function match( $rule, $screen, $field_group ) { $mime_type = get_post_mime_type( $attachment ); // Allow for unspecific mim_type matching such as "image" or "video". - if ( ! strpos( $rule['value'], '/' ) ) { + $rule_value = $rule['value'] ?? ''; + if ( $rule_value && ! strpos( $rule_value, '/' ) ) { // Explode mime_type into bits ([0] => type, [1] => subtype) and match type. $bits = explode( '/', $mime_type ); - if ( $bits[0] === $rule['value'] ) { - $mime_type = $rule['value']; + if ( $bits[0] === $rule_value ) { + $mime_type = $rule_value; } } return $this->compare_to_rule( $mime_type, $rule ); diff --git a/includes/locations/class-acf-location-current-user-role.php b/includes/locations/class-acf-location-current-user-role.php index 4b8ad4c6..ee4b4638 100644 --- a/includes/locations/class-acf-location-current-user-role.php +++ b/includes/locations/class-acf-location-current-user-role.php @@ -42,16 +42,17 @@ public function match( $rule, $screen, $field_group ) { } // Check super_admin value. - if ( $rule['value'] == 'super_admin' ) { + $rule_value = $rule['value'] ?? ''; + if ( 'super_admin' === $rule_value ) { $result = is_super_admin( $user->ID ); // Check role. } else { - $result = in_array( $rule['value'], $user->roles ); + $result = in_array( $rule_value, $user->roles, true ); } // Reverse result for "!=" operator. - if ( $rule['operator'] === '!=' ) { + if ( ( $rule['operator'] ?? '' ) === '!=' ) { return ! $result; } return $result; diff --git a/includes/locations/class-acf-location-current-user.php b/includes/locations/class-acf-location-current-user.php index 474d713e..24e60882 100644 --- a/includes/locations/class-acf-location-current-user.php +++ b/includes/locations/class-acf-location-current-user.php @@ -34,7 +34,7 @@ public function initialize() { * @return boolean */ public function match( $rule, $screen, $field_group ) { - switch ( $rule['value'] ) { + switch ( $rule['value'] ?? '' ) { case 'logged_in': $result = is_user_logged_in(); break; @@ -50,7 +50,7 @@ public function match( $rule, $screen, $field_group ) { } // Reverse result for "!=" operator. - if ( $rule['operator'] === '!=' ) { + if ( ( $rule['operator'] ?? '' ) === '!=' ) { return ! $result; } return $result; diff --git a/includes/locations/class-acf-location-nav-menu.php b/includes/locations/class-acf-location-nav-menu.php index bfa0bc1e..375f3ea4 100644 --- a/includes/locations/class-acf-location-nav-menu.php +++ b/includes/locations/class-acf-location-nav-menu.php @@ -44,7 +44,8 @@ public function match( $rule, $screen, $field_group ) { } // Allow for "location/xxx" rule value. - $bits = explode( '/', $rule['value'] ); + $rule['value'] = $rule['value'] ?? ''; + $bits = explode( '/', $rule['value'] ); if ( $bits[0] === 'location' ) { $location = $bits[1]; diff --git a/includes/locations/class-acf-location-page-template.php b/includes/locations/class-acf-location-page-template.php index cc08b1ef..d9399e1b 100644 --- a/includes/locations/class-acf-location-page-template.php +++ b/includes/locations/class-acf-location-page-template.php @@ -48,7 +48,7 @@ public function match( $rule, $screen, $field_group ) { // Page templates were extended in WordPress version 4.7 for all post types. // Prevent this rule (which is scoped to the "page" post type) appearing on all post types without a template selected (default template). - if ( $rule['value'] === 'default' && $post_type !== 'page' ) { + if ( 'default' === ( $rule['value'] ?? '' ) && 'page' !== $post_type ) { return false; } diff --git a/includes/locations/class-acf-location-page-type.php b/includes/locations/class-acf-location-page-type.php index 263bb370..3962f6cd 100644 --- a/includes/locations/class-acf-location-page-type.php +++ b/includes/locations/class-acf-location-page-type.php @@ -51,7 +51,7 @@ public function match( $rule, $screen, $field_group ) { } // Compare. - switch ( $rule['value'] ) { + switch ( $rule['value'] ?? '' ) { case 'front_page': $front_page = (int) get_option( 'page_on_front' ); $result = ( $front_page === $post->ID ); @@ -89,7 +89,7 @@ public function match( $rule, $screen, $field_group ) { } // Reverse result for "!=" operator. - if ( $rule['operator'] === '!=' ) { + if ( ( $rule['operator'] ?? '' ) === '!=' ) { return ! $result; } return $result; diff --git a/includes/locations/class-acf-location-post-taxonomy.php b/includes/locations/class-acf-location-post-taxonomy.php index 1b0a06dc..fbc193ef 100644 --- a/includes/locations/class-acf-location-post-taxonomy.php +++ b/includes/locations/class-acf-location-post-taxonomy.php @@ -46,7 +46,7 @@ public function match( $rule, $screen, $field_group ) { } // Get WP_Term from rule value. - $term = acf_get_term( $rule['value'] ); + $term = acf_get_term( $rule['value'] ?? '' ); if ( ! $term || is_wp_error( $term ) ) { return false; } @@ -67,7 +67,7 @@ public function match( $rule, $screen, $field_group ) { $result = ( in_array( $term->term_id, $post_terms ) || in_array( $term->slug, $post_terms ) ); // Reverse result for "!=" operator. - if ( $rule['operator'] === '!=' ) { + if ( ( $rule['operator'] ?? '' ) === '!=' ) { return ! $result; } return $result; @@ -96,8 +96,8 @@ public function get_values( $rule ) { * @return string|array */ public function get_object_subtype( $rule ) { - if ( $rule['operator'] === '==' ) { - $term = acf_decode_term( $rule['value'] ); + if ( ( $rule['operator'] ?? '' ) === '==' ) { + $term = acf_decode_term( $rule['value'] ?? '' ); if ( $term ) { $taxonomy = get_taxonomy( $term['taxonomy'] ); if ( $taxonomy ) { diff --git a/includes/locations/class-acf-location-post-template.php b/includes/locations/class-acf-location-post-template.php index ebac9b55..9c318062 100644 --- a/includes/locations/class-acf-location-post-template.php +++ b/includes/locations/class-acf-location-post-template.php @@ -97,18 +97,19 @@ public function get_values( $rule ) { * @return string|array */ public function get_object_subtype( $rule ) { - if ( $rule['operator'] === '==' ) { + if ( ( $rule['operator'] ?? '' ) === '==' ) { $post_templates = acf_get_post_templates(); + $value = $rule['value'] ?? ''; // If "default", return array of all post types which have templates. - if ( $rule['value'] === 'default' ) { + if ( 'default' === $value ) { return array_keys( $post_templates ); // Otherwise, generate list of post types that have the selected template. } else { $post_types = array(); foreach ( $post_templates as $post_type => $templates ) { - if ( isset( $templates[ $rule['value'] ] ) ) { + if ( isset( $templates[ $value ] ) ) { $post_types[] = $post_type; } } diff --git a/includes/locations/class-acf-location-post-type.php b/includes/locations/class-acf-location-post-type.php index b2177594..3d3c4840 100644 --- a/includes/locations/class-acf-location-post-type.php +++ b/includes/locations/class-acf-location-post-type.php @@ -82,8 +82,8 @@ public function get_values( $rule ) { * @return string|array */ public function get_object_subtype( $rule ) { - if ( $rule['operator'] === '==' ) { - return $rule['value']; + if ( ( $rule['operator'] ?? '' ) === '==' ) { + return $rule['value'] ?? ''; } return ''; } diff --git a/includes/locations/class-acf-location-taxonomy.php b/includes/locations/class-acf-location-taxonomy.php index ec60be49..c0f1ce7d 100644 --- a/includes/locations/class-acf-location-taxonomy.php +++ b/includes/locations/class-acf-location-taxonomy.php @@ -74,9 +74,9 @@ public function get_values( $rule ) { * @param array $rule A location rule. * @return string|array */ - function get_object_subtype( $rule ) { - if ( $rule['operator'] === '==' ) { - return $rule['value']; + public function get_object_subtype( $rule ) { + if ( ( $rule['operator'] ?? '' ) === '==' ) { + return $rule['value'] ?? ''; } return ''; } diff --git a/includes/locations/class-acf-location-user-form.php b/includes/locations/class-acf-location-user-form.php index 85d74c9b..1814803d 100644 --- a/includes/locations/class-acf-location-user-form.php +++ b/includes/locations/class-acf-location-user-form.php @@ -48,7 +48,7 @@ public function match( $rule, $screen, $field_group ) { } // The "Add / Edit" choice (foolishly valued "edit") should match true for either "add" or "edit". - if ( $rule['value'] === 'edit' && $user_form === 'add' ) { + if ( 'edit' === ( $rule['value'] ?? '' ) && 'add' === $user_form ) { $user_form = 'edit'; } diff --git a/includes/locations/class-acf-location-user-role.php b/includes/locations/class-acf-location-user-role.php index 9dca6184..a3127eeb 100644 --- a/includes/locations/class-acf-location-user-role.php +++ b/includes/locations/class-acf-location-user-role.php @@ -50,7 +50,7 @@ public function match( $rule, $screen, $field_group ) { $user_role = get_option( 'default_role' ); // Check if user can, and if so, set the value allowing them to match. - } elseif ( user_can( $user_id, $rule['value'] ) ) { + } elseif ( ( $rule['value'] ?? '' ) && user_can( $user_id, $rule['value'] ) ) { $user_role = $rule['value']; } } else { diff --git a/package-lock.json b/package-lock.json index 8a778078..98076750 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "dependencies": { "@wordpress/icons": "^10.26.0", - "dompurify": "3.3.0", + "dompurify": "3.4.11", "md5": "^2.3.0" }, "devDependencies": { @@ -13590,9 +13590,9 @@ } }, "node_modules/dompurify": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz", - "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", + "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" diff --git a/package.json b/package.json index 8b3c766c..1c0627be 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@wordpress/icons": "^10.26.0", - "dompurify": "3.3.0", + "dompurify": "3.4.11", "md5": "^2.3.0" }, "devDependencies": { diff --git a/tests/js/blocks-v3/jsx-parser.test.js b/tests/js/blocks-v3/jsx-parser.test.js index c8f255cd..22b0bd17 100644 --- a/tests/js/blocks-v3/jsx-parser.test.js +++ b/tests/js/blocks-v3/jsx-parser.test.js @@ -139,16 +139,18 @@ describe( 'parseJSX', () => { expect( result.props.config ).toEqual( { key: 'value' } ); } ); - test( 'throws SyntaxError on invalid JSON array attribute', () => { - expect( () => { - parseJSX( '
Content
' ); - } ).toThrow( SyntaxError ); + test( 'keeps invalid JSON array attribute as a string', () => { + const result = parseJSX( + '
Content
' + ); + expect( result.props.items ).toBe( '[invalid json' ); } ); - test( 'throws SyntaxError on invalid JSON object attribute', () => { - expect( () => { - parseJSX( '
Content
' ); - } ).toThrow( SyntaxError ); + test( 'keeps invalid JSON object attribute as a string', () => { + const result = parseJSX( + '
Content
' + ); + expect( result.props.config ).toBe( '{not: valid}' ); } ); } );