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

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