PluginProbe
Elementor Website Builder – more than just a page builder / 4.2.0
Elementor Website Builder – more than just a page builder v4.2.0
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 4.0.7 All 451 releases
elementor / core / kits / manager.php

manager.php in Elementor Website Builder – more than just a page builder 4.2.0, at core/kits/manager.php

505 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\Core\Kits;
3
4 use Elementor\Core\Base\Document;
5 use Elementor\Core\Kits\Controls\Repeater;
6 use Elementor\Core\Kits\Documents\Tabs\Global_Colors;
7 use Elementor\Core\Kits\Documents\Tabs\Global_Typography;
8 use Elementor\Plugin;
9 use Elementor\Core\Files\CSS\Post as Post_CSS;
10 use Elementor\Core\Files\CSS\Post_Preview;
11 use Elementor\Core\Documents_Manager;
12 use Elementor\Core\Kits\Documents\Kit;
13 use Elementor\TemplateLibrary\Source_Local;
14 use Elementor\Utils;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit; // Exit if accessed directly.
18 }
19
20 class Manager {
21
22 const OPTION_ACTIVE = 'elementor_active_kit';
23
24 const OPTION_PREVIOUS = 'elementor_previous_kit';
25
26 const E_HASH_COMMAND_OPEN_SITE_SETTINGS = 'e:run:panel/global/open';
27
28 private $should_skip_trash_kit_confirmation = false;
29
30 public function get_active_id() {
31 return get_option( self::OPTION_ACTIVE );
32 }
33
34 public function get_previous_id() {
35 return get_option( self::OPTION_PREVIOUS );
36 }
37
38 public function get_kit( $kit_id ) {
39 $kit = Plugin::$instance->documents->get( $kit_id );
40
41 if ( ! $this->is_valid_kit( $kit ) ) {
42 return $this->get_empty_kit_instance();
43 }
44
45 return $kit;
46 }
47
48 public function get_active_kit() {
49 return $this->get_kit( $this->get_active_id() );
50 }
51
52 public function get_active_kit_for_frontend() {
53 $kit = Plugin::$instance->documents->get_doc_for_frontend( $this->get_active_id() );
54
55 if ( ! $this->is_valid_kit( $kit ) ) {
56 return $this->get_empty_kit_instance();
57 }
58
59 return $kit;
60 }
61
62 /**
63 * @param $kit
64 *
65 * @return bool
66 */
67 private function is_valid_kit( $kit ) {
68 return $kit && $kit instanceof Kit && 'trash' !== $kit->get_main_post()->post_status;
69 }
70
71 /**
72 * Returns an empty kit for situation when there is no kit in the site.
73 *
74 * @return Kit
75 * @throws \Exception If the kit instance cannot be created.
76 */
77 private function get_empty_kit_instance() {
78 return new Kit( [
79 'settings' => [],
80 'post_id' => 0,
81 ] );
82 }
83
84 /**
85 * Checks if specific post is a kit.
86 *
87 * @param $post_id
88 *
89 * @return bool
90 */
91 public function is_kit( $post_id ) {
92 $document = Plugin::$instance->documents->get( $post_id );
93
94 return $document && $document instanceof Kit && ! $document->is_revision();
95 }
96
97
98 /**
99 * Init kit controls.
100 *
101 * A temp solution in order to avoid init kit group control from within another group control.
102 *
103 * After moving the `default_font` to the kit, the Typography group control cause initialize the kit controls at: https://github.com/elementor/elementor/blob/e6e1db9eddef7e3c1a5b2ba0c2338e2af2a3bfe3/includes/controls/groups/typography.php#L91
104 * and because the group control is a singleton, its args are changed to the last kit group control.
105 */
106 public function init_kit_controls() {
107 $this->get_active_kit_for_frontend()->get_settings();
108 }
109
110 public function get_current_settings( $setting = null ) {
111 $kit = $this->get_active_kit_for_frontend();
112
113 if ( ! $kit ) {
114 return '';
115 }
116
117 return $kit->get_settings( $setting );
118 }
119
120 public function create( array $kit_data = [], array $kit_meta_data = [] ) {
121 $default_kit_data = [
122 'post_status' => 'publish',
123 ];
124
125 $kit_data = array_merge( $default_kit_data, $kit_data );
126
127 $kit_data['post_type'] = Source_Local::CPT;
128
129 $kit = Plugin::$instance->documents->create( 'kit', $kit_data, $kit_meta_data );
130
131 if ( isset( $kit_data['settings'] ) ) {
132 $kit->save( [ 'settings' => $kit_data['settings'] ] );
133 }
134
135 return $kit->get_id();
136 }
137
138 public function create_new_kit( $kit_name = '', $settings = [], $active = true ) {
139 $kit_name = $kit_name ? $kit_name : esc_html__( 'Custom', 'elementor' );
140
141 $current_kit = $this->get_active_kit();
142
143 $kit_meta_data = $this->get_meta_to_preserve( $current_kit );
144
145 $id = $this->create( [
146 'post_title' => $kit_name,
147 'settings' => $settings,
148 ], $kit_meta_data );
149
150 if ( $active ) {
151 update_option( self::OPTION_PREVIOUS, $this->get_active_id() );
152 update_option( self::OPTION_ACTIVE, $id );
153 }
154
155 do_action( 'elementor/kit/after_new_kit_created', [
156 'new_kit_id' => $id,
157 'previous_kit_id' => $current_kit->get_post()->ID,
158 ] );
159
160 return $id;
161 }
162
163 private function get_meta_to_preserve( Kit $current_kit ): array {
164 $meta_keys_to_preserve = apply_filters( 'elementor/kit/meta_to_preserve_on_kit_import', [] );
165
166 $kit_meta_data = [];
167
168 foreach ( $meta_keys_to_preserve as $meta_key ) {
169 $value = $current_kit->get_meta( $meta_key );
170
171 if ( ! empty( $value ) ) {
172 $kit_meta_data[ $meta_key ] = $value;
173 }
174 }
175
176 return $kit_meta_data;
177 }
178
179 public function create_default() {
180 return $this->create( [
181 'post_title' => esc_html__( 'Default Kit', 'elementor' ),
182 ] );
183 }
184
185 /**
186 * Create a default kit if needed.
187 *
188 * This action runs on activation hook, all the Plugin components do not exists and
189 * the Document manager and Kits manager instances cannot be used.
190 *
191 * @return int|void|\WP_Error
192 */
193 public static function create_default_kit() {
194 if ( get_option( self::OPTION_ACTIVE ) ) {
195 return;
196 }
197
198 $id = wp_insert_post( [
199 'post_title' => esc_html__( 'Default Kit', 'elementor' ),
200 'post_type' => Source_Local::CPT,
201 'post_status' => 'publish',
202 'meta_input' => [
203 '_elementor_edit_mode' => 'builder',
204 Document::TYPE_META_KEY => 'kit',
205 ],
206 ] );
207
208 update_option( self::OPTION_ACTIVE, $id );
209
210 return $id;
211 }
212
213 /**
214 * @param $imported_kit_id int The id of the imported kit that should be deleted.
215 * @param $active_kit_id int The id of the kit that should set as 'active_kit' after the deletion.
216 * @param $previous_kit_id int The id of the kit that should set as 'previous_kit' after the deletion.
217 * @return void
218 */
219 public function revert( int $imported_kit_id, int $active_kit_id, int $previous_kit_id ) {
220 // If the kit that should set as active is not a valid kit then abort the revert.
221 if ( ! $this->is_kit( $active_kit_id ) ) {
222 return;
223 }
224
225 // This a hacky solution to avoid from the revert process to be interrupted by the `trash_kit_confirmation`.
226 $this->should_skip_trash_kit_confirmation = true;
227
228 $kit = $this->get_kit( $imported_kit_id );
229 $kit->force_delete();
230
231 $this->should_skip_trash_kit_confirmation = false;
232
233 update_option( self::OPTION_ACTIVE, $active_kit_id );
234
235 if ( $this->is_kit( $previous_kit_id ) ) {
236 update_option( self::OPTION_PREVIOUS, $previous_kit_id );
237 }
238 }
239
240 /**
241 * @param Documents_Manager $documents_manager
242 */
243 public function register_document( $documents_manager ) {
244 $documents_manager->register_document_type( 'kit', Kit::get_class_full_name() );
245 }
246
247 public function localize_settings( $settings ) {
248 $kit = $this->get_active_kit();
249 $kit_controls = $kit->get_controls();
250 $design_system_controls = [
251 'colors' => $kit_controls['system_colors']['fields'],
252 'typography' => $kit_controls['system_typography']['fields'],
253 ];
254
255 $settings = array_replace_recursive( $settings, [
256 'kit_id' => $kit->get_main_id(),
257 'kit_config' => [
258 'typography_prefix' => Global_Typography::TYPOGRAPHY_GROUP_PREFIX,
259 'design_system_controls' => $design_system_controls,
260 ],
261 'user' => [
262 'can_edit_kit' => $kit->is_editable_by_current_user(),
263 ],
264 ] );
265
266 return $settings;
267 }
268
269 public function preview_enqueue_styles() {
270 $kit = $this->get_kit_for_frontend();
271
272 if ( $kit ) {
273 // On preview, the global style is not enqueued.
274 $this->frontend_before_enqueue_styles();
275
276 Plugin::$instance->frontend->print_fonts_links();
277 }
278 }
279
280 public function frontend_before_enqueue_styles() {
281 $kit = $this->get_kit_for_frontend();
282
283 if ( $kit ) {
284 if ( $kit->is_autosave() ) {
285 $css_file = Post_Preview::create( $kit->get_id() );
286 } else {
287 $css_file = Post_CSS::create( $kit->get_id() );
288 }
289
290 $css_file->enqueue();
291 }
292 }
293
294 public function render_panel_html() {
295 require __DIR__ . '/views/panel.php';
296 }
297
298 public function get_kit_for_frontend() {
299 $kit = false;
300 $active_kit = $this->get_active_kit();
301 $is_kit_preview = is_preview() && isset( $_GET['preview_id'] ) && $active_kit->get_main_id() === (int) $_GET['preview_id'];
302
303 if ( $is_kit_preview ) {
304 $kit = Plugin::$instance->documents->get_doc_or_auto_save( $active_kit->get_main_id(), get_current_user_id() );
305 } elseif ( null !== $active_kit->get_main_post() && 'publish' === $active_kit->get_main_post()->post_status ) {
306 $kit = $active_kit;
307 }
308
309 return $kit;
310 }
311
312 public function update_kit_settings_based_on_option( $key, $value ) {
313 /** @var Kit $active_kit */
314 $active_kit = $this->get_active_kit();
315
316 if ( $active_kit->is_saving() ) {
317 return;
318 }
319
320 $active_kit->update_settings( [ $key => $value ] );
321 }
322
323 /**
324 * Map Scheme To Global
325 *
326 * Convert a given scheme value to its corresponding default global value
327 *
328 * @param string $type 'color'/'typography'.
329 * @param $value
330 * @return mixed
331 */
332 private function map_scheme_to_global( $type, $value ) {
333 $schemes_to_globals_map = [
334 'color' => [
335 '1' => Global_Colors::COLOR_PRIMARY,
336 '2' => Global_Colors::COLOR_SECONDARY,
337 '3' => Global_Colors::COLOR_TEXT,
338 '4' => Global_Colors::COLOR_ACCENT,
339 ],
340 'typography' => [
341 '1' => Global_Typography::TYPOGRAPHY_PRIMARY,
342 '2' => Global_Typography::TYPOGRAPHY_SECONDARY,
343 '3' => Global_Typography::TYPOGRAPHY_TEXT,
344 '4' => Global_Typography::TYPOGRAPHY_ACCENT,
345 ],
346 ];
347
348 return $schemes_to_globals_map[ $type ][ $value ];
349 }
350
351 /**
352 * Convert Scheme to Default Global
353 *
354 * If a control has a scheme property, convert it to a default Global.
355 *
356 * @param $scheme - Control scheme property
357 * @return array - Control/group control args
358 * @since 3.0.0
359 * @access public
360 */
361 public function convert_scheme_to_global( $scheme ) {
362 if ( isset( $scheme['type'] ) && isset( $scheme['value'] ) ) {
363 // _deprecated_argument( $args['scheme'], '3.0.0', 'Schemes are now deprecated - use $args[\'global\'] instead.' );
364 return $this->map_scheme_to_global( $scheme['type'], $scheme['value'] );
365 }
366
367 // Typography control 'scheme' properties usually only include the string with the typography value ('1'-'4').
368 return $this->map_scheme_to_global( 'typography', $scheme );
369 }
370
371 public function register_controls() {
372 $controls_manager = Plugin::$instance->controls_manager;
373
374 $controls_manager->register( new Repeater() );
375 }
376
377 public function is_custom_colors_enabled() {
378 return ! get_option( 'elementor_disable_color_schemes' );
379 }
380
381 public function is_custom_typography_enabled() {
382 return ! get_option( 'elementor_disable_typography_schemes' );
383 }
384
385 /**
386 * Add kit wrapper body class.
387 *
388 * It should be added even for non Elementor pages,
389 * in order to support embedded templates.
390 */
391 private function add_body_class() {
392 $kit = $this->get_kit_for_frontend();
393
394 if ( $kit ) {
395 Plugin::$instance->frontend->add_body_class( 'elementor-kit-' . $kit->get_main_id() );
396 }
397 }
398
399 /**
400 * Send a confirm message before move a kit to trash, or if delete permanently not for trash.
401 *
402 * @param $post_id
403 * @param bool $is_permanently_delete
404 */
405 private function before_delete_kit( $post_id, $is_permanently_delete = false ) {
406 if ( $this->should_skip_trash_kit_confirmation ) {
407 return;
408 }
409
410 $document = Plugin::$instance->documents->get( $post_id );
411
412 if (
413 ! $document ||
414 ! $this->is_kit( $post_id ) ||
415 isset( $_GET['force_delete_kit'] ) || // phpcs:ignore -- nonce validation is not require here.
416 ( $is_permanently_delete && $document->is_trash() )
417 ) {
418 return;
419 }
420
421 ob_start();
422 require __DIR__ . '/views/trash-kit-confirmation.php';
423
424 $confirmation_content = ob_get_clean();
425
426 // PHPCS - the content does not contain user input value.
427 wp_die( new \WP_Error( 'cant_delete_kit', $confirmation_content ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
428 }
429
430 /**
431 * Add 'Edit with elementor -> Site Settings' in admin bar.
432 *
433 * @param [] $admin_bar_config
434 *
435 * @return array $admin_bar_config
436 */
437 private function add_menu_in_admin_bar( $admin_bar_config ) {
438 $document = Plugin::$instance->documents->get( get_the_ID() );
439
440 if ( ! $document || ! $document->is_built_with_elementor() ) {
441 $recent_edited_post = Utils::get_recently_edited_posts_query( [
442 'posts_per_page' => 1,
443 ] );
444
445 if ( $recent_edited_post->post_count ) {
446 $posts = $recent_edited_post->get_posts();
447 $document = Plugin::$instance->documents->get( reset( $posts )->ID );
448 }
449 }
450
451 if ( $document ) {
452 $document_edit_url = add_query_arg(
453 [
454 'active-document' => $this->get_active_id(),
455 ],
456 $document->get_edit_url()
457 );
458
459 $admin_bar_config['elementor_edit_page']['children'][] = [
460 'id' => 'elementor_site_settings',
461 'title' => esc_html__( 'Site Settings', 'elementor' ),
462 'sub_title' => esc_html__( 'Site', 'elementor' ),
463 'href' => $document_edit_url,
464 'class' => 'elementor-site-settings',
465 'parent_class' => 'elementor-second-section',
466 ];
467 }
468
469 return $admin_bar_config;
470 }
471
472 public function __construct() {
473 add_action( 'elementor/documents/register', [ $this, 'register_document' ] );
474 add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] );
475 add_filter( 'elementor/editor/footer', [ $this, 'render_panel_html' ] );
476 add_action( 'elementor/frontend/after_enqueue_styles', [ $this, 'frontend_before_enqueue_styles' ], 0 );
477 add_action( 'elementor/preview/enqueue_styles', [ $this, 'preview_enqueue_styles' ], 0 );
478 add_action( 'elementor/controls/register', [ $this, 'register_controls' ] );
479
480 add_action( 'wp_trash_post', function ( $post_id ) {
481 $this->before_delete_kit( $post_id );
482 } );
483
484 add_action( 'before_delete_post', function ( $post_id ) {
485 $this->before_delete_kit( $post_id, true );
486 } );
487
488 add_action( 'update_option_blogname', function ( $old_value, $value ) {
489 $this->update_kit_settings_based_on_option( 'site_name', $value );
490 }, 10, 2 );
491
492 add_action( 'update_option_blogdescription', function ( $old_value, $value ) {
493 $this->update_kit_settings_based_on_option( 'site_description', $value );
494 }, 10, 2 );
495
496 add_action( 'wp_head', function() {
497 $this->add_body_class();
498 } );
499
500 add_filter( 'elementor/frontend/admin_bar/settings', function ( $admin_bar_config ) {
501 return $this->add_menu_in_admin_bar( $admin_bar_config );
502 }, 9 /* Before site-editor (theme-builder) */ );
503 }
504 }
505