module.php (11365B)
1 <?php 2 namespace Elementor\Modules\PageTemplates; 3 4 use Elementor\Controls_Manager; 5 use Elementor\Core\Base\Document; 6 use Elementor\Core\Base\Module as BaseModule; 7 use Elementor\Core\Kits\Documents\Kit; 8 use Elementor\Plugin; 9 use Elementor\Utils; 10 use Elementor\Core\DocumentTypes\PageBase as PageBase; 11 use Elementor\Modules\Library\Documents\Page as LibraryPageDocument; 12 13 if ( ! defined( 'ABSPATH' ) ) { 14 exit; // Exit if accessed directly 15 } 16 17 /** 18 * Elementor page templates module. 19 * 20 * Elementor page templates module handler class is responsible for registering 21 * and managing Elementor page templates modules. 22 * 23 * @since 2.0.0 24 */ 25 class Module extends BaseModule { 26 27 /** 28 * The of the theme. 29 */ 30 const TEMPLATE_THEME = 'elementor_theme'; 31 32 /** 33 * Elementor Canvas template name. 34 */ 35 const TEMPLATE_CANVAS = 'elementor_canvas'; 36 37 /** 38 * Elementor Header & Footer template name. 39 */ 40 const TEMPLATE_HEADER_FOOTER = 'elementor_header_footer'; 41 42 /** 43 * Print callback. 44 * 45 * Holds the page template callback content. 46 * 47 * @since 2.0.0 48 * @access protected 49 * 50 * @var callable 51 */ 52 protected $print_callback; 53 54 /** 55 * Get module name. 56 * 57 * Retrieve the page templates module name. 58 * 59 * @since 2.0.0 60 * @access public 61 * 62 * @return string Module name. 63 */ 64 public function get_name() { 65 return 'page-templates'; 66 } 67 68 /** 69 * Template include. 70 * 71 * Update the path for the Elementor Canvas template. 72 * 73 * Fired by `template_include` filter. 74 * 75 * @since 2.0.0 76 * @access public 77 * 78 * @param string $template The path of the template to include. 79 * 80 * @return string The path of the template to include. 81 */ 82 public function template_include( $template ) { 83 if ( is_singular() ) { 84 $document = Plugin::$instance->documents->get_doc_for_frontend( get_the_ID() ); 85 86 if ( $document && $document::get_property( 'support_wp_page_templates' ) ) { 87 $page_template = $document->get_meta( '_wp_page_template' ); 88 89 $template_path = $this->get_template_path( $page_template ); 90 91 if ( self::TEMPLATE_THEME !== $page_template && ! $template_path && $document->is_built_with_elementor() ) { 92 $kit_default_template = Plugin::$instance->kits_manager->get_current_settings( 'default_page_template' ); 93 $template_path = $this->get_template_path( $kit_default_template ); 94 } 95 96 if ( $template_path ) { 97 $template = $template_path; 98 99 Plugin::$instance->inspector->add_log( 'Page Template', Plugin::$instance->inspector->parse_template_path( $template ), $document->get_edit_url() ); 100 } 101 } 102 } 103 104 return $template; 105 } 106 107 /** 108 * Add WordPress templates. 109 * 110 * Adds Elementor templates to all the post types that support 111 * Elementor. 112 * 113 * Fired by `init` action. 114 * 115 * @since 2.0.0 116 * @access public 117 */ 118 public function add_wp_templates_support() { 119 $post_types = get_post_types_by_support( 'elementor' ); 120 121 foreach ( $post_types as $post_type ) { 122 add_filter( "theme_{$post_type}_templates", [ $this, 'add_page_templates' ], 10, 4 ); 123 } 124 } 125 126 /** 127 * Add page templates. 128 * 129 * Add the Elementor page templates to the theme templates. 130 * 131 * Fired by `theme_{$post_type}_templates` filter. 132 * 133 * @since 2.0.0 134 * @access public 135 * @static 136 * 137 * @param array $page_templates Array of page templates. Keys are filenames, 138 * checks are translated names. 139 * 140 * @param \WP_Theme $wp_theme 141 * @param \WP_Post $post 142 * 143 * @return array Page templates. 144 */ 145 public function add_page_templates( $page_templates, $wp_theme, $post ) { 146 if ( $post ) { 147 // FIX ME: Gutenberg not send $post as WP_Post object, just the post ID. 148 $post_id = ! empty( $post->ID ) ? $post->ID : $post; 149 150 $document = Plugin::$instance->documents->get( $post_id ); 151 if ( $document && ! $document::get_property( 'support_wp_page_templates' ) ) { 152 return $page_templates; 153 } 154 } 155 156 $page_templates = [ 157 self::TEMPLATE_CANVAS => _x( 'Elementor Canvas', 'Page Template', 'elementor' ), 158 self::TEMPLATE_HEADER_FOOTER => _x( 'Elementor Full Width', 'Page Template', 'elementor' ), 159 self::TEMPLATE_THEME => _x( 'Theme', 'Page Template', 'elementor' ), 160 ] + $page_templates; 161 162 return $page_templates; 163 } 164 165 /** 166 * Set print callback. 167 * 168 * Set the page template callback. 169 * 170 * @since 2.0.0 171 * @access public 172 * 173 * @param callable $callback 174 */ 175 public function set_print_callback( $callback ) { 176 $this->print_callback = $callback; 177 } 178 179 /** 180 * Print callback. 181 * 182 * Prints the page template content using WordPress loop. 183 * 184 * @since 2.0.0 185 * @access public 186 */ 187 public function print_callback() { 188 while ( have_posts() ) : 189 the_post(); 190 the_content(); 191 endwhile; 192 } 193 194 /** 195 * Print content. 196 * 197 * Prints the page template content. 198 * 199 * @since 2.0.0 200 * @access public 201 */ 202 public function print_content() { 203 if ( ! $this->print_callback ) { 204 $this->print_callback = [ $this, 'print_callback' ]; 205 } 206 207 call_user_func( $this->print_callback ); 208 } 209 210 /** 211 * Get page template path. 212 * 213 * Retrieve the path for any given page template. 214 * 215 * @since 2.0.0 216 * @access public 217 * 218 * @param string $page_template The page template name. 219 * 220 * @return string Page template path. 221 */ 222 public function get_template_path( $page_template ) { 223 $template_path = ''; 224 switch ( $page_template ) { 225 case self::TEMPLATE_CANVAS: 226 $template_path = __DIR__ . '/templates/canvas.php'; 227 break; 228 case self::TEMPLATE_HEADER_FOOTER: 229 $template_path = __DIR__ . '/templates/header-footer.php'; 230 break; 231 } 232 233 return $template_path; 234 } 235 236 /** 237 * Register template control. 238 * 239 * Adds custom controls to any given document. 240 * 241 * Fired by `update_post_metadata` action. 242 * 243 * @since 2.0.0 244 * @access public 245 * 246 * @param Document $document The document instance. 247 */ 248 public function action_register_template_control( $document ) { 249 if ( $document instanceof PageBase || $document instanceof LibraryPageDocument ) { 250 $this->register_template_control( $document ); 251 } 252 } 253 254 /** 255 * Register template control. 256 * 257 * Adds custom controls to any given document. 258 * 259 * @since 2.0.0 260 * @access public 261 * 262 * @param Document $document The document instance. 263 * @param string $control_id Optional. The control ID. Default is `template`. 264 */ 265 public function register_template_control( $document, $control_id = 'template' ) { 266 if ( ! Utils::is_cpt_custom_templates_supported() ) { 267 return; 268 } 269 270 require_once ABSPATH . '/wp-admin/includes/template.php'; 271 272 $document->start_injection( [ 273 'of' => 'post_status', 274 'fallback' => [ 275 'of' => 'post_title', 276 ], 277 ] ); 278 279 $control_options = [ 280 'options' => array_flip( get_page_templates( null, $document->get_main_post()->post_type ) ), 281 ]; 282 283 $this->add_template_controls( $document, $control_id, $control_options ); 284 285 $document->end_injection(); 286 } 287 288 // The $options variable is an array of $control_options to overwrite the default 289 public function add_template_controls( Document $document, $control_id, $control_options ) { 290 // Default Control Options 291 $default_control_options = [ 292 'label' => esc_html__( 'Page Layout', 'elementor' ), 293 'type' => Controls_Manager::SELECT, 294 'default' => 'default', 295 'options' => [ 296 'default' => esc_html__( 'Default', 'elementor' ), 297 ], 298 ]; 299 300 $control_options = array_replace_recursive( $default_control_options, $control_options ); 301 302 $document->add_control( 303 $control_id, 304 $control_options 305 ); 306 307 $document->add_control( 308 $control_id . '_default_description', 309 [ 310 'type' => Controls_Manager::RAW_HTML, 311 'raw' => '<b>' . esc_html__( 'The default page template as defined in Elementor Panel → Hamburger Menu → Site Settings.', 'elementor' ) . '</b>', 312 'content_classes' => 'elementor-descriptor', 313 'condition' => [ 314 $control_id => 'default', 315 ], 316 ] 317 ); 318 319 $document->add_control( 320 $control_id . '_theme_description', 321 [ 322 'type' => Controls_Manager::RAW_HTML, 323 'raw' => '<b>' . esc_html__( 'Default Page Template from your theme.', 'elementor' ) . '</b>', 324 'content_classes' => 'elementor-descriptor', 325 'condition' => [ 326 $control_id => self::TEMPLATE_THEME, 327 ], 328 ] 329 ); 330 331 $document->add_control( 332 $control_id . '_canvas_description', 333 [ 334 'type' => Controls_Manager::RAW_HTML, 335 'raw' => '<b>' . esc_html__( 'No header, no footer, just Elementor', 'elementor' ) . '</b>', 336 'content_classes' => 'elementor-descriptor', 337 'condition' => [ 338 $control_id => self::TEMPLATE_CANVAS, 339 ], 340 ] 341 ); 342 343 $document->add_control( 344 $control_id . '_header_footer_description', 345 [ 346 'type' => Controls_Manager::RAW_HTML, 347 'raw' => '<b>' . esc_html__( 'This template includes the header, full-width content and footer', 'elementor' ) . '</b>', 348 'content_classes' => 'elementor-descriptor', 349 'condition' => [ 350 $control_id => self::TEMPLATE_HEADER_FOOTER, 351 ], 352 ] 353 ); 354 355 if ( $document instanceof Kit ) { 356 $document->add_control( 357 'reload_preview_description', 358 [ 359 'type' => Controls_Manager::RAW_HTML, 360 'raw' => esc_html__( 'Changes will be reflected in the preview only after the page reloads.', 'elementor' ), 361 'content_classes' => 'elementor-descriptor', 362 ] 363 ); 364 } 365 } 366 367 /** 368 * Filter metadata update. 369 * 370 * Filters whether to update metadata of a specific type. 371 * 372 * Elementor don't allow WordPress to update the parent page template 373 * during `wp_update_post`. 374 * 375 * Fired by `update_{$meta_type}_metadata` filter. 376 * 377 * @since 2.0.0 378 * @access public 379 * 380 * @param bool $check Whether to allow updating metadata for the given type. 381 * @param int $object_id Object ID. 382 * @param string $meta_key Meta key. 383 * 384 * @return bool Whether to allow updating metadata of a specific type. 385 */ 386 public function filter_update_meta( $check, $object_id, $meta_key ) { 387 if ( '_wp_page_template' === $meta_key && Plugin::$instance->common ) { 388 /** @var \Elementor\Core\Common\Modules\Ajax\Module $ajax */ 389 $ajax = Plugin::$instance->common->get_component( 'ajax' ); 390 391 $ajax_data = $ajax->get_current_action_data(); 392 393 $is_autosave_action = $ajax_data && 'save_builder' === $ajax_data['action'] && Document::STATUS_AUTOSAVE === $ajax_data['data']['status']; 394 395 // Don't allow WP to update the parent page template. 396 // (during `wp_update_post` from page-settings or save_plain_text). 397 if ( $is_autosave_action && ! wp_is_post_autosave( $object_id ) && Document::STATUS_DRAFT !== get_post_status( $object_id ) ) { 398 $check = false; 399 } 400 } 401 402 return $check; 403 } 404 405 /** 406 * Support `wp_body_open` action, available since WordPress 5.2. 407 * 408 * @since 2.7.0 409 * @access public 410 */ 411 public static function body_open() { 412 if ( function_exists( 'wp_body_open' ) ) { 413 wp_body_open(); 414 } else { 415 do_action( 'wp_body_open' ); 416 } 417 } 418 419 /** 420 * Page templates module constructor. 421 * 422 * Initializing Elementor page templates module. 423 * 424 * @since 2.0.0 425 * @access public 426 */ 427 public function __construct() { 428 add_action( 'init', [ $this, 'add_wp_templates_support' ] ); 429 430 add_filter( 'template_include', [ $this, 'template_include' ], 11 /* After Plugins/WooCommerce */ ); 431 432 add_action( 'elementor/documents/register_controls', [ $this, 'action_register_template_control' ] ); 433 434 add_filter( 'update_post_metadata', [ $this, 'filter_update_meta' ], 10, 3 ); 435 } 436 }