← All changes
|
modules/mcp/abilities/appliers/element-config-applier.php
+73
-2
4.3.0-beta2
→
trunk
View file →
| @@ -11,8 +11,9 @@ | ||
| 11 | 11 | use Elementor\Modules\Mcp\Abilities\Appliers\V3\V3_Non_Style_Allowlist; |
| 12 | 12 | use Elementor\Modules\Mcp\Abilities\Appliers\V3\V3_Settings_Validator; |
| 13 | 13 | use Elementor\Modules\Mcp\Abilities\Build_Composition\Widget_Type_Resolver; |
| 14 | 14 | use Elementor\Modules\Mcp\Abilities\Prop_Canonicalizer; |
| 15 | +use Elementor\Modules\Mcp\Abilities\Utils\Dropped_Plain_Keys_Finder; | |
| 15 | 16 | |
| 16 | 17 | if ( ! defined( 'ABSPATH' ) ) { |
| 17 | 18 | exit; |
| 18 | 19 | } |
| @@ -45,8 +46,9 @@ | ||
| 45 | 46 | */ |
| 46 | 47 | public function apply( array &$config_id_index, array $element_config, array $widget_configs, ?Document $document = null ): array { |
| 47 | 48 | $errors = []; |
| 48 | 49 | $warnings = []; |
| 50 | + $warning_codes = []; | |
| 49 | 51 | $component_entries = []; |
| 50 | 52 | |
| 51 | 53 | foreach ( $element_config as $config_id => $settings ) { |
| 52 | 54 | if ( ! isset( $config_id_index[ $config_id ] ) || ! is_array( $settings ) ) { |
| @@ -102,10 +104,19 @@ | ||
| 102 | 104 | $node['settings'] = $this->merge_with_clears( $node['settings'] ?? [], $settings ); |
| 103 | 105 | continue; |
| 104 | 106 | } |
| 105 | 107 | |
| 106 | - $outcome = $this->resolve_settings_against_schema( $settings, $schema, $tag, $config_id, $errors, $warnings ); | |
| 108 | + $outcome = $this->resolve_settings_against_schema( $settings, $schema, $tag, $config_id, $errors, $warnings, $warning_codes ); | |
| 107 | 109 | |
| 110 | + $this->warn_dropped_object_sub_keys( | |
| 111 | + $node['settings'] ?? [], | |
| 112 | + $outcome['resolved'], | |
| 113 | + $tag, | |
| 114 | + $config_id, | |
| 115 | + $warnings, | |
| 116 | + $warning_codes | |
| 117 | + ); | |
| 118 | + | |
| 108 | 119 | $node['settings'] = array_merge( $node['settings'] ?? [], $outcome['resolved'] ); |
| 109 | 120 | |
| 110 | 121 | foreach ( $outcome['cleared'] as $cleared_key ) { |
| 111 | 122 | unset( $node['settings'][ $cleared_key ] ); |
| @@ -130,8 +141,9 @@ | ||
| 130 | 141 | |
| 131 | 142 | return [ |
| 132 | 143 | 'error' => $this->combine_errors( $errors, $component_error ), |
| 133 | 144 | 'warnings' => $warnings, |
| 145 | + 'warning_codes' => array_values( array_unique( $warning_codes ) ), | |
| 134 | 146 | ]; |
| 135 | 147 | } |
| 136 | 148 | |
| 137 | 149 | private function combine_errors( array $settings_errors, ?\WP_Error $component_error ): ?\WP_Error { |
| @@ -163,9 +175,10 @@ | ||
| 163 | 175 | array $schema, |
| 164 | 176 | string $element_type, |
| 165 | 177 | string $config_id, |
| 166 | 178 | array &$errors, |
| 167 | - array &$warnings | |
| 179 | + array &$warnings, | |
| 180 | + array &$warning_codes = [] | |
| 168 | 181 | ): array { |
| 169 | 182 | $alias_map = Prop_Canonicalizer::build_alias_map( $schema ); |
| 170 | 183 | $resolved = []; |
| 171 | 184 | $cleared = []; |
| @@ -179,8 +192,9 @@ | ||
| 179 | 192 | $config_id, |
| 180 | 193 | $name, |
| 181 | 194 | $element_type |
| 182 | 195 | ); |
| 196 | + $warning_codes[] = 'prop_not_supported'; | |
| 183 | 197 | continue; |
| 184 | 198 | } |
| 185 | 199 | |
| 186 | 200 | if ( null === $value ) { |
| @@ -206,8 +220,14 @@ | ||
| 206 | 220 | ); |
| 207 | 221 | continue; |
| 208 | 222 | } |
| 209 | 223 | |
| 224 | + $dropped_paths = Dropped_Plain_Keys_Finder::find( $value, $resolved_value, $canonical ); | |
| 225 | + | |
| 226 | + if ( ! empty( $dropped_paths ) ) { | |
| 227 | + $this->warn_plain_keys_dropped( $dropped_paths, $canonical, $element_type, $config_id, $warnings, $warning_codes ); | |
| 228 | + } | |
| 229 | + | |
| 210 | 230 | $resolved[ $canonical ] = $resolved_value; |
| 211 | 231 | } |
| 212 | 232 | |
| 213 | 233 | return [ |
| @@ -213,8 +233,59 @@ | ||
| 213 | 233 | return [ |
| 214 | 234 | 'resolved' => $resolved, |
| 215 | 235 | 'cleared' => $cleared, |
| 216 | 236 | ]; |
| 237 | + } | |
| 238 | + | |
| 239 | + private function warn_dropped_object_sub_keys( array $node_settings, array $resolved, string $element_type, string $config_id, array &$warnings, array &$warning_codes ): void { | |
| 240 | + foreach ( $resolved as $key => $value ) { | |
| 241 | + $stored = $node_settings[ $key ] ?? null; | |
| 242 | + | |
| 243 | + if ( | |
| 244 | + ! $this->is_object_prop_value( $stored ) | |
| 245 | + || ! $this->is_object_prop_value( $value ) | |
| 246 | + || $stored['$$type'] !== $value['$$type'] | |
| 247 | + ) { | |
| 248 | + continue; | |
| 249 | + } | |
| 250 | + | |
| 251 | + $stored_sub_values = array_filter( $stored['value'], fn( $sub_value ) => null !== $sub_value ); | |
| 252 | + $dropped_sub_keys = array_keys( array_diff_key( $stored_sub_values, $value['value'] ) ); | |
| 253 | + | |
| 254 | + if ( empty( $dropped_sub_keys ) ) { | |
| 255 | + continue; | |
| 256 | + } | |
| 257 | + | |
| 258 | + $warnings[] = sprintf( | |
| 259 | + '[%s] Property "%s" on "%s" was replaced as a whole, dropping its stored "%s". Send the full value (as returned by elementor/get-page-structure) to keep them. See elementor://widgets/schema/%s.', | |
| 260 | + $config_id, | |
| 261 | + $key, | |
| 262 | + $element_type, | |
| 263 | + implode( '", "', $dropped_sub_keys ), | |
| 264 | + $element_type | |
| 265 | + ); | |
| 266 | + $warning_codes[] = 'prop_subkeys_dropped'; | |
| 267 | + } | |
| 268 | + } | |
| 269 | + | |
| 270 | + private function warn_plain_keys_dropped( array $dropped_paths, string $key, string $element_type, string $config_id, array &$warnings, array &$warning_codes ): void { | |
| 271 | + $warnings[] = sprintf( | |
| 272 | + '[%s] Property "%s" on "%s" was saved without "%s": each is either not a field of the prop or has a value that could not be resolved. Use the shape from elementor://widgets/schema/%s.', | |
| 273 | + $config_id, | |
| 274 | + $key, | |
| 275 | + $element_type, | |
| 276 | + implode( '", "', $dropped_paths ), | |
| 277 | + $element_type | |
| 278 | + ); | |
| 279 | + $warning_codes[] = 'prop_keys_dropped'; | |
| 280 | + } | |
| 281 | + | |
| 282 | + private function is_object_prop_value( $value ): bool { | |
| 283 | + return is_array( $value ) | |
| 284 | + && isset( $value['$$type'] ) | |
| 285 | + && is_array( $value['value'] ?? null ) | |
| 286 | + && ! empty( $value['value'] ) | |
| 287 | + && ! wp_is_numeric_array( $value['value'] ); | |
| 217 | 288 | } |
| 218 | 289 | |
| 219 | 290 | private function merge_with_clears( array $existing, array $incoming ): array { |
| 220 | 291 | $merged = $existing; |