<?php namespace Elementor\Modules\PageTemplates; use Elementor\Controls_Manager; use Elementor\Core\Base\Document; use Elementor\Core\Base\Module as BaseModule; use Elementor\Core\Kits\Documents\Kit; use Elementor\Plugin; use Elementor\Utils; use Elementor\Core\DocumentTypes\PageBase as PageBase; use Elementor\Modules\Library\Documents\Page as LibraryPageDocument; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly } /** * Elementor page templates module. * * Elementor page templates module handler class is responsible for registering * and managing Elementor page templates modules. * * @since 2.0.0 */ class Module extends BaseModule { /** * The of the theme. */ const TEMPLATE_THEME = 'elementor_theme'; /** * Elementor Canvas template name. */ const TEMPLATE_CANVAS = 'elementor_canvas'; /** * Elementor Header & Footer template name. */ const TEMPLATE_HEADER_FOOTER = 'elementor_header_footer'; /** * Print callback. * * Holds the page template callback content. * * @since 2.0.0 * @access protected * * @var callable */ protected $print_callback; /** * Get module name. * * Retrieve the page templates module name. * * @since 2.0.0 * @access public * * @return string Module name. */ public function get_name() { return 'page-templates'; } /** * Template include. * * Update the path for the Elementor Canvas template. * * Fired by `template_include` filter. * * @since 2.0.0 * @access public * * @param string $template The path of the template to include. * * @return string The path of the template to include. */ public function template_include( $template ) { if ( is_singular() ) { $document = Plugin::$instance->documents->get_doc_for_frontend( get_the_ID() ); if ( $document && $document::get_property( 'support_wp_page_templates' ) ) { $page_template = $document->get_meta( '_wp_page_template' ); $template_path = $this->get_template_path( $page_template ); if ( self::TEMPLATE_THEME !== $page_template && ! $template_path && $document->is_built_with_elementor() ) { $kit_default_template = Plugin::$instance->kits_manager->get_current_settings( 'default_page_template' ); $template_path = $this->get_template_path( $kit_default_template ); } if ( $template_path ) { $template = $template_path; Plugin::$instance->inspector->add_log( 'Page Template', Plugin::$instance->inspector->parse_template_path( $template ), $document->get_edit_url() ); } } } return $template; } /** * Add WordPress templates. * * Adds Elementor templates to all the post types that support * Elementor. * * Fired by `init` action. * * @since 2.0.0 * @access public */ public function add_wp_templates_support() { $post_types = get_post_types_by_support( 'elementor' ); foreach ( $post_types as $post_type ) { add_filter( "theme_{$post_type}_templates", [ $this, 'add_page_templates' ], 10, 4 ); } } /** * Add page templates. * * Add the Elementor page templates to the theme templates. * * Fired by `theme_{$post_type}_templates` filter. * * @since 2.0.0 * @access public * @static * * @param array $page_templates Array of page templates. Keys are filenames, * checks are translated names. * * @param \WP_Theme $wp_theme * @param \WP_Post $post * * @return array Page templates. */ public function add_page_templates( $page_templates, $wp_theme, $post ) { if ( $post ) { // FIX ME: Gutenberg not send $post as WP_Post object, just the post ID. $post_id = ! empty( $post->ID ) ? $post->ID : $post; $document = Plugin::$instance->documents->get( $post_id ); if ( $document && ! $document::get_property( 'support_wp_page_templates' ) ) { return $page_templates; } } $page_templates = [ self::TEMPLATE_CANVAS => esc_html__( 'Elementor Canvas', 'elementor' ), self::TEMPLATE_HEADER_FOOTER => esc_html__( 'Elementor Full Width', 'elementor' ), self::TEMPLATE_THEME => esc_html__( 'Theme', 'elementor' ), ] + $page_templates; return $page_templates; } /** * Set print callback. * * Set the page template callback. * * @since 2.0.0 * @access public * * @param callable $callback */ public function set_print_callback( $callback ) { $this->print_callback = $callback; } /** * Print callback. * * Prints the page template content using WordPress loop. * * @since 2.0.0 * @access public */ public function print_callback() { while ( have_posts() ) : the_post(); the_content(); endwhile; } /** * Print content. * * Prints the page template content. * * @since 2.0.0 * @access public */ public function print_content() { if ( ! $this->print_callback ) { $this->print_callback = [ $this, 'print_callback' ]; } call_user_func( $this->print_callback ); } /** * Get page template path. * * Retrieve the path for any given page template. * * @since 2.0.0 * @access public * * @param string $page_template The page template name. * * @return string Page template path. */ public function get_template_path( $page_template ) { $template_path = ''; switch ( $page_template ) { case self::TEMPLATE_CANVAS: $template_path = __DIR__ . '/templates/canvas.php'; break; case self::TEMPLATE_HEADER_FOOTER: $template_path = __DIR__ . '/templates/header-footer.php'; break; } return $template_path; } /** * Register template control. * * Adds custom controls to any given document. * * Fired by `update_post_metadata` action. * * @since 2.0.0 * @access public * * @param Document $document The document instance. */ public function action_register_template_control( $document ) { if ( ( $document instanceof PageBase || $document instanceof LibraryPageDocument ) && $document::get_property( 'support_page_layout' ) ) { $this->register_template_control( $document ); } } /** * Register template control. * * Adds custom controls to any given document. * * @since 2.0.0 * @access public * * @param Document $document The document instance. * @param string $control_id Optional. The control ID. Default is `template`. */ public function register_template_control( $document, $control_id = 'template' ) { if ( ! Utils::is_cpt_custom_templates_supported() ) { return; } require_once ABSPATH . '/wp-admin/includes/template.php'; $document->start_injection( [ 'of' => 'post_status', 'fallback' => [ 'of' => 'post_title', ], ] ); $control_options = [ 'options' => array_flip( get_page_templates( null, $document->get_main_post()->post_type ) ), ]; $this->add_template_controls( $document, $control_id, $control_options ); $document->end_injection(); } // The $options variable is an array of $control_options to overwrite the default public function add_template_controls( Document $document, $control_id, $control_options ) { // Default Control Options $default_control_options = [ 'label' => esc_html__( 'Page Layout', 'elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'default', 'options' => [ 'default' => esc_html__( 'Default', 'elementor' ), ], ]; $control_options = array_replace_recursive( $default_control_options, $control_options ); $document->add_control( $control_id, $control_options ); $document->add_control( $control_id . '_default_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '<b>' . esc_html__( 'The default page template as defined in Elementor Panel → Hamburger Menu → Site Settings.', 'elementor' ) . '</b>', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => 'default', ], ] ); $document->add_control( $control_id . '_theme_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '<b>' . esc_html__( 'Default Page Template from your theme.', 'elementor' ) . '</b>', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => self::TEMPLATE_THEME, ], ] ); $document->add_control( $control_id . '_canvas_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '<b>' . esc_html__( 'No header, no footer, just Elementor', 'elementor' ) . '</b>', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => self::TEMPLATE_CANVAS, ], ] ); $document->add_control( $control_id . '_header_footer_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => '<b>' . esc_html__( 'This template includes the header, full-width content and footer', 'elementor' ) . '</b>', 'content_classes' => 'elementor-descriptor', 'condition' => [ $control_id => self::TEMPLATE_HEADER_FOOTER, ], ] ); if ( $document instanceof Kit ) { $document->add_control( 'reload_preview_description', [ 'type' => Controls_Manager::RAW_HTML, 'raw' => esc_html__( 'Changes will be reflected in the preview only after the page reloads.', 'elementor' ), 'content_classes' => 'elementor-descriptor', ] ); } } /** * Filter metadata update. * * Filters whether to update metadata of a specific type. * * Elementor don't allow WordPress to update the parent page template * during `wp_update_post`. * * Fired by `update_{$meta_type}_metadata` filter. * * @since 2.0.0 * @access public * * @param bool $check Whether to allow updating metadata for the given type. * @param int $object_id Object ID. * @param string $meta_key Meta key. * * @return bool Whether to allow updating metadata of a specific type. */ public function filter_update_meta( $check, $object_id, $meta_key ) { if ( '_wp_page_template' === $meta_key && Plugin::$instance->common ) { /** @var \Elementor\Core\Common\Modules\Ajax\Module $ajax */ $ajax = Plugin::$instance->common->get_component( 'ajax' ); $ajax_data = $ajax->get_current_action_data(); $is_autosave_action = $ajax_data && 'save_builder' === $ajax_data['action'] && Document::STATUS_AUTOSAVE === $ajax_data['data']['status']; // Don't allow WP to update the parent page template. // (during `wp_update_post` from page-settings or save_plain_text). if ( $is_autosave_action && ! wp_is_post_autosave( $object_id ) && Document::STATUS_DRAFT !== get_post_status( $object_id ) ) { $check = false; } } return $check; } /** * Support `wp_body_open` action, available since WordPress 5.2. * * @since 2.7.0 * @access public */ public static function body_open() { wp_body_open(); } /** * Page templates module constructor. * * Initializing Elementor page templates module. * * @since 2.0.0 * @access public */ public function __construct() { add_action( 'init', [ $this, 'add_wp_templates_support' ] ); add_filter( 'template_include', [ $this, 'template_include' ], 11 /* After Plugins/WooCommerce */ ); add_action( 'elementor/documents/register_controls', [ $this, 'action_register_template_control' ] ); add_filter( 'update_post_metadata', [ $this, 'filter_update_meta' ], 10, 3 ); } }