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

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