PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.5.3
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.5.3
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Editors / BlockEditor / TemplatesController.php

TemplatesController.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 3.5.3, at includes/Editors/BlockEditor/TemplatesController.php

324 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Editors\BlockEditor;
4 use WPDeveloper\BetterDocs\Utils\Base;
5 use WPDeveloper\BetterDocs\Utils\BlockTemplate;
6
7 /**
8 * BlockTypesController class.
9 *
10 * @internal
11 */
12 class TemplatesController extends Base {
13 protected $blockTemplate;
14
15 /**
16 * Constructor.
17 */
18 public function __construct( BlockTemplate $blockTemplate ) {
19 $this->blockTemplate = $blockTemplate;
20 $this->init();
21 }
22
23 /**
24 * Initialization method.
25 */
26 protected function init() {
27 if (! betterdocs()->helper->current_theme_is_fse_theme() ) {
28 return;
29 }
30 add_filter( 'pre_get_block_template', [ $this, 'get_block_template_fallback' ], 10, 3 );
31 add_filter( 'pre_get_block_file_template', [ $this, 'get_block_file_template' ], 10, 3 );
32 add_filter( 'get_block_templates', [ $this, 'add_block_templates' ], 10, 3 );
33 add_filter( 'taxonomy_template_hierarchy', [ $this, 'add_doc_archive_to_eligible_for_fallback_templates' ], 10, 1 );
34 }
35
36 /**
37 * This function is used on the `pre_get_block_template` hook to return the fallback template from the db in case
38 * the template is eligible for it.
39 *
40 * @param \WP_Block_Template|null $template Block template object to short-circuit the default query,
41 * or null to allow WP to run its normal queries.
42 * @param string $id Template unique identifier (example: theme_slug//template_slug).
43 * @param string $template_type wp_template or wp_template_part.
44 *
45 * @return object|null
46 */
47 public function get_block_template_fallback( $template, $id, $template_type ) {
48 $template_name_parts = explode( '//', $id );
49 list( $theme, $slug ) = $template_name_parts;
50
51 if ( ! $this->blockTemplate->template_is_eligible_for_docs_archive_fallback( $slug ) ) {
52 return null;
53 }
54
55 $wp_query_args = [
56 'post_name__in' => [ 'archive-docs', $slug ],
57 'post_type' => $template_type,
58 'post_status' => [ 'auto-draft', 'draft', 'publish', 'trash' ],
59 'no_found_rows' => true,
60 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
61 [
62 'taxonomy' => 'wp_theme',
63 'field' => 'name',
64 'terms' => $theme
65 ]
66 ]
67 ];
68 $template_query = new \WP_Query( $wp_query_args );
69 $posts = $template_query->posts;
70
71 // If we have more than one result from the query, it means that the current template is present in the db (has
72 // been customized by the user) and we should not return the `archive-docs` template.
73 if ( count( $posts ) > 1 ) {
74 return null;
75 }
76
77 if ( count( $posts ) > 0 ) {
78 $template = _build_block_template_result_from_post( $posts[0] );
79
80 if ( ! is_wp_error( $template ) ) {
81 $template->id = $theme . '//' . $slug;
82 $template->slug = $slug;
83 $template->title = $this->blockTemplate->get_block_template_title( $slug );
84 $template->description = $this->blockTemplate->get_block_template_description( $slug );
85 unset( $template->source );
86
87 return $template;
88 }
89 }
90 return $template;
91 }
92
93 /**
94 * Adds the `archive-docs` template to the `taxonomy-doc_category`, `taxonomy-doc_tag`
95 * templates to be able to fall back to it.
96 *
97 * @param array $template_hierarchy A list of template candidates, in descending order of priority.
98 */
99 public function add_doc_archive_to_eligible_for_fallback_templates( $template_hierarchy ) {
100 $template_slugs = array_map(
101 '_strip_template_file_suffix',
102 $template_hierarchy
103 );
104
105 $templates_eligible_for_fallback = array_filter(
106 $template_slugs,
107 [ $this->blockTemplate, 'template_is_eligible_for_docs_archive_fallback' ]
108 );
109
110 if ( count( $templates_eligible_for_fallback ) > 0 ) {
111 $template_hierarchy[] = 'archive-docs';
112 }
113
114 return $template_hierarchy;
115 }
116
117 /**
118 * This function checks if there's a block template file in `betterdocs/includes/blocks/templates/`
119 * to return to pre_get_posts short-circuiting the query in Gutenberg.
120 *
121 * @param \WP_Block_Template|null $template Return a block template object to short-circuit the default query,
122 * or null to allow WP to run its normal queries.
123 * @param string $id Template unique identifier (example: theme_slug//template_slug).
124 * @param string $template_type wp_template or wp_template_part.
125 *
126 * @return mixed|\WP_Block_Template|\WP_Error
127 */
128 public function get_block_file_template( $template, $id, $template_type ) {
129
130 $template_name_parts = explode( '//', $id );
131
132 if ( count( $template_name_parts ) < 2 ) {
133 return $template;
134 }
135
136 list( $template_id, $template_slug ) = $template_name_parts;
137
138 // If we are not dealing with a BetterDocs template let's return early and let it continue through the process.
139 if ( BlockTemplate::PLUGIN_SLUG !== $template_id ) {
140 return $template;
141 }
142
143 // If we don't have a template let Gutenberg do its thing.
144 if ( ! $this->block_template_is_available( $template_slug, $template_type ) ) {
145 return $template;
146 }
147
148 $directory = $this->blockTemplate->get_templates_directory( $template_type );
149
150 $template_file_path = $directory . '/' . $template_slug . '.html';
151
152 $template_object = $this->blockTemplate->create_new_block_template_object( $template_file_path, $template_type, $template_slug );
153
154 $template_built = $this->blockTemplate->build_template_result_from_file( $template_object, $template_type );
155
156 if ( null !== $template_built ) {
157 return $template_built;
158 }
159
160 // Hand back over to Gutenberg if we can't find a template.
161 return $template;
162 }
163
164 /**
165 * Add the block template objects to be used.
166 *
167 * @param array $query_result Array of template objects.
168 * @param array $query Optional. Arguments to retrieve templates.
169 * @param string $template_type wp_template or wp_template_part.
170 * @return array
171 */
172 public function add_block_templates( $query_result, $query, $template_type ) {
173 if ( ! $this->blockTemplate->supports_block_templates() ) {
174 return $query_result;
175 }
176
177 $post_type = isset( $query['post_type'] ) ? $query['post_type'] : '';
178 $slugs = isset( $query['slug__in'] ) ? $query['slug__in'] : [];
179
180 $template_files = $this->get_block_templates( $slugs, $template_type );
181
182 // @todo: Add apply_filters to _gutenberg_get_template_files() in Gutenberg to prevent duplication of logic.
183 foreach ( $template_files as $template_file ) {
184 // If we have a template which is eligible for a fallback, we need to explicitly tell Gutenberg that
185 // it has a theme file (because it is using the fallback template file). And then `continue` to avoid
186 // adding duplicates.
187 if ( $this->blockTemplate->set_has_theme_file_if_fallback_is_available( $query_result, $template_file ) ) {
188 continue;
189 }
190
191 // If the current $post_type is set (e.g. on an Edit Post screen), and isn't included in the available post_types
192 // on the template file, then lets skip it so that it doesn't get added. This is typically used to hide templates
193 // in the template dropdown on the Edit Post page.
194 if ( $post_type &&
195 isset( $template_file->post_types ) &&
196 ! in_array( $post_type, $template_file->post_types, true )
197 ) {
198 continue;
199 }
200
201 // It would be custom if the template was modified in the editor, so if it's not custom we can load it from
202 // the filesystem.
203 if ( 'custom' !== $template_file->source ) {
204 $template = $this->blockTemplate->build_template_result_from_file( $template_file, $template_type );
205 } else {
206 $template_file->title = $this->blockTemplate->get_block_template_title( $template_file->slug );
207 $template_file->description = $this->blockTemplate->get_block_template_description( $template_file->slug );
208 $query_result[] = $template_file;
209 continue;
210 }
211
212 $is_not_custom = false === array_search(
213 wp_get_theme()->get_stylesheet() . '//' . $template_file->slug,
214 array_column( $query_result, 'id' ),
215 true
216 );
217 $fits_slug_query =
218 ! isset( $query['slug__in'] ) || in_array( $template_file->slug, $query['slug__in'], true );
219 $fits_area_query =
220 ! isset( $query['area'] ) || $template_file->area === $query['area'];
221 $should_include = $is_not_custom && $fits_slug_query && $fits_area_query;
222 if ( $should_include ) {
223 $query_result[] = $template;
224 }
225 }
226
227 // We need to remove theme (i.e. filesystem) templates that have the same slug as a customised one.
228 // This only affects saved templates that were saved BEFORE a theme template with the same slug was added.
229 $query_result = BlockTemplate::remove_theme_templates_with_custom_alternative( $query_result );
230
231 /**
232 * WC templates from theme aren't included in `$this->get_block_templates()` but are handled by Gutenberg.
233 * We need to do additional search through all templates file to update title and description for WC
234 * templates that aren't listed in theme.json.
235 */
236 $query_result = array_map(
237 function ( $template ) {
238 if ( 'theme' === $template->origin ) {
239 return $template;
240 }
241 if ( $template->title === $template->slug ) {
242 $template->title = $this->blockTemplate->get_block_template_title( $template->slug );
243 }
244 if ( ! $template->description ) {
245 $template->description = $this->blockTemplate->get_block_template_description( $template->slug );
246 }
247 return $template;
248 },
249 $query_result
250 );
251
252 return $query_result;
253 }
254
255 /**
256 * Gets the templates saved in the database.
257 *
258 * @param array $slugs An array of slugs to retrieve templates for.
259 * @param string $template_type wp_template or wp_template_part.
260 *
261 * @return int[]|\WP_Post[] An array of found templates.
262 */
263 public function get_block_templates_from_db( $slugs = [], $template_type = 'wp_template' ) {
264 $check_query_args = [
265 'post_type' => $template_type,
266 'posts_per_page' => -1,
267 'no_found_rows' => true,
268 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
269 [
270 'taxonomy' => 'wp_theme',
271 'field' => 'name',
272 'terms' => [ BlockTemplate::PLUGIN_SLUG, get_stylesheet() ]
273 ]
274 ]
275 ];
276
277 if ( is_array( $slugs ) && count( $slugs ) > 0 ) {
278 $check_query_args['post_name__in'] = $slugs;
279 }
280
281 $check_query = new \WP_Query( $check_query_args );
282 $saved_betterdocs_templates = $check_query->posts;
283
284 return array_map(
285 function ( $saved_betterdocs_template ) {
286 return $this->blockTemplate->build_template_result_from_post( $saved_betterdocs_template );
287 },
288 $saved_betterdocs_templates
289 );
290 }
291
292 /**
293 * Get and build the block template objects from the block template files.
294 *
295 * @param array $slugs An array of slugs to retrieve templates for.
296 * @param string $template_type wp_template or wp_template_part.
297 *
298 * @return array WP_Block_Template[] An array of block template objects.
299 */
300 public function get_block_templates( $slugs = [], $template_type = 'wp_template' ) {
301 $templates_from_db = $this->get_block_templates_from_db( $slugs, $template_type );
302 $templates_from_betterdocs = $this->blockTemplate->get_block_templates_from_betterdocs( $slugs, $templates_from_db, $template_type );
303 $templates = array_merge( $templates_from_db, $templates_from_betterdocs );
304 return $templates;
305 }
306
307 /**
308 * Checks whether a block template with that name exists in BetterDocs Blocks
309 *
310 * @param string $template_name Template to check.
311 * @param string $template_type wp_template or wp_template_part.
312 *
313 * @return boolean
314 */
315 public function block_template_is_available( $template_name, $template_type = 'wp_template' ) {
316 if ( ! $template_name ) {
317 return false;
318 }
319 $directory = $this->blockTemplate->get_templates_directory( $template_type ) . '/' . $template_name . '.html';
320
321 return is_readable( $directory ) || $this->get_block_templates( [ $template_name ], $template_type );
322 }
323 }
324