PluginProbe
Elementor Website Builder – more than just a page builder / 3.27.4
Elementor Website Builder – more than just a page builder v3.27.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 +59 -359 4.1.0-dev23.27.4 View file →
@@ -1,11 +1,12 @@
1 1 <?php
2 2 namespace Elementor\Modules\GlobalClasses;
3 3
4 4 use Elementor\Core\Kits\Documents\Kit;
5 -use Elementor\Modules\DesignSystemSync\Classes\Global_Classes_Sync_Map;
6 -use Elementor\Modules\GlobalClasses\Concerns\Has_Kit_Dependency;
7 -use Elementor\Modules\GlobalClasses\Concerns\Has_Preview_Context;
5 +use Elementor\Modules\AtomicWidgets\Parsers\Style_Parser;
6 +use Elementor\Modules\AtomicWidgets\Styles\Style_Schema;
7 +use Elementor\Modules\AtomicWidgets\Styles\Utils as Atomic_Styles_Utils;
8 +use Elementor\Plugin;
8 9
9 10 if ( ! defined( 'ABSPATH' ) ) {
10 11 exit; // Exit if accessed directly.
11 12 }
@@ -10,403 +11,102 @@
10 11 exit; // Exit if accessed directly.
11 12 }
12 13
13 14 class Global_Classes_Repository {
14 - use Has_Kit_Dependency;
15 - use Has_Preview_Context;
15 + const META_KEY = '_elementor_global_classes';
16 16
17 - const META_KEY_FRONTEND = '_elementor_global_classes';
18 - const META_KEY_PREVIEW = '_elementor_global_classes_preview';
19 -
20 - const CONTEXT_FRONTEND = 'frontend';
21 - const CONTEXT_PREVIEW = 'preview';
22 -
23 - const READ_BATCH_SIZE = 100;
24 - const PERSIST_BATCH_SIZE = 100;
25 -
26 - protected array $context_keys = [
27 - 'event' => [
28 - 'frontend' => self::CONTEXT_FRONTEND,
29 - 'preview' => self::CONTEXT_PREVIEW,
30 - ],
31 - 'meta_key' => [
32 - 'frontend' => self::META_KEY_FRONTEND,
33 - 'preview' => self::META_KEY_PREVIEW,
34 - ],
35 - ];
36 -
37 - private ?Global_Classes $cache = null;
38 -
39 - public function __construct( ?Kit $kit = null ) {
40 - if ( null !== $kit ) {
41 - $this->set_kit( $kit );
42 - }
17 + public static function make(): Global_Classes_Repository {
18 + return new self();
43 19 }
44 20
45 - public static function make( ?Kit $kit = null ): Global_Classes_Repository {
46 - return new self( $kit );
47 - }
21 + public function all() {
22 + $all = Plugin::$instance->kits_manager->get_active_kit()->get_json_meta( self::META_KEY );
48 23
49 - protected function on_preview_change(): void {
50 - $this->cache = null;
24 + return Global_Classes::make( $all['items'] ?? [], $all['order'] ?? [] );
51 25 }
52 26
53 - /**
54 - * This method may be too heavy to use
55 - * Be mindful as this call would cause the server to freeze for as much time as needed until it fetches
56 - * all global classes
57 - */
58 - public function all( bool $force = false ): Global_Classes {
59 - if ( ! $force && null !== $this->cache ) {
60 - return $this->cache;
61 - }
62 -
63 - $this->cache = $this->all_from_posts();
64 -
65 - return $this->cache;
27 + public function get( string $id ) {
28 + return $this->all()->get_items()->get( $id );
66 29 }
67 30
68 - public function all_labels(): array {
69 - return $this->labels()->get_ordered_labels();
70 - }
31 + public function delete( string $id ) {
32 + $all = $this->all();
71 33
72 - public function get_order(): array {
73 - return Global_Classes_Order::make( $this->get_kit() )->get_order();
74 - }
75 -
76 - public function update_order_and_labels( array $order, array $new_labels ): void {
77 - Global_Classes_Order::make( $this->get_kit() )->set_order( $order );
78 -
79 - $labels = $this->labels();
80 - $existing_labels = $labels->get_labels();
81 -
82 - foreach ( $new_labels as $id => $label ) {
83 - $existing_labels[ $id ] = $label;
34 + if ( ! isset( $all->get_items()[ $id ] ) ) {
35 + throw new \Exception( "Global class with id ${id} not found" );
84 36 }
85 37
86 - $labels->set_labels( $existing_labels );
87 -
88 - $this->cache = null;
38 + Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( self::META_KEY, [
39 + 'items' => $all->get_items()->except( [ $id ] )->all(),
40 + 'order' => $all->get_order()->filter( fn( $item ) => $item !== $id )->all(),
41 + ] );
89 42 }
90 43
91 - private function labels(): Global_Classes_Labels {
92 - return Global_Classes_Labels::make( $this->get_kit() )->set_preview( $this->is_preview() );
93 - }
44 + public function put( string $id, array $value ) {
45 + $all = $this->all();
94 46
95 - public function get( string $class_id ): ?array {
96 - $post = Global_Class_Post::find_by_class_id( $class_id, $this->is_preview() );
47 + unset( $value['id'] );
97 48
98 - return $post ? $post->to_array() : null;
99 - }
100 -
101 - public function get_by_ids( array $class_ids ): array {
102 - if ( empty( $class_ids ) ) {
103 - return [];
49 + if ( ! isset( $all->get_items()[ $id ] ) ) {
50 + throw new \Exception( "Global class with id {$id} not found" );
104 51 }
105 52
106 - $items = [];
107 -
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;
53 + if ( $value === $all->get_items()[ $id ] ) {
54 + return $value;
111 55 }
112 56
113 - return $items;
114 - }
115 -
116 - public function apply_changes( array $touched_items, array $changes, array $order ): void {
117 - $labels = $this->labels();
118 - $before = $labels->get_labels();
119 - $is_preview = $this->is_preview();
120 - $to_delete = $changes['deleted'] ?? [];
121 - $to_create = $changes['added'] ?? [];
122 - $to_update = $changes['modified'] ?? [];
123 - $order_changed = isset( $changes['order'] ) && $changes['order'];
124 -
125 - $final_label_map = [];
126 - foreach ( $order as $id ) {
127 - if ( isset( $touched_items[ $id ] ) ) {
128 - $final_label_map[ $id ] = $touched_items[ $id ]['label'];
129 - } elseif ( isset( $before[ $id ] ) ) {
130 - $final_label_map[ $id ] = $before[ $id ];
131 - }
132 - }
133 -
134 - $this->persist_class_batch_mutations( $to_delete, $to_create, $to_update, $touched_items, $is_preview );
135 -
136 - $classes_order = Global_Classes_Order::make( $this->get_kit() );
137 - $classes_order->set_order( $order );
138 - $labels->set_labels( $final_label_map );
139 -
140 - if ( ! $is_preview ) {
141 - Global_Classes_Sync_Map::make( $this->get_kit() )->apply_changes( $touched_items, $to_delete );
142 -
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 - } );
150 - }
151 -
152 - $this->cache = null;
153 - $this->flush_runtime_cache();
154 -
155 - do_action( 'elementor/global_classes/update', $this->get_context_key( 'event' ), [
156 - 'added' => $to_create,
157 - 'deleted' => $to_delete,
158 - 'modified' => $to_update,
159 - 'order' => $order_changed,
57 + $value = Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( self::META_KEY, [
58 + 'items' => $all->get_items()->merge( [ $id => $value ] )->all(),
59 + 'order' => $all->get_order()->all(),
160 60 ] );
161 - }
162 61
163 - public function each_item( callable $cb, bool $skip_migration = false, int $batch_size = self::READ_BATCH_SIZE ): void {
164 - $order = Global_Classes_Order::make( $this->get_kit() )->get_order();
165 -
166 - if ( empty( $order ) ) {
167 - return;
62 + if ( ! $value ) {
63 + throw new \Exception( 'Failed to update global class' );
168 64 }
169 65
170 - foreach ( array_chunk( $order, $batch_size ) as $chunk ) {
171 - foreach ( $this->iterate_class_posts_for_ids( $chunk ) as $class_post ) {
172 - $cb( $class_post->to_array( $skip_migration ) );
173 - }
174 - }
66 + return $this->get( $id );
175 67 }
176 68
177 - public function put( array $items, array $order ) {
178 - $current_ids = Global_Classes_Order::make( $this->get_kit() )->get_order();
69 + public function create( array $value ) {
70 + $all = $this->all();
71 + $id = $this->generate_global_class_id();
179 72
180 - $new_ids = array_keys( $items );
73 + $value['id'] = $id;
181 74
182 - $current_order_string = implode( ';', $current_ids );
75 + $updated = Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( self::META_KEY, [
76 + 'items' => $all->get_items()->merge( [ $id => $value ] )->all(),
77 + 'order' => $all->get_order()->push( $id )->all(),
78 + ] );
183 79
184 - $changes = [
185 - 'added' => array_values( array_diff( $new_ids, $current_ids ) ),
186 - 'deleted' => array_values( array_diff( $current_ids, $new_ids ) ),
187 - 'modified' => array_values( array_intersect( $new_ids, $current_ids ) ),
188 - 'order' => implode( ';', $order ) !== $current_order_string,
189 - ];
190 -
191 - $this->put_to_posts( $items, $order, $current_ids );
192 -
193 - $this->cache = null;
194 -
195 - do_action( 'elementor/global_classes/update', $this->get_context_key( 'event' ), $changes );
196 - }
197 -
198 - private function all_from_posts(): Global_Classes {
199 - $classes_order = Global_Classes_Order::make( $this->get_kit() );
200 - $order = $classes_order->get_order();
201 -
202 - if ( empty( $order ) ) {
203 - return Global_Classes::make( [], [] );
80 + if ( ! $updated ) {
81 + throw new \Exception( 'Failed to create global class' );
204 82 }
205 83
206 - $items = [];
207 -
208 - foreach ( $this->iterate_class_posts_for_ids( $order ) as $class_post ) {
209 - $class_data = $class_post->to_array();
210 - $items[ $class_data['id'] ] = $class_data;
211 - }
212 -
213 - $order = Global_Classes_Parser::sanitize_order( $items, $order );
214 -
215 - return Global_Classes::make( $items, $order );
84 + return $this->get( $id );
216 85 }
217 86
218 - private function put_to_posts( array $items, array $order, array $current_ids ): void {
219 - $is_preview = $this->is_preview();
220 - $new_ids = array_keys( $items );
87 + public function arrange( array $value ) {
88 + $all = $this->all();
221 89
222 - $to_delete = array_diff( $current_ids, $new_ids );
223 - $to_create = array_diff( $new_ids, $current_ids );
224 - $to_update = array_intersect( $new_ids, $current_ids );
225 -
226 - $this->persist_class_batch_mutations( $to_delete, $to_create, $to_update, $items, $is_preview );
227 -
228 - $classes_order = Global_Classes_Order::make( $this->get_kit() );
229 - $classes_order->set_order( $order );
230 -
231 - $label_map = [];
232 - foreach ( $order as $id ) {
233 - if ( isset( $items[ $id ]['label'] ) ) {
234 - $label_map[ $id ] = $items[ $id ]['label'];
235 - }
90 + if ( $all->get_order()->all() === $value ) {
91 + return $value;
236 92 }
237 - $this->labels()->set_labels( $label_map );
238 93
239 - if ( ! $is_preview ) {
240 - $touched_ids = array_merge( array_values( $to_create ), array_values( $to_update ) );
241 - $touched_items = array_intersect_key(
242 - $items,
243 - array_flip( $touched_ids )
244 - );
245 - Global_Classes_Sync_Map::make( $this->get_kit() )->apply_changes( $touched_items, array_values( $to_delete ) );
94 + $updated = Plugin::$instance->kits_manager->get_active_kit()->update_json_meta( self::META_KEY, [
95 + 'items' => $all->get_items()->all(),
96 + 'order' => $value,
97 + ] );
246 98
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 - } );
99 + if ( ! $updated ) {
100 + throw new \Exception( 'Failed to arrange global classes' );
254 101 }
255 - }
256 102
257 - private function iterate_class_posts_for_ids( array $class_ids ): \Generator {
258 - foreach ( array_chunk( $class_ids, self::READ_BATCH_SIZE ) as $chunk ) {
259 - $posts = get_posts( [
260 - 'post_type' => Global_Class_Post_Type::CPT,
261 - 'post_status' => 'publish',
262 - 'posts_per_page' => -1,
263 - 'meta_query' => [
264 - [
265 - 'key' => Global_Class_Post::META_KEY_ID,
266 - 'value' => $chunk,
267 - 'compare' => 'IN',
268 - ],
269 - ],
270 - ] );
271 -
272 - $seen_class_ids = [];
273 -
274 - 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 - yield Global_Class_Post::from_post( $post, $this->is_preview() );
289 - clean_post_cache( $post->ID );
290 - }
291 - unset( $posts, $seen_class_ids );
292 - $this->flush_runtime_cache();
293 - }
103 + return $this->all()->get_order()->all();
294 104 }
295 105
296 - private function persist_class_batch_mutations(
297 - array $to_delete,
298 - array $to_create,
299 - array $to_update,
300 - array $items_by_id,
301 - bool $is_preview
302 - ): void {
303 - $relations = new Global_Classes_Relations();
106 + private function generate_global_class_id() {
107 + $existing_ids = $this->all()->get_items()->keys();
108 + $kit_id = Plugin::$instance->kits_manager->get_active_kit()->get_id();
304 109
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 );
307 -
308 - if ( $post ) {
309 - if ( $is_preview ) {
310 - $post->set_preview( true );
311 - $post->update_data( [] );
312 - clean_post_cache( $post->get_post_id() );
313 - } else {
314 - $relations->clear_class_relations( $class_id );
315 - $post->delete();
316 - }
317 - }
318 - } );
319 -
320 - $this->each_class_id_batch( $to_create, function ( string $class_id ) use ( $items_by_id, $is_preview ) {
321 - if ( ! isset( $items_by_id[ $class_id ] ) ) {
322 - return;
323 - }
324 -
325 - $item = $items_by_id[ $class_id ];
326 - $data = $this->build_class_data_for_storage( $item );
327 -
328 - if ( $is_preview ) {
329 - $post = Global_Class_Post::find_by_class_id( $class_id, true );
330 -
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 - }
341 - }
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 -
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 ] ) ) {
352 - return;
353 - }
354 -
355 - $item = $items_by_id[ $class_id ];
356 - $post = Global_Class_Post::find_by_class_id( $class_id, $is_preview );
357 -
358 - if ( ! $post ) {
359 - return;
360 - }
361 -
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 - } );
367 - }
368 -
369 - public function delete_all(): void {
370 - $order = $this->get_order();
371 -
372 - $this->each_class_id_batch( $order, function ( string $class_id ) {
373 - $post = Global_Class_Post::find_by_class_id( $class_id );
374 -
375 - if ( $post ) {
376 - $post->delete();
377 - }
378 - } );
379 -
380 - Global_Classes_Order::make( $this->get_kit() )->set_order( [] );
381 - $this->labels()->set_labels( [] );
382 - }
383 -
384 - private function each_class_id_batch( $class_ids, callable $callback, int $batch_size = self::PERSIST_BATCH_SIZE ): void {
385 - $class_ids = is_array( $class_ids ) ? $class_ids : iterator_to_array( $class_ids, false );
386 - foreach ( array_chunk( array_values( $class_ids ), $batch_size ) as $batch ) {
387 - foreach ( $batch as $class_id ) {
388 - $callback( $class_id );
389 - }
390 - $this->flush_runtime_cache();
391 - }
392 - }
393 -
394 - private function flush_runtime_cache(): void {
395 - if ( function_exists( 'wp_cache_flush_runtime' ) ) {
396 - wp_cache_flush_runtime();
397 - }
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;
110 + return Atomic_Styles_Utils::generate_id( 'g-' . $kit_id . '-', $existing_ids );
411 111 }
412 112 }