module.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. <?php
  2. namespace Elementor\Modules\PageTemplates;
  3. use Elementor\Controls_Manager;
  4. use Elementor\Core\Base\Document;
  5. use Elementor\Core\Base\Module as BaseModule;
  6. use Elementor\Core\DocumentTypes\Post as PostDocument;
  7. use Elementor\DB;
  8. use Elementor\Modules\Library\Documents\Page as PageDocument;
  9. use Elementor\Plugin;
  10. use Elementor\Utils;
  11. if ( ! defined( 'ABSPATH' ) ) {
  12. exit; // Exit if accessed directly
  13. }
  14. /**
  15. * Elementor page templates module.
  16. *
  17. * Elementor page templates module handler class is responsible for registering
  18. * and managing Elementor page templates modules.
  19. *
  20. * @since 2.0.0
  21. */
  22. class Module extends BaseModule {
  23. /**
  24. * Elementor Canvas template name.
  25. */
  26. const TEMPLATE_CANVAS = 'elementor_canvas';
  27. /**
  28. * Elementor Header & Footer template name.
  29. */
  30. const TEMPLATE_HEADER_FOOTER = 'elementor_header_footer';
  31. /**
  32. * Print callback.
  33. *
  34. * Holds the page template callback content.
  35. *
  36. * @since 2.0.0
  37. * @access protected
  38. *
  39. * @var callable
  40. */
  41. protected $print_callback;
  42. /**
  43. * Get module name.
  44. *
  45. * Retrieve the page templates module name.
  46. *
  47. * @since 2.0.0
  48. * @access public
  49. *
  50. * @return string Module name.
  51. */
  52. public function get_name() {
  53. return 'page-templates';
  54. }
  55. /**
  56. * Template include.
  57. *
  58. * Update the path for the Elementor Canvas template.
  59. *
  60. * Fired by `template_include` filter.
  61. *
  62. * @since 2.0.0
  63. * @access public
  64. *
  65. * @param string $template The path of the template to include.
  66. *
  67. * @return string The path of the template to include.
  68. */
  69. public function template_include( $template ) {
  70. if ( is_singular() ) {
  71. $document = Plugin::$instance->documents->get_doc_for_frontend( get_the_ID() );
  72. if ( $document ) {
  73. $template_path = $this->get_template_path( $document->get_meta( '_wp_page_template' ) );
  74. if ( $template_path ) {
  75. $template = $template_path;
  76. Plugin::$instance->inspector->add_log( 'Page Template', Plugin::$instance->inspector->parse_template_path( $template ), $document->get_edit_url() );
  77. }
  78. }
  79. }
  80. return $template;
  81. }
  82. /**
  83. * Add WordPress templates.
  84. *
  85. * Adds Elementor templates to all the post types that support
  86. * Elementor.
  87. *
  88. * Fired by `init` action.
  89. *
  90. * @since 2.0.0
  91. * @access public
  92. */
  93. public function add_wp_templates_support() {
  94. $post_types = get_post_types_by_support( 'elementor' );
  95. foreach ( $post_types as $post_type ) {
  96. add_filter( "theme_{$post_type}_templates", [ $this, 'add_page_templates' ], 10, 4 );
  97. }
  98. }
  99. /**
  100. * Add page templates.
  101. *
  102. * Add the Elementor page templates to the theme templates.
  103. *
  104. * Fired by `theme_{$post_type}_templates` filter.
  105. *
  106. * @since 2.0.0
  107. * @access public
  108. * @static
  109. *
  110. * @param array $page_templates Array of page templates. Keys are filenames,
  111. * checks are translated names.
  112. *
  113. * @param \WP_Theme $wp_theme
  114. * @param \WP_Post $post
  115. *
  116. * @return array Page templates.
  117. */
  118. public function add_page_templates( $page_templates, $wp_theme, $post ) {
  119. if ( $post ) {
  120. // FIX ME: Gutenberg not send $post as WP_Post object, just the post ID.
  121. $post_id = ! empty( $post->ID ) ? $post->ID : $post;
  122. $document = Plugin::$instance->documents->get( $post_id );
  123. if ( $document && ! $document::get_property( 'support_wp_page_templates' ) ) {
  124. return $page_templates;
  125. }
  126. }
  127. $page_templates = [
  128. self::TEMPLATE_CANVAS => _x( 'Elementor Canvas', 'Page Template', 'elementor' ),
  129. self::TEMPLATE_HEADER_FOOTER => _x( 'Elementor Full Width', 'Page Template', 'elementor' ),
  130. ] + $page_templates;
  131. return $page_templates;
  132. }
  133. /**
  134. * Set print callback.
  135. *
  136. * Set the page template callback.
  137. *
  138. * @since 2.0.0
  139. * @access public
  140. *
  141. * @param callable $callback
  142. */
  143. public function set_print_callback( $callback ) {
  144. $this->print_callback = $callback;
  145. }
  146. /**
  147. * Print callback.
  148. *
  149. * Prints the page template content using WordPress loop.
  150. *
  151. * @since 2.0.0
  152. * @access public
  153. */
  154. public function print_callback() {
  155. while ( have_posts() ) :
  156. the_post();
  157. the_content();
  158. endwhile;
  159. }
  160. /**
  161. * Print content.
  162. *
  163. * Prints the page template content.
  164. *
  165. * @since 2.0.0
  166. * @access public
  167. */
  168. public function print_content() {
  169. if ( ! $this->print_callback ) {
  170. $this->print_callback = [ $this, 'print_callback' ];
  171. }
  172. call_user_func( $this->print_callback );
  173. }
  174. /**
  175. * Get page template path.
  176. *
  177. * Retrieve the path for any given page template.
  178. *
  179. * @since 2.0.0
  180. * @access public
  181. *
  182. * @param string $page_template The page template name.
  183. *
  184. * @return string Page template path.
  185. */
  186. public function get_template_path( $page_template ) {
  187. $template_path = '';
  188. switch ( $page_template ) {
  189. case self::TEMPLATE_CANVAS:
  190. $template_path = __DIR__ . '/templates/canvas.php';
  191. break;
  192. case self::TEMPLATE_HEADER_FOOTER:
  193. $template_path = __DIR__ . '/templates/header-footer.php';
  194. break;
  195. }
  196. return $template_path;
  197. }
  198. /**
  199. * Register template control.
  200. *
  201. * Adds custom controls to any given document.
  202. *
  203. * Fired by `update_post_metadata` action.
  204. *
  205. * @since 2.0.0
  206. * @access public
  207. *
  208. * @param Document $document The document instance.
  209. */
  210. public function action_register_template_control( $document ) {
  211. if ( $document instanceof PostDocument || $document instanceof PageDocument ) {
  212. $this->register_template_control( $document );
  213. }
  214. }
  215. /**
  216. * Register template control.
  217. *
  218. * Adds custom controls to any given document.
  219. *
  220. * @since 2.0.0
  221. * @access public
  222. *
  223. * @param Document $document The document instance.
  224. * @param string $control_id Optional. The control ID. Default is `template`.
  225. */
  226. public function register_template_control( $document, $control_id = 'template' ) {
  227. if ( ! Utils::is_cpt_custom_templates_supported() ) {
  228. return;
  229. }
  230. require_once ABSPATH . '/wp-admin/includes/template.php';
  231. $options = [
  232. 'default' => __( 'Default', 'elementor' ),
  233. ];
  234. $options += array_flip( get_page_templates( null, $document->get_main_post()->post_type ) );
  235. $document->start_injection( [
  236. 'of' => 'post_status',
  237. 'fallback' => [
  238. 'of' => 'post_title',
  239. ],
  240. ] );
  241. $document->add_control(
  242. $control_id,
  243. [
  244. 'label' => __( 'Page Layout', 'elementor' ),
  245. 'type' => Controls_Manager::SELECT,
  246. 'default' => 'default',
  247. 'options' => $options,
  248. ]
  249. );
  250. $document->add_control(
  251. $control_id . '_default_description',
  252. [
  253. 'type' => Controls_Manager::RAW_HTML,
  254. 'raw' => __( 'Default Page Template from your theme', 'elementor' ),
  255. 'separator' => 'none',
  256. 'content_classes' => 'elementor-descriptor',
  257. 'condition' => [
  258. $control_id => 'default',
  259. ],
  260. ]
  261. );
  262. $document->add_control(
  263. $control_id . '_canvas_description',
  264. [
  265. 'type' => Controls_Manager::RAW_HTML,
  266. 'raw' => __( 'No header, no footer, just Elementor', 'elementor' ),
  267. 'separator' => 'none',
  268. 'content_classes' => 'elementor-descriptor',
  269. 'condition' => [
  270. $control_id => self::TEMPLATE_CANVAS,
  271. ],
  272. ]
  273. );
  274. $document->add_control(
  275. $control_id . '_header_footer_description',
  276. [
  277. 'type' => Controls_Manager::RAW_HTML,
  278. 'raw' => __( 'This template includes the header, full-width content and footer', 'elementor' ),
  279. 'separator' => 'none',
  280. 'content_classes' => 'elementor-descriptor',
  281. 'condition' => [
  282. $control_id => self::TEMPLATE_HEADER_FOOTER,
  283. ],
  284. ]
  285. );
  286. $document->end_injection();
  287. }
  288. /**
  289. * Filter metadata update.
  290. *
  291. * Filters whether to update metadata of a specific type.
  292. *
  293. * Elementor don't allow WordPress to update the parent page template
  294. * during `wp_update_post`.
  295. *
  296. * Fired by `update_{$meta_type}_metadata` filter.
  297. *
  298. * @since 2.0.0
  299. * @access public
  300. *
  301. * @param bool $check Whether to allow updating metadata for the given type.
  302. * @param int $object_id Object ID.
  303. * @param string $meta_key Meta key.
  304. *
  305. * @return bool Whether to allow updating metadata of a specific type.
  306. */
  307. public function filter_update_meta( $check, $object_id, $meta_key ) {
  308. if ( '_wp_page_template' === $meta_key ) {
  309. $ajax_data = Plugin::$instance->ajax->get_current_action_data();
  310. $is_autosave_action = $ajax_data && 'save_builder' === $ajax_data['action'] && DB::STATUS_AUTOSAVE === $ajax_data['data']['status'];
  311. // Don't allow WP to update the parent page template.
  312. // (during `wp_update_post` from page-settings or save_plain_text).
  313. if ( $is_autosave_action && ! wp_is_post_autosave( $object_id ) && DB::STATUS_DRAFT !== get_post_status( $object_id ) ) {
  314. $check = false;
  315. }
  316. }
  317. return $check;
  318. }
  319. /**
  320. * Page templates module constructor.
  321. *
  322. * Initializing Elementor page templates module.
  323. *
  324. * @since 2.0.0
  325. * @access public
  326. */
  327. public function __construct() {
  328. add_action( 'init', [ $this, 'add_wp_templates_support' ] );
  329. add_filter( 'template_include', [ $this, 'template_include' ], 11 /* After Plugins/WooCommerce */ );
  330. add_action( 'elementor/documents/register_controls', [ $this, 'action_register_template_control' ] );
  331. add_filter( 'update_post_metadata', [ $this, 'filter_update_meta' ], 10, 3 );
  332. }
  333. }