manager.php (11707B)
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 as 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 E_HASH_COMMAND_OPEN_SITE_SETTINGS = 'e:run:panel/global/open'; 25 26 public function get_active_id() { 27 return get_option( self::OPTION_ACTIVE ); 28 } 29 30 public function get_active_kit() { 31 $kit = Plugin::$instance->documents->get( $this->get_active_id() ); 32 33 if ( ! $this->is_valid_kit( $kit ) ) { 34 return $this->get_empty_kit_instance(); 35 } 36 37 return $kit; 38 } 39 40 public function get_active_kit_for_frontend() { 41 $kit = Plugin::$instance->documents->get_doc_for_frontend( $this->get_active_id() ); 42 43 if ( ! $this->is_valid_kit( $kit ) ) { 44 return $this->get_empty_kit_instance(); 45 } 46 47 return $kit; 48 } 49 50 /** 51 * @param $kit 52 * 53 * @return bool 54 */ 55 private function is_valid_kit( $kit ) { 56 return $kit && $kit instanceof Kit && 'trash' !== $kit->get_main_post()->post_status; 57 } 58 59 /** 60 * Returns an empty kit for situation when there is no kit in the site. 61 * 62 * @return Kit 63 * @throws \Exception 64 */ 65 private function get_empty_kit_instance() { 66 return new Kit( [ 67 'settings' => [], 68 'post_id' => 0, 69 ] ); 70 } 71 72 /** 73 * Checks if specific post is a kit. 74 * 75 * @param $post_id 76 * 77 * @return bool 78 */ 79 public function is_kit( $post_id ) { 80 $document = Plugin::$instance->documents->get( $post_id ); 81 82 return $document && $document instanceof Kit && ! $document->is_revision(); 83 } 84 85 86 /** 87 * Init kit controls. 88 * 89 * A temp solution in order to avoid init kit group control from within another group control. 90 * 91 * 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 92 * and because the group control is a singleton, its args are changed to the last kit group control. 93 */ 94 public function init_kit_controls() { 95 $this->get_active_kit_for_frontend()->get_settings(); 96 } 97 98 public function get_current_settings( $setting = null ) { 99 $kit = $this->get_active_kit_for_frontend(); 100 101 if ( ! $kit ) { 102 return ''; 103 } 104 105 return $kit->get_settings( $setting ); 106 } 107 108 public function create( array $kit_data = [], array $kit_meta_data = [] ) { 109 $default_kit_data = [ 110 'post_status' => 'publish', 111 ]; 112 113 $kit_data = array_merge( $default_kit_data, $kit_data ); 114 115 $kit_data['post_type'] = Source_Local::CPT; 116 117 $kit = Plugin::$instance->documents->create( 'kit', $kit_data, $kit_meta_data ); 118 119 return $kit->get_id(); 120 } 121 122 public function create_default() { 123 return $this->create( [ 124 'post_title' => esc_html__( 'Default Kit', 'elementor' ), 125 ] ); 126 } 127 128 /** 129 * Create a default kit if needed. 130 * 131 * This action runs on activation hook, all the Plugin components do not exists and 132 * the Document manager and Kits manager instances cannot be used. 133 * 134 * @return int|void|\WP_Error 135 */ 136 public static function create_default_kit() { 137 if ( get_option( self::OPTION_ACTIVE ) ) { 138 return; 139 } 140 141 $id = wp_insert_post( [ 142 'post_title' => __( 'Default Kit', 'elementor' ), 143 'post_type' => Source_Local::CPT, 144 'post_status' => 'publish', 145 'meta_input' => [ 146 '_elementor_edit_mode' => 'builder', 147 Document::TYPE_META_KEY => 'kit', 148 ], 149 ] ); 150 151 update_option( self::OPTION_ACTIVE, $id ); 152 153 return $id; 154 } 155 156 /** 157 * @param Documents_Manager $documents_manager 158 */ 159 public function register_document( $documents_manager ) { 160 $documents_manager->register_document_type( 'kit', Kit::get_class_full_name() ); 161 } 162 163 public function localize_settings( $settings ) { 164 $kit = $this->get_active_kit(); 165 $kit_controls = $kit->get_controls(); 166 $design_system_controls = [ 167 'colors' => $kit_controls['system_colors']['fields'], 168 'typography' => $kit_controls['system_typography']['fields'], 169 ]; 170 171 $settings = array_replace_recursive( $settings, [ 172 'kit_id' => $kit->get_main_id(), 173 'kit_config' => [ 174 'typography_prefix' => Global_Typography::TYPOGRAPHY_GROUP_PREFIX, 175 'design_system_controls' => $design_system_controls, 176 ], 177 'user' => [ 178 'can_edit_kit' => $kit->is_editable_by_current_user(), 179 ], 180 ] ); 181 182 return $settings; 183 } 184 185 public function preview_enqueue_styles() { 186 $kit = $this->get_kit_for_frontend(); 187 188 if ( $kit ) { 189 // On preview, the global style is not enqueued. 190 $this->frontend_before_enqueue_styles(); 191 192 Plugin::$instance->frontend->print_fonts_links(); 193 } 194 } 195 196 public function frontend_before_enqueue_styles() { 197 $kit = $this->get_kit_for_frontend(); 198 199 if ( $kit ) { 200 if ( $kit->is_autosave() ) { 201 $css_file = Post_Preview::create( $kit->get_id() ); 202 } else { 203 $css_file = Post_CSS::create( $kit->get_id() ); 204 } 205 206 $css_file->enqueue(); 207 } 208 } 209 210 public function render_panel_html() { 211 require __DIR__ . '/views/panel.php'; 212 } 213 214 public function get_kit_for_frontend() { 215 $kit = false; 216 $active_kit = $this->get_active_kit(); 217 $is_kit_preview = is_preview() && isset( $_GET['preview_id'] ) && $active_kit->get_main_id() === (int) $_GET['preview_id']; 218 219 if ( $is_kit_preview ) { 220 $kit = Plugin::$instance->documents->get_doc_or_auto_save( $active_kit->get_main_id(), get_current_user_id() ); 221 } elseif ( 'publish' === $active_kit->get_main_post()->post_status ) { 222 $kit = $active_kit; 223 } 224 225 return $kit; 226 } 227 228 public function update_kit_settings_based_on_option( $key, $value ) { 229 /** @var Kit $active_kit */ 230 $active_kit = $this->get_active_kit(); 231 232 if ( $active_kit->is_saving() ) { 233 return; 234 } 235 236 $active_kit->update_settings( [ $key => $value ] ); 237 } 238 239 /** 240 * Map Scheme To Global 241 * 242 * Convert a given scheme value to its corresponding default global value 243 * 244 * @param string $type 'color'/'typography' 245 * @param $value 246 * @return mixed 247 */ 248 private function map_scheme_to_global( $type, $value ) { 249 $schemes_to_globals_map = [ 250 'color' => [ 251 '1' => Global_Colors::COLOR_PRIMARY, 252 '2' => Global_Colors::COLOR_SECONDARY, 253 '3' => Global_Colors::COLOR_TEXT, 254 '4' => Global_Colors::COLOR_ACCENT, 255 ], 256 'typography' => [ 257 '1' => Global_Typography::TYPOGRAPHY_PRIMARY, 258 '2' => Global_Typography::TYPOGRAPHY_SECONDARY, 259 '3' => Global_Typography::TYPOGRAPHY_TEXT, 260 '4' => Global_Typography::TYPOGRAPHY_ACCENT, 261 ], 262 ]; 263 264 return $schemes_to_globals_map[ $type ][ $value ]; 265 } 266 267 /** 268 * Convert Scheme to Default Global 269 * 270 * If a control has a scheme property, convert it to a default Global. 271 * 272 * @param $scheme - Control scheme property 273 * @return array - Control/group control args 274 * @since 3.0.0 275 * @access public 276 */ 277 public function convert_scheme_to_global( $scheme ) { 278 if ( isset( $scheme['type'] ) && isset( $scheme['value'] ) ) { 279 //_deprecated_argument( $args['scheme'], '3.0.0', 'Schemes are now deprecated - use $args[\'global\'] instead.' ); 280 return $this->map_scheme_to_global( $scheme['type'], $scheme['value'] ); 281 } 282 283 // Typography control 'scheme' properties usually only include the string with the typography value ('1'-'4'). 284 return $this->map_scheme_to_global( 'typography', $scheme ); 285 } 286 287 public function register_controls() { 288 $controls_manager = Plugin::$instance->controls_manager; 289 290 $controls_manager->register_control( Repeater::CONTROL_TYPE, new Repeater() ); 291 } 292 293 public function is_custom_colors_enabled() { 294 return ! get_option( 'elementor_disable_color_schemes' ); 295 } 296 297 public function is_custom_typography_enabled() { 298 return ! get_option( 'elementor_disable_typography_schemes' ); 299 } 300 301 /** 302 * Add kit wrapper body class. 303 * 304 * It should be added even for non Elementor pages, 305 * in order to support embedded templates. 306 */ 307 private function add_body_class() { 308 $kit = $this->get_kit_for_frontend(); 309 310 if ( $kit ) { 311 Plugin::$instance->frontend->add_body_class( 'elementor-kit-' . $kit->get_main_id() ); 312 } 313 } 314 315 /** 316 * Send a confirm message before move a kit to trash, or if delete permanently not for trash. 317 * 318 * @param $post_id 319 * @param false $is_permanently_delete 320 */ 321 private function before_delete_kit( $post_id, $is_permanently_delete = false ) { 322 $document = Plugin::$instance->documents->get( $post_id ); 323 324 if ( 325 ! $document || 326 ! $this->is_kit( $post_id ) || 327 isset( $_GET['force_delete_kit'] ) || // phpcs:ignore -- nonce validation is not require here. 328 ( $is_permanently_delete && $document->is_trash() ) 329 ) { 330 return; 331 } 332 333 ob_start(); 334 require __DIR__ . '/views/trash-kit-confirmation.php'; 335 336 $confirmation_content = ob_get_clean(); 337 338 // PHPCS - the content does not contain user input value. 339 wp_die( new \WP_Error( 'cant_delete_kit', $confirmation_content ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 340 } 341 342 /** 343 * Add 'Edit with elementor -> Site Settings' in admin bar. 344 * 345 * @param [] $admin_bar_config 346 * 347 * @return array $admin_bar_config 348 */ 349 private function add_menu_in_admin_bar( $admin_bar_config ) { 350 $document = Plugin::$instance->documents->get( get_the_ID() ); 351 352 if ( ! $document || ! $document->is_built_with_elementor() ) { 353 $recent_edited_post = Utils::get_recently_edited_posts_query( [ 354 'posts_per_page' => 1, 355 ] ); 356 357 if ( $recent_edited_post->post_count ) { 358 $posts = $recent_edited_post->get_posts(); 359 $document = Plugin::$instance->documents->get( reset( $posts )->ID ); 360 } 361 } 362 363 if ( $document ) { 364 $admin_bar_config['elementor_edit_page']['children'][] = [ 365 'id' => 'elementor_site_settings', 366 'title' => esc_html__( 'Site Settings', 'elementor' ), 367 'sub_title' => esc_html__( 'Site', 'elementor' ), 368 'href' => $document->get_edit_url() . '#' . self::E_HASH_COMMAND_OPEN_SITE_SETTINGS, 369 'class' => 'elementor-site-settings', 370 'parent_class' => 'elementor-second-section', 371 ]; 372 } 373 374 return $admin_bar_config; 375 } 376 377 public function __construct() { 378 add_action( 'elementor/documents/register', [ $this, 'register_document' ] ); 379 add_filter( 'elementor/editor/localize_settings', [ $this, 'localize_settings' ] ); 380 add_filter( 'elementor/editor/footer', [ $this, 'render_panel_html' ] ); 381 add_action( 'elementor/frontend/after_enqueue_styles', [ $this, 'frontend_before_enqueue_styles' ], 0 ); 382 add_action( 'elementor/preview/enqueue_styles', [ $this, 'preview_enqueue_styles' ], 0 ); 383 add_action( 'elementor/controls/controls_registered', [ $this, 'register_controls' ] ); 384 385 add_action( 'wp_trash_post', function ( $post_id ) { 386 $this->before_delete_kit( $post_id ); 387 } ); 388 389 add_action( 'before_delete_post', function ( $post_id ) { 390 $this->before_delete_kit( $post_id, true ); 391 } ); 392 393 add_action( 'update_option_blogname', function ( $old_value, $value ) { 394 $this->update_kit_settings_based_on_option( 'site_name', $value ); 395 }, 10, 2 ); 396 397 add_action( 'update_option_blogdescription', function ( $old_value, $value ) { 398 $this->update_kit_settings_based_on_option( 'site_description', $value ); 399 }, 10, 2 ); 400 401 add_action( 'wp_head', function() { 402 $this->add_body_class(); 403 } ); 404 405 add_filter( 'elementor/frontend/admin_bar/settings', function ( $admin_bar_config ) { 406 return $this->add_menu_in_admin_bar( $admin_bar_config ); 407 }, 9 /* Before site-editor (theme-builder) */ ); 408 } 409 }