PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / trunk
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! vtrunk
3.8.0 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 All 112 releases
templately / modules / theme-builder / Managers / TemplateManager.php

TemplateManager.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! trunk, at modules/theme-builder/Managers/TemplateManager.php

307 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Templately\Modules\ThemeBuilder\Managers;
4
5 use Templately\Modules\ThemeBuilder\PageTemplates;
6 use Templately\Modules\ThemeBuilder\Source;
7 use Templately\Modules\ThemeBuilder\Types\Archive;
8 use Templately\Modules\ThemeBuilder\Types\BaseTemplate;
9 use Templately\Modules\ThemeBuilder\Types\CourseArchive;
10 use Templately\Modules\ThemeBuilder\Types\CourseSingle;
11 use Templately\Modules\ThemeBuilder\Types\DocsArchive;
12 use Templately\Modules\ThemeBuilder\Types\DocsSingle;
13 use Templately\Modules\ThemeBuilder\Types\Error;
14 use Templately\Modules\ThemeBuilder\Types\FluentProductSingle;
15 use Templately\Modules\ThemeBuilder\Types\Footer;
16 use Templately\Modules\ThemeBuilder\Types\Header;
17 use Templately\Modules\ThemeBuilder\Types\Page;
18 use Templately\Modules\ThemeBuilder\Types\PageSingle;
19 use Templately\Modules\ThemeBuilder\Types\Post;
20 use Templately\Modules\ThemeBuilder\Types\ProductArchive;
21 use Templately\Modules\ThemeBuilder\Types\ProductSingle;
22 use Templately\Modules\ThemeBuilder\Types\Search;
23 use Templately\Modules\ThemeBuilder\Types\Single;
24 use WP_Error;
25
26 class TemplateManager {
27 private $types = [];
28 private $documents = [];
29
30 public function __construct( $builder ) {
31 $this->register_types();
32 }
33
34 public function register_types() {
35 $types = [
36 'header' => Header::class,
37 'footer' => Footer::class,
38 'single' => Single::class,
39 'archive' => Archive::class,
40 'post' => Post::class,
41 'page' => Page::class,
42 'error' => Error::class,
43
44 // WooCommerce Template Types
45 'product_single' => ProductSingle::class,
46 'fluent_product_single' => FluentProductSingle::class,
47 'product_archive' => ProductArchive::class,
48
49 'page_single' => PageSingle::class,
50
51 // LMS (LearnDash)
52 'course_archive' => CourseArchive::class,
53 'course_single' => CourseSingle::class,
54
55 // BetterDocs
56 'docs_single' => DocsSingle::class,
57 'docs_archive' => DocsArchive::class,
58
59 // Search results
60 'search' => Search::class,
61 ];
62
63 $this->types = $types;
64 }
65
66 /**
67 * @param $type
68 * @param $post_data
69 * @param array $meta
70 *
71 * @return BaseTemplate|WP_Error
72 */
73 public function create( $type, $post_data, array $meta = [] ) {
74 /**
75 * @var BaseTemplate $class ;
76 */
77 $class = $this->get_template_type( $type );
78
79 $should_update_title = false;
80
81 if ( empty( $post_data['post_title'] ) ) {
82 $post_data['post_title'] = ucwords( $type ) . ' Template';
83
84 $should_update_title = true;
85 }
86
87 $meta = wp_parse_args( $meta, [
88 Source::TYPE_META_KEY => $type,
89 Source::PLATFORM_META_KEY => $meta['platform'] ?? 'gutenberg'
90 ] );
91
92 if ( isset( $meta['platform'] ) ) {
93 unset( $meta['platform'] );
94 }
95
96 if ( $meta[ Source::PLATFORM_META_KEY ] == 'elementor' ) {
97 $meta['_wp_page_template'] = 'elementor_header_footer';
98 } elseif ( $meta[ Source::PLATFORM_META_KEY ] == 'gutenberg' ) {
99 $meta['_wp_page_template'] = PageTemplates::TEMPLATE_HEADER_FOOTER;
100 }
101
102 $post_data['meta_input'] = $meta;
103
104 $post_type = $class::get_property( 'cpt' );
105 if ( ! empty( $post_type ) && empty( $post_data['post_type'] ) ) {
106 $post_data['post_type'] = $post_type;
107 }
108
109 $post_id = wp_insert_post( $post_data );
110
111 if ( is_wp_error( $post_id ) ) {
112 return $post_id;
113 }
114
115 if ( $should_update_title ) {
116 $post_data['ID'] = $post_id;
117 $post_data['post_title'] .= " #{$post_id} (by Templately)";
118 unset( $post_data['meta_input'] );
119
120 wp_update_post( $post_data );
121 }
122
123 // A template you just created — by importing a pack, or by hand — is the one you
124 // mean to use. Without this the tie-break falls to "lowest post ID", so a second
125 // site-wide header of the same type would be shadowed by the first one ever
126 // imported and never render.
127 $this->set_active( $post_id, $type );
128
129 return new $class( [
130 'post_id' => $post_id
131 ] );
132 }
133
134 /**
135 * Make `$post_id` the active template for its type, and the only one.
136 *
137 * Scoped per TYPE, not per location or condition set: "active" answers "which header",
138 * not "which header here" — conditions already answer the second and keep overriding
139 * this. Clearing the flag from a sibling therefore never stops it rendering where its
140 * conditions are more specific; it only gives up the tie.
141 *
142 * @param int $post_id Template to activate.
143 * @param string $type Its `_templately_template_type`; read from the post when omitted.
144 * @return void
145 */
146 public function set_active( int $post_id, string $type = '' ) {
147 if ( ! $post_id ) {
148 return;
149 }
150
151 if ( '' === $type ) {
152 $type = (string) get_post_meta( $post_id, Source::TYPE_META_KEY, true );
153 }
154
155 if ( '' === $type ) {
156 return;
157 }
158
159 $siblings = get_posts( [
160 'post_type' => Source::CPT,
161 'post_status' => 'any',
162 'numberposts' => -1,
163 'fields' => 'ids',
164 'suppress_filters' => false,
165 'meta_query' => [
166 [ 'key' => Source::TYPE_META_KEY, 'value' => $type ],
167 [ 'key' => Source::ACTIVE_META_KEY, 'value' => '1' ],
168 ],
169 ] );
170
171 foreach ( $siblings as $sibling_id ) {
172 if ( (int) $sibling_id !== $post_id ) {
173 delete_post_meta( (int) $sibling_id, Source::ACTIVE_META_KEY );
174 }
175 }
176
177 update_post_meta( $post_id, Source::ACTIVE_META_KEY, '1' );
178
179 self::$active_id_cache = [];
180 }
181
182 /**
183 * `get_active_id()` results, per type. Invalidated by `set_active()` — a stale entry
184 * would badge the previous winner on the very screen the user just changed.
185 *
186 * @var array<string,int>
187 */
188 private static $active_id_cache = [];
189
190 /** Whether `$post_id` carries the active flag. */
191 public static function is_active( int $post_id ): bool {
192 return '1' === (string) get_post_meta( $post_id, Source::ACTIVE_META_KEY, true );
193 }
194
195 /**
196 * The template of `$type` that WINS a tie — flagged if one is, else most recently
197 * modified. Mirrors `ConditionManager::sort_by_priority()`'s tie-break, minus the
198 * post-ID step, which only ever separates two templates saved in the same second.
199 *
200 * Reading the flag alone is not enough to tell a user what is happening: no site
201 * predating the flag has one set, so a list keyed on it shows a badge on NOTHING
202 * while two headers sit there and one of them is quietly winning. The badge has to
203 * answer "which of these am I getting", which is this — not "which was flagged".
204 *
205 * Conditions still override per request, so this is the DEFAULT for the type, not a
206 * promise about every URL.
207 *
208 * @param string $type
209 * @return int Template id, or 0 when the type has none.
210 */
211 public static function get_active_id( string $type ): int {
212 if ( '' === $type ) {
213 return 0;
214 }
215
216 if ( isset( self::$active_id_cache[ $type ] ) ) {
217 return self::$active_id_cache[ $type ];
218 }
219
220 $flagged = get_posts( [
221 'post_type' => Source::CPT,
222 'post_status' => 'any',
223 'numberposts' => 1,
224 'fields' => 'ids',
225 'suppress_filters' => false,
226 'meta_query' => [
227 [ 'key' => Source::TYPE_META_KEY, 'value' => $type ],
228 [ 'key' => Source::ACTIVE_META_KEY, 'value' => '1' ],
229 ],
230 ] );
231
232 if ( ! empty( $flagged ) ) {
233 return self::$active_id_cache[ $type ] = (int) $flagged[0];
234 }
235
236 $recent = get_posts( [
237 'post_type' => Source::CPT,
238 'post_status' => 'any',
239 'numberposts' => 1,
240 'fields' => 'ids',
241 'orderby' => [ 'modified' => 'DESC', 'ID' => 'ASC' ],
242 'suppress_filters' => false,
243 'meta_query' => [
244 [ 'key' => Source::TYPE_META_KEY, 'value' => $type ],
245 ],
246 ] );
247
248 return self::$active_id_cache[ $type ] = ! empty( $recent ) ? (int) $recent[0] : 0;
249 }
250
251 private function get_template_type( $type, $default = 'post' ): string {
252 if ( isset( $this->types[ $type ] ) ) {
253 return $this->types[ $type ];
254 }
255
256 return $this->types[ $default ];
257 }
258
259 /**
260 *
261 * @param int $post_id
262 * @param bool $from_cache
263 *
264 * @return ThemeTemplate||false
265 */
266 public function get( int $post_id, $from_cache = true ) {
267 $this->register_types();
268
269 $post_id = absint( $post_id );
270
271 if ( ! $post_id || ! get_post( $post_id ) ) {
272 return false;
273 }
274
275 if ( ! $from_cache || ! isset( $this->documents[ $post_id ] ) ) {
276 $doc_type = $this->get_template_type_by_id( $post_id );
277 $doc_type_class = $this->get_template_type( $doc_type );
278
279 $this->documents[ $post_id ] = new $doc_type_class( [
280 'post_id' => $post_id,
281 ] );
282 }
283
284 return $this->documents[ $post_id ];
285 }
286
287 private function get_template_type_by_id( int $post_id ) {
288 // Auto-save inherits from the original post.
289 if ( wp_is_post_autosave( $post_id ) ) {
290 $post_id = wp_get_post_parent_id( $post_id );
291 }
292
293 $template_type = get_post_meta( $post_id, Source::TYPE_META_KEY, true );
294
295 if ( $template_type && isset( $this->types[ $template_type ] ) ) {
296 return $template_type;
297 }
298
299 return 'post';
300 }
301
302 public function get_template_types(): array {
303 $this->register_types();
304
305 return $this->types;
306 }
307 }