PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.5.3
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.5.3
3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.10 All 111 releases
templately / includes / Builder / Managers / LocationManager.php

LocationManager.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! 3.5.3, at includes/Builder/Managers/LocationManager.php

604 lines 17.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Templately\Builder\Managers;
4
5 use EbStyleHandler;
6 use EssentialBlocks\Modules\StyleHandler;
7 use Templately\Builder\PageTemplates;
8 use Templately\Builder\Source;
9 use Templately\Builder\ThemeBuilder;
10 use Templately\Builder\Types\BaseTemplate;
11 use Templately\Builder\Types\ThemeTemplate;
12 use ElementorPro\Modules\ThemeBuilder\Module;
13 use Elementor\Core\Files\CSS\Post as Post_CSS;
14 use Elementor\Plugin;
15 use Templately\Builder\TemplateLoader;
16 use Elementor\Core\Base\Elements_Iteration_Actions\Assets as Assets_Iteration_Action;
17
18 class LocationManager {
19 /**
20 * @var array<string, ThemeTemplate>
21 */
22 public $locations_queue = [];
23 public $locations_skipped = [];
24 public $locations_printed = [];
25 public $did_locations = [];
26
27 protected $locations = [];
28
29 /**
30 * @var ThemeBuilder
31 */
32 protected $builder;
33
34 public function __construct( $builder ) {
35 $this->builder = $builder;
36
37
38 /**
39 * Priority is 13,
40 * Because it should be run after elementor & woocommerce
41 */
42 add_filter( 'template_include', [ $this, 'template_include' ], 13 );
43
44 /**
45 * Priority is 7,
46 * Because it should run before elementor
47 */
48 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_elementor_styles' ], 7 );
49 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_template_assets' ], 8 );
50 add_action( 'wp_enqueue_scripts', [ $this, 'preload_gutenberg_template_styles' ], 9 );
51
52 // Cache clearing hooks
53 add_action( 'save_post', [ $this, 'clear_header_styles_cache' ] );
54 add_action( 'delete_post', [ $this, 'clear_header_styles_cache' ] );
55 add_action( 'trash_post', [ $this, 'clear_header_styles_cache' ] );
56 }
57
58 private function set_locations() {
59 if(!empty($this->locations)) {
60 return;
61 }
62 $this->locations = [
63 'header' => [
64 'label' => __( 'Header', 'templately' ),
65 'multiple' => false
66 ],
67 'footer' => [
68 'label' => __( 'Footer', 'templately' ),
69 'multiple' => false
70 ],
71 'archive' => [
72 'label' => __( 'Archive', 'templately' ),
73 'multiple' => false
74 ],
75 'single' => [
76 'label' => __( 'Single', 'templately' ),
77 'multiple' => false
78 ]
79 ];
80 }
81
82 public function template_include( $template_path ) {
83 $location = '';
84 $page_template_module = $this->get_template_module();
85
86 /**
87 * Return if it is Elementor Template.
88 * This should be elementor's responsibility.
89 */
90
91 if ( get_post_type( get_the_ID() ) === 'elementor_library' ) {
92 return $template_path;
93 }
94 else if( $this->get_platform(get_the_ID()) === 'elementor' && !class_exists('Elementor\Plugin') ) {
95 return $template_path;
96 }
97
98 if ( is_singular() ) {
99 /**
100 * @var BaseTemplate $template
101 */
102 $template = $this->builder::$templates_manager->get( get_the_ID() );
103
104 if ( $template && $template->get_property( 'support_wp_page_templates' ) ) {
105 $page_template_module->set_platform( $template->get_platform() );
106 $wp_page_template = $template->get_meta( '_wp_page_template' );
107
108 $_custom_template_path = $page_template_module->get_template_path( $wp_page_template );
109
110
111 if ( empty( $_custom_template_path ) ) {
112 $location = 'single';
113
114 $templates_for_location = $this->builder::$conditions_manager->get_templates_by_location( $location );
115
116 if ( empty( $templates_for_location ) ) {
117 return $template_path;
118 }
119
120 $template_id = key( $templates_for_location );
121
122 $template = $templates_for_location[ $template_id ];
123 $page_template = $template->get_meta( '_wp_page_template' );
124 $platform = $template->get_platform();
125
126 if( $platform === 'elementor' && !class_exists('Elementor\Plugin') ) {
127 return $template_path;
128 }
129 if ( ! empty( $platform ) ) {
130 $page_template_module->set_platform( $platform );
131 }
132 $path = $page_template_module->get_template_path( $page_template );
133 $page_template_module->set_print_callback( function () use ( $location ) {
134 $this->do_location( $location );
135 } );
136 set_query_var( 'using_templately_template', 1 );
137
138 return $path;
139 }
140
141 if ( $wp_page_template && $wp_page_template !== 'default' ) {
142 set_query_var( 'using_templately_template', 1 );
143
144 return $_custom_template_path;
145 }
146 }
147 } else {
148 $template = false;
149 }
150
151 if ( $template instanceof ThemeTemplate ) {
152 $location = $template->get_location();
153 } elseif ( function_exists( 'is_shop' ) && is_shop() ) {
154 $location = 'archive';
155 } elseif ( is_archive() || is_tax() || is_home() || is_search() ) {
156 $location = 'archive';
157 } elseif ( is_singular() || is_404() ) {
158 $location = 'single';
159 }
160
161 if ( $location ) {
162 $templates_for_location = $this->builder::$conditions_manager->get_templates_by_location( $location );
163
164 if ( empty( $templates_for_location ) ) {
165 set_query_var( 'using_templately_template', 1 );
166
167 return $template_path;
168 }
169
170 if ( 'single' === $location || 'archive' === $location ) {
171 $template_id = key( $templates_for_location );
172 $template = $templates_for_location[ $template_id ];
173 $document_page_template = $template->get_meta( '_wp_page_template' );
174 $platform = $template->get_platform();
175
176 if ( ! empty( $platform ) ) {
177 $page_template_module->set_platform( $platform );
178 }
179
180 if ( $document_page_template ) {
181 $page_template = $document_page_template;
182 }
183
184 if(!empty($template_id) && !empty($template) && $template->get_type() == "course_archive"){
185 // $__post = $GLOBALS['post'];
186 // learndash_course_grid_load_resources();
187 // $GLOBALS['post'] = $__post;
188 $GLOBALS['post'] = get_post($template_id);
189 }
190 }
191 }
192 $is_header_footer = 'header' === $location || 'footer' === $location;
193 if ( empty( $page_template ) && ! $is_header_footer ) {
194 $page_template = $page_template_module->get_header_footer_template();
195 }
196
197 if ( ! empty( $page_template ) ) {
198 $path = $page_template_module->get_template_path( $page_template );
199
200 if ( $path ) {
201 $page_template_module->set_print_callback( function () use ( $location ) {
202 $this->do_location( $location );
203 } );
204 set_query_var( 'using_templately_template', 1 );
205 $template_path = $path;
206 }
207 }
208
209 return $template_path;
210 }
211
212 private function get_platform($post_id) {
213 $post_type = get_post_type( get_the_ID() );
214 if ( $post_type == Source::CPT ) {
215 $platform = get_post_meta( $post_id, Source::PLATFORM_META_KEY, true );
216 } elseif ( get_post_meta( $post_id, '_elementor_edit_mode', true ) == 'builder' || $post_type == 'elementor_library' ) {
217 $platform = 'elementor';
218 } else {
219 $platform = 'gutenberg';
220 }
221 return $platform;
222 }
223
224 /**
225 * Get page templating modules and set platform if needed.
226 *
227 * @param string $platform
228 *
229 * @return PageTemplates
230 */
231 private function get_template_module( string $platform = '' ): PageTemplates {
232 $module = templately()->theme_builder::$page_template_module;
233
234 if ( ! empty( $platform ) ) {
235 $module = $module->set_platform( $platform );
236 }
237
238 return $module;
239 }
240
241 public function get_location( $location ) {
242 $locations = $this->get_locations();
243
244 return $locations[ $location ] ?? [];
245 }
246
247 public function get_locations(): array {
248 /**
249 * Don't know yet if we need it or not.
250 */
251 $this->set_locations();
252
253 $this->register_locations();
254
255 return $this->locations;
256 }
257
258 public function register_locations() {
259 if ( ! did_action( 'templately_locations' ) ) {
260 do_action( 'templately_locations', $this );
261 }
262 }
263
264 /**
265 * Getting the Idea From Elementor itself.
266 *
267 * @param $location
268 *
269 * @return bool
270 */
271 public function do_location( $location ): bool {
272 $templates_for_location = $this->builder::$conditions_manager->get_templates_by_location( $location );
273
274 foreach ( $templates_for_location as $template_id => $template ) {
275 $this->add_template_to_location( $location, $template_id );
276 }
277
278 if ( empty( $this->locations_queue[ $location ] ) ) {
279 return false;
280 }
281
282 while ( ! empty( $this->locations_queue[ $location ] ) ) {
283 $template_id = key( $this->locations_queue[ $location ] );
284 $template = $this->builder->get_template( $template_id );
285
286 if ( ! $template || $this->is_printed( $location, $template_id ) ) {
287 $this->skip_template_from_location( $location, $template_id );
288 continue;
289 }
290
291 if ( empty( $documents_by_conditions[ $template_id ] ) ) {
292 $post_status = get_post_status( $template_id );
293 if ( 'publish' !== $post_status ) {
294 $this->skip_template_from_location( $location, $template_id );
295 continue;
296 }
297 }
298
299 // Fire before printing to allow style preparation
300 do_action("templately_printed_location", $template_id, $location, $template);
301
302 $template->print_content();
303 $this->did_locations[] = $location;
304
305 $this->set_is_printed( $location, $template_id );
306 }
307
308 return true;
309 }
310
311 /**
312 * Enqueue CSS styles for Elementor-based templates.
313 *
314 * Uses Elementor's Post_CSS class to enqueue styles for all locations
315 * (header, footer, archive, single). Only runs for Elementor platform.
316 *
317 * @since 3.0.0
318 * @return void
319 */
320 public function enqueue_elementor_styles() {
321 if (
322 class_exists('ElementorPro\Modules\ThemeBuilder\Module') &&
323 Module::is_preview()
324 ) {
325 return;
326 }
327
328 $locations = $this->get_locations();
329
330 if ( empty( $locations ) ) {
331 return;
332 }
333
334 if(class_exists('Elementor\Core\Files\CSS\Post')){
335 $current_post_id = get_the_ID();
336
337 foreach ( $locations as $location => $settings ) {
338 $templates_for_location = $this->builder::$conditions_manager->get_templates_by_location( $location );
339
340 foreach ( $templates_for_location as $template ) {
341 if ( ! $template->is_elementor_template() ) {
342 continue;
343 }
344
345 $this->iterate_elements($template->get_main_id());
346
347 $post_id = $template->get_main_id();
348
349 // Don't enqueue current post here (let the preview/frontend components to handle it)
350 if ( $current_post_id !== $post_id ) {
351 $css_file = new Post_CSS( $post_id );
352 $css_file->enqueue();
353 }
354 }
355 }
356
357 }
358 }
359
360 private function iterate_elements( $post_id ) {
361 if ( ! class_exists( 'Elementor\Plugin' ) || ! class_exists( Assets_Iteration_Action::class ) ) {
362 return;
363 }
364
365 $plugin = Plugin::instance();
366 if ( ! $plugin || ! isset( $plugin->documents ) || ! isset( $plugin->db ) || ! isset( $plugin->elements_manager ) ) {
367 return;
368 }
369
370 $document = $plugin->documents->get( $post_id );
371 if ( ! $document ) {
372 return;
373 }
374
375 $elements = $document->get_elements_data();
376 if ( empty( $elements ) ) {
377 return;
378 }
379
380 $unique_page_elements = [];
381 $elements_iteration_action = new Assets_Iteration_Action( $document );
382 $elements_iteration_action->set_mode( 'render' );
383
384 $plugin->db->iterate_data( $elements, function( array $element_data ) use ( &$unique_page_elements, $elements_iteration_action, $plugin ) {
385 $element_type = 'widget' === $element_data['elType'] ? $element_data['widgetType'] : $element_data['elType'];
386
387 $element = $plugin->elements_manager->create_element_instance( $element_data );
388
389 if ( $element ) {
390 if ( ! in_array( $element_type, $unique_page_elements, true ) ) {
391 $unique_page_elements[] = $element_type;
392
393 $elements_iteration_action->unique_element_action( $element );
394 }
395
396 $elements_iteration_action->element_action( $element );
397 }
398
399 return $element_data;
400 } );
401
402 $elements_iteration_action->after_elements_iteration();
403 }
404
405 /**
406 * Fire action for Essential Blocks to write CSS files.
407 *
408 * Iterates all Gutenberg template locations and fires the
409 * templately_printed_location action.
410 *
411 * Note: This action only triggers Essential Blocks to generate the CSS file
412 * and add the template ID to its processing list. It does NOT enqueue the
413 * CSS file immediately. The actual enqueue happens later in Essential Blocks'
414 * `enqueue_frontend_assets` method (hooked to wp_enqueue_scripts at priority 10),
415 * which relies on this action having already fired (at priority 8).
416 *
417 * @since 3.0.0
418 * @return void
419 */
420 public function enqueue_template_assets() {
421 $using_templately_builder = get_query_var( 'using_templately_template' );
422 if ( ($using_templately_builder || TemplateLoader::is_header_footer()) && function_exists( 'templately' ) ) {
423 $template_locations = [ 'header', 'footer', 'archive', 'single' ];
424 foreach ( $template_locations as $location ) {
425 $template = templately()->theme_builder::$conditions_manager->get_templates_by_location( $location );
426 if ( empty( $template ) ) {
427 continue;
428 }
429 $template = array_pop( $template );
430 if ( $template->platform == 'gutenberg' ) {
431 $template = is_array( $template ) ? array_pop( $template ) : $template;
432 do_action("templately_printed_location", $template->get_main_id(), $location, $template);
433 }
434 }
435 }
436 }
437
438 /**
439 * Pre-parse Gutenberg template blocks to enqueue styles in head.
440 *
441 * Iterates all template locations, parses block content, and triggers
442 * block style registration BEFORE wp_head() outputs styles. This ensures
443 * all block styles are properly enqueued rather than printed inline.
444 *
445 * @since 3.0.0
446 * @return void
447 */
448 public function preload_gutenberg_template_styles() {
449 if ( ! function_exists( 'templately' ) || ! function_exists( 'parse_blocks' ) ) {
450 return;
451 }
452
453 $template_locations = [ 'header' ];
454
455 foreach ( $template_locations as $location ) {
456 $templates = templately()->theme_builder::$conditions_manager->get_templates_by_location( $location );
457
458 if ( empty( $templates ) ) {
459 continue;
460 }
461
462 foreach ( $templates as $template ) {
463 if ( $template->platform !== 'gutenberg' ) {
464 continue;
465 }
466
467 $post_id = $template->get_main_id();
468 $post = get_post( $post_id );
469
470 if ( ! $post ) {
471 continue;
472 }
473
474 // Check for cached styles
475 $transient_key = 'templately_header_styles_' . $post_id;
476 $style_handles = get_transient( $transient_key );
477
478 if ( false === $style_handles ) {
479 if ( empty( $post->post_content ) ) {
480 continue;
481 }
482
483 // Parse blocks and extract styles
484 $blocks = parse_blocks( $post->post_content );
485 $style_handles = [];
486 $this->get_block_styles_recursive( $blocks, $style_handles );
487
488 // Cache the handles for a long time (e.g., 1 month)
489 set_transient( $transient_key, $style_handles, MONTH_IN_SECONDS );
490 }
491
492 // Enqueue the styles
493 if ( ! empty( $style_handles ) ) {
494 foreach ( $style_handles as $handle ) {
495 wp_enqueue_style( $handle );
496 }
497 }
498 }
499 }
500 }
501
502 /**
503 * Recursively extract style handles for all blocks.
504 *
505 * @since 3.0.0
506 * @param array $blocks Parsed blocks array.
507 * @param array $handles Reference to array of handles to populate.
508 * @return void
509 */
510 private function get_block_styles_recursive( array $blocks, array &$handles ) {
511 if ( ! class_exists( 'WP_Block_Type_Registry' ) ) {
512 return;
513 }
514
515 foreach ( $blocks as $block ) {
516 if ( ! empty( $block['blockName'] ) ) {
517 // Get registered block type
518 $block_type = \WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
519
520 if ( $block_type ) {
521 // Enqueue style handles (WP 5.9+)
522 if ( ! empty( $block_type->style_handles ) ) {
523 foreach ( $block_type->style_handles as $handle ) {
524 if ( ! in_array( $handle, $handles ) ) {
525 $handles[] = $handle;
526 }
527 }
528 }
529 // Legacy style property support
530 if ( ! empty( $block_type->style ) ) {
531 $styles = is_array( $block_type->style ) ? $block_type->style : [ $block_type->style ];
532 foreach ( $styles as $style ) {
533 if ( ! in_array( $style, $handles ) ) {
534 $handles[] = $style;
535 }
536 }
537 }
538 }
539 }
540
541 // Process inner blocks recursively
542 if ( ! empty( $block['innerBlocks'] ) ) {
543 $this->get_block_styles_recursive( $block['innerBlocks'], $handles );
544 }
545 }
546 }
547
548
549 /**
550 * @param string $location
551 * @param integer $template_id
552 */
553 public function add_template_to_location( string $location, int $template_id ) {
554 if ( isset( $this->locations_skipped[ $location ][ $template_id ] ) ) {
555 return;
556 }
557
558 if ( ! isset( $this->locations_queue[ $location ] ) ) {
559 $this->locations_queue[ $location ] = [];
560 }
561
562 $this->locations_queue[ $location ][ $template_id ] = $template_id;
563 }
564
565 public function is_printed( $location, $template_id ): bool {
566 return isset( $this->locations_printed[ $location ][ $template_id ] );
567 }
568
569 public function skip_template_from_location( $location, $template_id ) {
570 $this->remove_template_from_location( $location, $template_id );
571
572 if ( ! isset( $this->locations_skipped[ $location ] ) ) {
573 $this->locations_skipped[ $location ] = [];
574 }
575
576 $this->locations_skipped[ $location ][ $template_id ] = $template_id;
577 }
578
579 public function remove_template_from_location( $location, $template_id ) {
580 unset( $this->locations_queue[ $location ][ $template_id ] );
581 }
582
583 public function set_is_printed( $location, $template_id ) {
584 if ( ! isset( $this->locations_printed[ $location ] ) ) {
585 $this->locations_printed[ $location ] = [];
586 }
587
588 $this->locations_printed[ $location ][ $template_id ] = $template_id;
589 $this->remove_template_from_location( $location, $template_id );
590 }
591
592 /**
593 * Clear the header styles cache for a specific post.
594 *
595 * @param int $post_id Post ID.
596 * @return void
597 */
598 public function clear_header_styles_cache( $post_id ) {
599 // Only clear if it might be a header template.
600 // Since we don't strictly know if it IS a header without checking metadata (which might be what's changing),
601 // we just clear the specific transient for this ID. It's harmless if the transient doesn't exist.
602 delete_transient( 'templately_header_styles_' . $post_id );
603 }
604 }