PluginProbe
Elementor Website Builder – more than just a page builder / 4.2.4
Elementor Website Builder – more than just a page builder v4.2.4
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 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 All 452 releases
← All changes | modules/global-classes/global-classes-repository.php +197 -97 4.1.0-beta14.2.4 View file →
@@ -4,8 +4,9 @@
4 4 use Elementor\Core\Kits\Documents\Kit;
5 5 use Elementor\Modules\DesignSystemSync\Classes\Global_Classes_Sync_Map;
6 6 use Elementor\Modules\GlobalClasses\Concerns\Has_Kit_Dependency;
7 7 use Elementor\Modules\GlobalClasses\Concerns\Has_Preview_Context;
8 +use Elementor\Modules\GlobalClasses\Utils\Global_Class_Data_Normalizer;
8 9
9 10 if ( ! defined( 'ABSPATH' ) ) {
10 11 exit; // Exit if accessed directly.
11 12 }
@@ -27,12 +28,8 @@
27 28 'event' => [
28 29 'frontend' => self::CONTEXT_FRONTEND,
29 30 'preview' => self::CONTEXT_PREVIEW,
30 31 ],
31 - 'meta_key' => [
32 - 'frontend' => self::META_KEY_FRONTEND,
33 - 'preview' => self::META_KEY_PREVIEW,
34 - ],
35 32 ];
36 33
37 34 private ?Global_Classes $cache = null;
38 35
@@ -69,13 +66,15 @@
69 66 return $this->labels()->get_ordered_labels();
70 67 }
71 68
72 69 public function get_order(): array {
73 - return Global_Classes_Order::make( $this->get_kit() )->get_order();
70 + return Global_Classes_Order::make( $this->get_kit() )->set_preview( $this->is_preview() )->get_order();
74 71 }
75 72
76 73 public function update_order_and_labels( array $order, array $new_labels ): void {
77 - Global_Classes_Order::make( $this->get_kit() )->set_order( $order );
74 + Global_Classes_Order::make( $this->get_kit() )
75 + ->set_preview( $this->is_preview() )
76 + ->set_order( $order );
78 77
79 78 $labels = $this->labels();
80 79 $existing_labels = $labels->get_labels();
81 80
@@ -84,8 +83,16 @@
84 83 }
85 84
86 85 $labels->set_labels( $existing_labels );
87 86
87 + if ( ! $this->is_preview() ) {
88 + Global_Classes_Order::make( $this->get_kit() )
89 + ->set_preview( true )
90 + ->set_order( $order );
91 +
92 + $this->clear_preview_labels_for_ids( array_keys( $new_labels ) );
93 + }
94 +
88 95 $this->cache = null;
89 96 }
90 97
91 98 private function labels(): Global_Classes_Labels {
@@ -92,9 +99,9 @@
92 99 return Global_Classes_Labels::make( $this->get_kit() )->set_preview( $this->is_preview() );
93 100 }
94 101
95 102 public function get( string $class_id ): ?array {
96 - $post = Global_Class_Post::find_by_class_id( $class_id, $this->is_preview() );
103 + $post = Global_Class_Post::find_by_class_id( $class_id, $this->is_preview(), $this->get_kit() );
97 104
98 105 return $post ? $post->to_array() : null;
99 106 }
100 107
@@ -102,13 +109,17 @@
102 109 if ( empty( $class_ids ) ) {
103 110 return [];
104 111 }
105 112
113 + $post_ids = Global_Classes_Post_IDs::make( $this->get_kit() )->get_post_ids( $class_ids );
106 114 $items = [];
107 115
108 - foreach ( $this->iterate_class_posts_for_ids( $class_ids ) as $class_post ) {
109 - $class_data = $class_post->to_array();
110 - $items[ $class_data['id'] ] = $class_data;
116 + foreach ( $post_ids as $class_id => $post_id ) {
117 + $post = Global_Class_Post::from_post_id( $post_id, $this->is_preview() );
118 +
119 + if ( $post ) {
120 + $items[ $class_id ] = $post->to_array();
121 + }
111 122 }
112 123
113 124 return $items;
114 125 }
@@ -130,24 +141,28 @@
130 141 $final_label_map[ $id ] = $before[ $id ];
131 142 }
132 143 }
133 144
145 + $affected_post_ids = $this->get_posts_affected_by_deletion( $to_delete );
146 +
134 147 $this->persist_class_batch_mutations( $to_delete, $to_create, $to_update, $touched_items, $is_preview );
135 148
136 - $classes_order = Global_Classes_Order::make( $this->get_kit() );
149 + $classes_order = Global_Classes_Order::make( $this->get_kit() )->set_preview( $this->is_preview() );
137 150 $classes_order->set_order( $order );
138 151 $labels->set_labels( $final_label_map );
139 152
140 153 if ( ! $is_preview ) {
141 154 Global_Classes_Sync_Map::make( $this->get_kit() )->apply_changes( $touched_items, $to_delete );
155 + Global_Classes_Order::make( $this->get_kit() )
156 + ->set_preview( true )
157 + ->set_order( $order );
142 158
143 - $this->each_class_id_batch( array_values( $to_update ), function ( string $class_id ) {
144 - $post = Global_Class_Post::find_by_class_id( $class_id, false );
145 - if ( $post ) {
146 - delete_post_meta( $post->get_post_id(), Global_Class_Post::META_KEY_DATA_PREVIEW );
147 - clean_post_cache( $post->get_post_id() );
148 - }
149 - } );
159 + $this->bulk_clear_preview_meta( array_values( $to_update ) );
160 + $this->clear_preview_labels_for_ids( array_merge(
161 + array_values( $to_create ),
162 + array_values( $to_update ),
163 + array_values( $to_delete )
164 + ) );
150 165 }
151 166
152 167 $this->cache = null;
153 168 $this->flush_runtime_cache();
@@ -156,9 +171,18 @@
156 171 'added' => $to_create,
157 172 'deleted' => $to_delete,
158 173 'modified' => $to_update,
159 174 'order' => $order_changed,
175 + 'affected_post_ids' => $affected_post_ids,
160 176 ] );
177 +
178 + if ( ! empty( $to_delete ) && ! $is_preview ) {
179 + do_action(
180 + 'elementor/global_classes/cleanup',
181 + $to_delete,
182 + $affected_post_ids
183 + );
184 + }
161 185 }
162 186
163 187 public function each_item( callable $cb, bool $skip_migration = false, int $batch_size = self::READ_BATCH_SIZE ): void {
164 188 $order = Global_Classes_Order::make( $this->get_kit() )->get_order();
@@ -174,31 +198,69 @@
174 198 }
175 199 }
176 200
177 201 public function put( array $items, array $order ) {
178 - $current_ids = Global_Classes_Order::make( $this->get_kit() )->get_order();
202 + $current_ids = Global_Classes_Order::make( $this->get_kit() )
203 + ->set_preview( $this->is_preview() )
204 + ->get_order();
179 205
180 206 $new_ids = array_keys( $items );
181 -
182 207 $current_order_string = implode( ';', $current_ids );
208 + $deleted_class_ids = array_values( array_diff( $current_ids, $new_ids ) );
183 209
184 210 $changes = [
185 211 'added' => array_values( array_diff( $new_ids, $current_ids ) ),
186 - 'deleted' => array_values( array_diff( $current_ids, $new_ids ) ),
212 + 'deleted' => $deleted_class_ids,
187 213 'modified' => array_values( array_intersect( $new_ids, $current_ids ) ),
188 214 'order' => implode( ';', $order ) !== $current_order_string,
189 215 ];
190 216
217 + /**
218 + * We collect all affected ids before the put_to_posts execution
219 + * as the update mechanism would handle the Global_Classes_Relations as it iterates over the update batches
220 + * So once we get to the cleanup phase - we would no longer have the relevant relations
221 + *
222 + * On top of that - by collecting all affected posts in advance, means we would iterate over each document's elements only once
223 + * (as the alternative would be to trigger the cleanup per removed class, but that means we may end up iterating over the same document N times, if all N styles are used in it)
224 + */
225 + $affected_post_ids = $this->get_posts_affected_by_deletion( $deleted_class_ids );
226 +
191 227 $this->put_to_posts( $items, $order, $current_ids );
192 228
193 229 $this->cache = null;
194 230
231 + $changes['affected_post_ids'] = $affected_post_ids;
232 +
195 233 do_action( 'elementor/global_classes/update', $this->get_context_key( 'event' ), $changes );
234 +
235 + if ( ! empty( $deleted_class_ids ) && ! $this->is_preview() ) {
236 + do_action(
237 + 'elementor/global_classes/cleanup',
238 + $deleted_class_ids,
239 + $affected_post_ids
240 + );
241 + }
196 242 }
197 243
244 + private function get_posts_affected_by_deletion( array $deleted_class_ids ): array {
245 + if ( empty( $deleted_class_ids ) ) {
246 + return [];
247 + }
248 +
249 + $relations = new Global_Classes_Relations();
250 + $post_ids = [];
251 +
252 + foreach ( $deleted_class_ids as $class_id ) {
253 + $post_ids[] = $relations->get_posts_by_style( $class_id );
254 + }
255 +
256 + return array_values( array_unique( array_merge( ...$post_ids ) ) );
257 + }
258 +
198 259 private function all_from_posts(): Global_Classes {
199 - $classes_order = Global_Classes_Order::make( $this->get_kit() );
200 - $order = $classes_order->get_order();
260 + $order = Global_Classes_Order::make( $this->get_kit() )
261 + ->set_preview( $this->is_preview() )
262 + ->get_order();
201 263
202 264 if ( empty( $order ) ) {
203 265 return Global_Classes::make( [], [] );
204 266 }
@@ -224,9 +286,9 @@
224 286 $to_update = array_intersect( $new_ids, $current_ids );
225 287
226 288 $this->persist_class_batch_mutations( $to_delete, $to_create, $to_update, $items, $is_preview );
227 289
228 - $classes_order = Global_Classes_Order::make( $this->get_kit() );
290 + $classes_order = Global_Classes_Order::make( $this->get_kit() )->set_preview( $this->is_preview() );
229 291 $classes_order->set_order( $order );
230 292
231 293 $label_map = [];
232 294 foreach ( $order as $id ) {
@@ -243,15 +305,14 @@
243 305 array_flip( $touched_ids )
244 306 );
245 307 Global_Classes_Sync_Map::make( $this->get_kit() )->apply_changes( $touched_items, array_values( $to_delete ) );
246 308
247 - $this->each_class_id_batch( array_values( $to_update ), function ( string $class_id ) {
248 - $post = Global_Class_Post::find_by_class_id( $class_id, false );
249 - if ( $post ) {
250 - delete_post_meta( $post->get_post_id(), Global_Class_Post::META_KEY_DATA_PREVIEW );
251 - clean_post_cache( $post->get_post_id() );
252 - }
253 - } );
309 + $this->bulk_clear_preview_meta( array_values( $to_update ) );
310 + $this->clear_preview_labels_for_ids( array_merge(
311 + array_values( $to_create ),
312 + array_values( $to_update ),
313 + array_values( $to_delete )
314 + ) );
254 315 }
255 316 }
256 317
257 318 private function iterate_class_posts_for_ids( array $class_ids ): \Generator {
@@ -268,28 +329,13 @@
268 329 ],
269 330 ],
270 331 ] );
271 332
272 - $seen_class_ids = [];
273 -
274 333 foreach ( $posts as $post ) {
275 - $class_id = get_post_meta( $post->ID, Global_Class_Post::META_KEY_ID, true );
276 -
277 - if ( $class_id && isset( $seen_class_ids[ $class_id ] ) ) {
278 - // if for some reason there's already a post meta to that class - delete any other ones
279 - // otherwise we may end up updating a stale post meta
280 - wp_delete_post( $post->ID, true );
281 - continue;
282 - }
283 -
284 - if ( $class_id ) {
285 - $seen_class_ids[ $class_id ] = true;
286 - }
287 -
288 334 yield Global_Class_Post::from_post( $post, $this->is_preview() );
289 335 clean_post_cache( $post->ID );
290 336 }
291 - unset( $posts, $seen_class_ids );
337 + unset( $posts );
292 338 $this->flush_runtime_cache();
293 339 }
294 340 }
295 341
@@ -300,13 +346,28 @@
300 346 array $items_by_id,
301 347 bool $is_preview
302 348 ): void {
303 349 $relations = new Global_Classes_Relations();
350 + $post_ids_map = Global_Classes_Post_IDs::make( $this->get_kit() );
304 351
305 - $this->each_class_id_batch( array_values( $to_delete ), function ( string $class_id ) use ( $is_preview, $relations ) {
306 - $post = Global_Class_Post::find_by_class_id( $class_id, false );
352 + $ids_to_resolve = array_values( array_merge(
353 + array_values( $to_delete ),
354 + array_values( $to_update ),
355 + array_values( $to_create )
356 + ) );
357 + $post_ids = $post_ids_map->get_post_ids( $ids_to_resolve );
307 358
308 - if ( $post ) {
359 + $this->each_class_id_batch(
360 + array_values( $to_delete ),
361 + function ( string $class_id ) use ( $is_preview, $relations, $post_ids ) {
362 + $post = isset( $post_ids[ $class_id ] )
363 + ? Global_Class_Post::from_post_id( $post_ids[ $class_id ], false )
364 + : null;
365 +
366 + if ( ! $post ) {
367 + return;
368 + }
369 +
309 370 if ( $is_preview ) {
310 371 $post->set_preview( true );
311 372 $post->update_data( [] );
312 373 clean_post_cache( $post->get_post_id() );
@@ -314,57 +375,65 @@
314 375 $relations->clear_class_relations( $class_id );
315 376 $post->delete();
316 377 }
317 378 }
318 - } );
379 + );
319 380
320 - $this->each_class_id_batch( $to_create, function ( string $class_id ) use ( $items_by_id, $is_preview ) {
381 + $this->each_class_id_batch( $to_create, function ( string $class_id ) use ( $items_by_id, $is_preview, $post_ids, $post_ids_map ) {
321 382 if ( ! isset( $items_by_id[ $class_id ] ) ) {
322 383 return;
323 384 }
324 385
325 386 $item = $items_by_id[ $class_id ];
326 - $data = $this->build_class_data_for_storage( $item );
387 + $data = Global_Class_Data_Normalizer::normalize_style_fields( $item );
388 + $kit = $this->get_kit();
389 + $existing_post_id = $post_ids[ $class_id ] ?? null;
390 + $existing_post = $existing_post_id ? Global_Class_Post::from_post_id( $existing_post_id, $is_preview ) : null;
327 391
328 - if ( $is_preview ) {
329 - $post = Global_Class_Post::find_by_class_id( $class_id, true );
392 + if ( $existing_post ) {
393 + $existing_post->update_data( $data );
330 394
331 - if ( $post ) {
332 - $post->set_preview( true );
333 - $post->update_data( $data );
334 - $post->update_label( $item['label'] );
335 - clean_post_cache( $post->get_post_id() );
336 - } else {
337 - $created = Global_Class_Post::create( $class_id, $item['label'], $data );
338 - if ( $created ) {
339 - clean_post_cache( $created->get_post_id() );
340 - }
395 + if ( ! $is_preview ) {
396 + $existing_post->update_label( $item['label'] );
341 397 }
342 - } else {
343 - $created = Global_Class_Post::create( $class_id, $item['label'], $data );
344 - if ( $created ) {
345 - clean_post_cache( $created->get_post_id() );
346 - }
347 - }
348 - } );
349 398
350 - $this->each_class_id_batch( $to_update, function ( string $class_id ) use ( $items_by_id, $is_preview ) {
351 - if ( ! isset( $items_by_id[ $class_id ] ) ) {
399 + clean_post_cache( $existing_post->get_post_id() );
400 +
352 401 return;
353 402 }
354 403
355 - $item = $items_by_id[ $class_id ];
356 - $post = Global_Class_Post::find_by_class_id( $class_id, $is_preview );
404 + $created = Global_Class_Post::create( $class_id, $item['label'], $data, $kit );
357 405
358 - if ( ! $post ) {
359 - return;
406 + if ( $created ) {
407 + $post_ids_map->set( $class_id, $created->get_post_id() );
408 + clean_post_cache( $created->get_post_id() );
360 409 }
410 + } );
361 411
362 - $data = $this->build_class_data_for_storage( $item );
363 - $post->update_data( $data );
364 - $post->update_label( $item['label'] );
365 - clean_post_cache( $post->get_post_id() );
366 - } );
412 + $this->each_class_id_batch(
413 + $to_update,
414 + function ( string $class_id ) use ( $items_by_id, $is_preview, $post_ids ) {
415 + if ( ! isset( $items_by_id[ $class_id ] ) || ! isset( $post_ids[ $class_id ] ) ) {
416 + return;
417 + }
418 +
419 + $item = $items_by_id[ $class_id ];
420 + $post = Global_Class_Post::from_post_id( $post_ids[ $class_id ], $is_preview );
421 +
422 + if ( ! $post ) {
423 + return;
424 + }
425 +
426 + $data = Global_Class_Data_Normalizer::normalize_style_fields( $item );
427 + $post->update_data( $data );
428 +
429 + if ( ! $is_preview ) {
430 + $post->update_label( $item['label'] );
431 + }
432 +
433 + clean_post_cache( $post->get_post_id() );
434 + }
435 + );
367 436 }
368 437
369 438 public function delete_all(): void {
370 439 $order = $this->get_order();
@@ -369,9 +438,9 @@
369 438 public function delete_all(): void {
370 439 $order = $this->get_order();
371 440
372 441 $this->each_class_id_batch( $order, function ( string $class_id ) {
373 - $post = Global_Class_Post::find_by_class_id( $class_id );
442 + $post = Global_Class_Post::find_by_class_id( $class_id, false, $this->get_kit() );
374 443
375 444 if ( $post ) {
376 445 $post->delete();
377 446 }
@@ -380,8 +449,52 @@
380 449 Global_Classes_Order::make( $this->get_kit() )->set_order( [] );
381 450 $this->labels()->set_labels( [] );
382 451 }
383 452
453 + private function clear_preview_labels_for_ids( array $class_ids ): void {
454 + if ( empty( $class_ids ) ) {
455 + return;
456 + }
457 +
458 + $preview_labels = Global_Classes_Labels::make( $this->get_kit() )->set_preview( true );
459 + $labels_map = $preview_labels->get_labels();
460 +
461 + if ( empty( $labels_map ) ) {
462 + return;
463 + }
464 +
465 + $ids_to_remove = array_intersect( array_unique( $class_ids ), array_keys( $labels_map ) );
466 +
467 + if ( empty( $ids_to_remove ) ) {
468 + return;
469 + }
470 +
471 + foreach ( $ids_to_remove as $id ) {
472 + unset( $labels_map[ $id ] );
473 + }
474 +
475 + $preview_labels->set_labels( $labels_map );
476 + }
477 +
478 + private function bulk_clear_preview_meta( array $class_ids ): void {
479 + if ( empty( $class_ids ) ) {
480 + return;
481 + }
482 +
483 + $post_ids_map = Global_Classes_Post_IDs::make( $this->get_kit() );
484 +
485 + foreach ( array_chunk( $class_ids, self::PERSIST_BATCH_SIZE ) as $chunk ) {
486 + $post_ids = $post_ids_map->get_post_ids( $chunk );
487 +
488 + foreach ( $post_ids as $post_id ) {
489 + delete_post_meta( $post_id, Global_Class_Post::META_KEY_DATA_PREVIEW );
490 + clean_post_cache( $post_id );
491 + }
492 +
493 + $this->flush_runtime_cache();
494 + }
495 + }
496 +
384 497 private function each_class_id_batch( $class_ids, callable $callback, int $batch_size = self::PERSIST_BATCH_SIZE ): void {
385 498 $class_ids = is_array( $class_ids ) ? $class_ids : iterator_to_array( $class_ids, false );
386 499 foreach ( array_chunk( array_values( $class_ids ), $batch_size ) as $batch ) {
387 500 foreach ( $batch as $class_id ) {
@@ -394,19 +507,6 @@
394 507 private function flush_runtime_cache(): void {
395 508 if ( function_exists( 'wp_cache_flush_runtime' ) ) {
396 509 wp_cache_flush_runtime();
397 510 }
398 - }
399 -
400 - private function build_class_data_for_storage( array $item ): array {
401 - $data = [
402 - 'type' => $item['type'] ?? 'class',
403 - 'variants' => $item['variants'] ?? [],
404 - ];
405 -
406 - if ( array_key_exists( 'sync_to_v3', $item ) ) {
407 - $data['sync_to_v3'] = (bool) $item['sync_to_v3'];
408 - }
409 -
410 - return $data;
411 511 }
412 512 }