PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.6.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.6.2
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 4.6.2, at includes/Editors/BlockEditor/TemplatesController.php

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