PluginProbe
Elementor Website Builder – more than just a page builder / trunk
Elementor Website Builder – more than just a page builder vtrunk
4.3.3 4.3.2 4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 All 456 releases
← All changes | modules/mcp/abilities/appliers/element-config-applier.php +73 -2 4.3.0-beta1 → 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;