PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.5.2
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.5.2
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.2, at includes/Builder/Managers/LocationManager.php

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