PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.2
4.9.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 All 200 releases
← All changes | includes/Utils/BlockTemplate.php +489 -436 3.4.14.9.2 View file →
@@ -5,493 +5,546 @@
5 5 * Utility methods used for serving block templates from BetterDocs Blocks.
6 6 * {@internal This class and its methods should only be used within the class-block-template-controller.php and is not intended for public use.}
7 7 */
8 8 class BlockTemplate {
9 - const ELIGIBLE_FOR_DOC_ARCHIVE_FALLBACK = [ 'taxonomy-doc_category', 'taxonomy-doc_tag' ];
9 + const ELIGIBLE_FOR_DOC_ARCHIVE_FALLBACK = [ 'taxonomy-doc_category', 'taxonomy-doc_tag' ];
10 10
11 - /**
12 - * BetterDocs plugin slug
13 - *
14 - * This is used to save templates to the DB which are stored against this value in the wp_terms table.
15 - *
16 - * @var string
17 - */
18 - const PLUGIN_SLUG = 'betterdocs/betterdocs';
11 + /**
12 + * BetterDocs plugin slug
13 + *
14 + * This is used to save templates to the DB which are stored against this value in the wp_terms table.
15 + *
16 + * @var string
17 + */
18 + const PLUGIN_SLUG = 'betterdocs/betterdocs';
19 19
20 - /**
21 - * Gets the directory where templates of a specific template type can be found.
22 - *
23 - * @param string $template_type wp_template or wp_template_part.
24 - *
25 - * @return string
26 - */
27 - public function get_templates_directory( $template_type = 'wp_template' ) {
28 - return BETTERDOCS_FSE_TEMPLATES_PATH . DIRECTORY_SEPARATOR;
29 - }
20 + /**
21 + * Gets the directory where templates of a specific template type can be found.
22 + *
23 + * @param string $template_type wp_template or wp_template_part.
24 + *
25 + * @return string
26 + */
27 + public function get_templates_directory( $template_type = 'wp_template' ) {
28 + return BETTERDOCS_FSE_TEMPLATES_PATH . DIRECTORY_SEPARATOR;
29 + }
30 30
31 - /**
32 - * Returns an array containing the references of
33 - * the passed blocks and their inner blocks.
34 - *
35 - * @param array $blocks array of blocks.
36 - *
37 - * @return array block references to the passed blocks and their inner blocks.
38 - */
39 - public function flatten_blocks( &$blocks ) {
40 - $all_blocks = [];
41 - $queue = [];
42 - foreach ( $blocks as &$block ) {
43 - $queue[] = &$block;
44 - }
45 - $queue_count = count( $queue );
31 + /**
32 + * Returns an array containing the references of
33 + * the passed blocks and their inner blocks.
34 + *
35 + * @param array $blocks array of blocks.
36 + *
37 + * @return array block references to the passed blocks and their inner blocks.
38 + */
39 + public function flatten_blocks( &$blocks ) {
40 + $all_blocks = [];
41 + $queue = [];
42 + foreach ( $blocks as &$block ) {
43 + $queue[] = &$block;
44 + }
45 + $queue_count = count( $queue );
46 46
47 - while ( $queue_count > 0 ) {
48 - $block = &$queue[0];
49 - array_shift( $queue );
50 - $all_blocks[] = &$block;
47 + while ( $queue_count > 0 ) {
48 + $block = &$queue[0];
49 + array_shift( $queue );
50 + $all_blocks[] = &$block;
51 51
52 - if ( ! empty( $block['innerBlocks'] ) ) {
53 - foreach ( $block['innerBlocks'] as &$inner_block ) {
54 - $queue[] = &$inner_block;
55 - }
56 - }
52 + if ( ! empty( $block['innerBlocks'] ) ) {
53 + foreach ( $block['innerBlocks'] as &$inner_block ) {
54 + $queue[] = &$inner_block;
55 + }
56 + }
57 57
58 - $queue_count = count( $queue );
59 - }
58 + $queue_count = count( $queue );
59 + }
60 60
61 - return $all_blocks;
62 - }
61 + return $all_blocks;
62 + }
63 63
64 - /**
65 - * Parses wp_template content and injects the current theme's
66 - * stylesheet as a theme attribute into each wp_template_part
67 - *
68 - * @param string $template_content serialized wp_template content.
69 - *
70 - * @return string Updated wp_template content.
71 - */
72 - public function inject_theme_attribute_in_content( $template_content ) {
73 - $has_updated_content = false;
74 - $new_content = '';
75 - $template_blocks = parse_blocks( $template_content );
64 + /**
65 + * Parses wp_template content and injects the current theme's
66 + * stylesheet as a theme attribute into each wp_template_part
67 + *
68 + * @param string $template_content serialized wp_template content.
69 + *
70 + * @return string Updated wp_template content.
71 + */
72 + public function inject_theme_attribute_in_content( $template_content ) {
73 + $has_updated_content = false;
74 + $new_content = '';
75 + $template_blocks = parse_blocks( $template_content );
76 76
77 - $blocks = $this->flatten_blocks( $template_blocks );
78 - foreach ( $blocks as &$block ) {
79 - if (
80 - 'core/template-part' === $block['blockName'] &&
81 - ! isset( $block['attrs']['theme'] )
82 - ) {
83 - $block['attrs']['theme'] = wp_get_theme()->get_stylesheet();
84 - $has_updated_content = true;
85 - }
86 - }
77 + $blocks = $this->flatten_blocks( $template_blocks );
78 + foreach ( $blocks as &$block ) {
79 + if (
80 + 'core/template-part' === $block['blockName'] &&
81 + ! isset( $block['attrs']['theme'] )
82 + ) {
83 + $block['attrs']['theme'] = wp_get_theme()->get_stylesheet();
84 + $has_updated_content = true;
85 + }
86 + }
87 87
88 - if ( $has_updated_content ) {
89 - foreach ( $template_blocks as &$block ) {
90 - $new_content .= serialize_block( $block );
91 - }
88 + if ( $has_updated_content ) {
89 + foreach ( $template_blocks as &$block ) {
90 + $new_content .= serialize_block( $block );
91 + }
92 92
93 - return $new_content;
94 - }
93 + return $new_content;
94 + }
95 95
96 - return $template_content;
97 - }
96 + return $template_content;
97 + }
98 98
99 - /**
100 - * Build a unified template object based a post Object.
101 - * Important: This method is an almost identical duplicate from wp-includes/block-template-utils.php as it was not intended for public use. It has been modified to build templates from plugins rather than themes.
102 - *
103 - * @param \WP_Post $post Template post.
104 - *
105 - * @return \WP_Block_Template|\WP_Error Template.
106 - */
107 - public function build_template_result_from_post( $post ) {
108 - $terms = get_the_terms( $post, 'wp_theme' );
99 + /**
100 + * Build a unified template object based a post Object.
101 + * Important: This method is an almost identical duplicate from wp-includes/block-template-utils.php as it was not intended for public use. It has been modified to build templates from plugins rather than themes.
102 + *
103 + * @param \WP_Post $post Template post.
104 + *
105 + * @return \WP_Block_Template|\WP_Error Template.
106 + */
107 + public static function build_template_result_from_post( $post ) {
108 + $terms = get_the_terms( $post, 'wp_theme' );
109 109
110 - if ( is_wp_error( $terms ) ) {
111 - return $terms;
112 - }
110 + if ( is_wp_error( $terms ) ) {
111 + return $terms;
112 + }
113 113
114 - if ( ! $terms ) {
115 - return new \WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.', 'betterdocs' ) );
116 - }
114 + if ( ! $terms ) {
115 + return new \WP_Error( 'template_missing_theme', __( 'No theme is defined for this template.', 'betterdocs' ) );
116 + }
117 117
118 - $theme = $terms[0]->name;
119 - $has_theme_file = true;
118 + $theme = $terms[0]->name;
119 + $has_theme_file = true;
120 120
121 - $template = new \WP_Block_Template();
122 - $template->wp_id = $post->ID;
123 - $template->id = $theme . '//' . $post->post_name;
124 - $template->theme = $theme;
125 - $template->content = $post->post_content;
126 - $template->slug = $post->post_name;
127 - $template->source = 'custom';
128 - $template->type = $post->post_type;
129 - $template->description = $post->post_excerpt;
130 - $template->title = $post->post_title;
131 - $template->status = $post->post_status;
132 - $template->has_theme_file = $has_theme_file;
133 - $template->is_custom = false;
134 - $template->post_types = []; // Don't appear in any Edit Post template selector dropdown.
121 + $template = new \WP_Block_Template();
122 + $template->wp_id = $post->ID;
123 + $template->id = $theme . '//' . $post->post_name;
124 + $template->theme = $theme;
125 + $template->content = $post->post_content;
126 + $template->slug = $post->post_name;
127 + $template->source = 'custom';
128 + $template->type = $post->post_type;
129 + $template->description = $post->post_excerpt;
130 + $template->title = $post->post_title;
131 + $template->status = $post->post_status;
132 + $template->has_theme_file = $has_theme_file;
133 + $template->is_custom = false;
134 + $template->post_types = array(); // Don't appear in any Edit Post template selector dropdown.
135 135
136 - if ( 'wp_template_part' === $post->post_type ) {
137 - $type_terms = get_the_terms( $post, 'wp_template_part_area' );
138 - if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) {
139 - $template->area = $type_terms[0]->name;
140 - }
141 - }
136 + if ( 'wp_template_part' === $post->post_type ) {
137 + $type_terms = get_the_terms( $post, 'wp_template_part_area' );
138 + if ( ! is_wp_error( $type_terms ) && false !== $type_terms ) {
139 + $template->area = $type_terms[0]->name;
140 + }
141 + }
142 142
143 - return $template;
144 - }
143 + // We are checking 'woocommerce' to maintain classic templates which are saved to the DB,
144 + // prior to updating to use the correct slug.
145 + // More information found here: https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/5423.
146 + if ( self::PLUGIN_SLUG === $theme ) {
147 + $template->origin = 'plugin';
148 + }
145 149
146 - /**
147 - * Build a unified template object based on a theme file.
148 - * Important: This method is an almost identical duplicate from wp-includes/block-template-utils.php as it was not intended for public use. It has been modified to build templates from plugins rather than themes.
149 - *
150 - * @param array|object $template_file Theme file.
151 - * @param string $template_type wp_template or wp_template_part.
152 - *
153 - * @return \WP_Block_Template Template.
154 - */
155 - public function build_template_result_from_file( $template_file, $template_type ) {
156 - $template_file = (object) $template_file;
150 + /*
151 + * Run the block hooks algorithm introduced in WP 6.4 on the template content.
152 + */
153 + if ( function_exists( 'inject_ignored_hooked_blocks_metadata_attributes' ) ) {
154 + $hooked_blocks = get_hooked_blocks();
155 + if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
156 + $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template );
157 + $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template );
158 + $blocks = parse_blocks( $template->content );
159 + $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
160 + }
161 + }
157 162
158 - // If the theme has an archive-docs.html template but does not have docs taxonomy templates
159 - // then we will load in the archive-docs.html template from the theme to use for docs taxonomies on the frontend.
160 - $template_is_from_theme = 'theme' === $template_file->source;
161 - $theme_name = wp_get_theme()->get( 'TextDomain' );
163 + return $template;
164 + }
162 165
163 - // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
164 - $template_content = file_get_contents( $template_file->path );
165 - $template = new \WP_Block_Template();
166 - $template->id = $template_is_from_theme ? $theme_name . '//' . $template_file->slug : self::PLUGIN_SLUG . '//' . $template_file->slug;
167 - $template->theme = $template_is_from_theme ? $theme_name : self::PLUGIN_SLUG;
168 - $template->content = $this->inject_theme_attribute_in_content( $template_content );
169 - // Plugin was agreed as a valid source value despite existing inline docs at the time of creating: https://github.com/WordPress/gutenberg/issues/36597#issuecomment-976232909.
170 - $template->source = $template_file->source ? $template_file->source : 'plugin';
171 - $template->slug = $template_file->slug;
172 - $template->type = $template_type;
173 - $template->title = ! empty( $template_file->title ) ? $template_file->title : $this->convert_slug_to_title( $template_file->slug );
174 - $template->status = 'publish';
175 - $template->has_theme_file = true;
176 - $template->origin = $template_file->source;
177 - $template->is_custom = false; // Templates loaded from the filesystem aren't custom, ones that have been edited and loaded from the DB are.
178 - $template->post_types = []; // Don't appear in any Edit Post template selector dropdown.
179 - $template->area = 'uncategorized';
180 - return $template;
181 - }
166 + /**
167 + * Build a unified template object based on a theme file.
168 + *
169 + * @internal Important: This method is an almost identical duplicate from wp-includes/block-template-utils.php as it was not intended for public use. It has been modified to build templates from plugins rather than themes.
170 + *
171 + * @param array|object $template_file Theme file.
172 + * @param string $template_type wp_template or wp_template_part.
173 + *
174 + * @return \WP_Block_Template Template.
175 + */
176 + public function build_template_result_from_file( $template_file, $template_type ) {
177 + $template_file = (object) $template_file;
182 178
183 - /**
184 - * Build a new template object so that we can make BetterDocs Blocks default templates available in the current theme should they not have any.
185 - *
186 - * @param string $template_file Block template file path.
187 - * @param string $template_type wp_template or wp_template_part.
188 - * @param string $template_slug Block template slug e.g. single-docs.
189 - * @param bool $template_is_from_theme If the block template file is being loaded from the current theme instead of BetterDocs Blocks.
190 - *
191 - * @return object Block template object.
192 - */
193 - public function create_new_block_template_object( $template_file, $template_type, $template_slug, $template_is_from_theme = false ) {
194 - $theme_name = wp_get_theme()->get( 'TextDomain' );
179 + // If the theme has an archive-products.html template but does not have product taxonomy templates
180 + // then we will load in the archive-product.html template from the theme to use for product taxonomies on the frontend.
181 + $template_is_from_theme = 'theme' === $template_file->source;
182 + $theme_name = wp_get_theme()->get( 'TextDomain' );
195 183
196 - $new_template_item = [
197 - 'slug' => $template_slug,
198 - 'id' => $template_is_from_theme ? $theme_name . '//' . $template_slug : self::PLUGIN_SLUG . '//' . $template_slug,
199 - 'path' => $template_file,
200 - 'type' => $template_type,
201 - 'theme' => $template_is_from_theme ? $theme_name : self::PLUGIN_SLUG,
202 - // Plugin was agreed as a valid source value despite existing inline docs at the time of creating: https://github.com/WordPress/gutenberg/issues/36597#issuecomment-976232909.
203 - 'source' => $template_is_from_theme ? 'theme' : 'plugin',
204 - 'title' => $this->convert_slug_to_title( $template_slug ),
205 - 'description' => '',
206 - 'post_types' => [] // Don't appear in any Edit Post template selector dropdown.
207 - ];
184 + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
185 + $template_content = file_get_contents( $template_file->path );
186 + $template = new \WP_Block_Template();
187 + $template->id = $template_is_from_theme ? $theme_name . '//' . $template_file->slug : self::PLUGIN_SLUG . '//' . $template_file->slug;
188 + $template->theme = $template_is_from_theme ? $theme_name : self::PLUGIN_SLUG;
189 + $template->content = self::inject_theme_attribute_in_content( $template_content );
190 + // Remove the term description block from the archive-product template
191 + // as the Product Catalog/Shop page doesn't have a description.
192 + if ( 'archive-docs' === $template_file->slug ) {
193 + $template->content = str_replace( '<!-- wp:term-description {"align":"wide"} /-->', '', $template->content );
194 + }
195 + // Plugin was agreed as a valid source value despite existing inline docs at the time of creating: https://github.com/WordPress/gutenberg/issues/36597#issuecomment-976232909.
196 + $template->source = $template_file->source ? $template_file->source : 'plugin';
197 + $template->slug = $template_file->slug;
198 + $template->type = $template_type;
199 + $template->title = ! empty( $template_file->title ) ? $template_file->title : self::get_block_template_title( $template_file->slug );
200 + $template->description = ! empty( $template_file->description ) ? $template_file->description : self::get_block_template_description( $template_file->slug );
201 + $template->status = 'publish';
202 + $template->has_theme_file = true;
203 + $template->origin = $template_file->source;
204 + $template->is_custom = false; // Templates loaded from the filesystem aren't custom, ones that have been edited and loaded from the DB are.
205 + $template->post_types = array(); // Don't appear in any Edit Post template selector dropdown.
206 + $template->area = 'uncategorized';
208 207
209 - return (object) $new_template_item;
210 - }
208 + /*
209 + * Run the block hooks algorithm introduced in WP 6.4 on the template content.
210 + */
211 + if ( function_exists( 'inject_ignored_hooked_blocks_metadata_attributes' ) ) {
212 + $before_block_visitor = '_inject_theme_attribute_in_template_part_block';
213 + $after_block_visitor = null;
214 + $hooked_blocks = get_hooked_blocks();
215 + if ( ! empty( $hooked_blocks ) || has_filter( 'hooked_block_types' ) ) {
216 + $before_block_visitor = make_before_block_visitor( $hooked_blocks, $template );
217 + $after_block_visitor = make_after_block_visitor( $hooked_blocks, $template );
218 + }
219 + $blocks = parse_blocks( $template->content );
220 + $template->content = traverse_and_serialize_blocks( $blocks, $before_block_visitor, $after_block_visitor );
221 + }
211 222
212 - /**
213 - * Finds all nested template part file paths in a theme's directory.
214 - *
215 - * @param string $base_directory The theme's file path.
216 - * @return array $path_list A list of paths to all template part files.
217 - */
218 - public function get_template_paths( $base_directory ) {
219 - $path_list = [];
220 - if ( file_exists( $base_directory ) ) {
221 - $nested_files = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $base_directory ) );
222 - $nested_html_files = new \RegexIterator( $nested_files, '/^.+\.html$/i', \RecursiveRegexIterator::GET_MATCH );
223 - foreach ( $nested_html_files as $path => $file ) {
224 - $path_list[] = $path;
225 - }
226 - }
227 - return $path_list;
228 - }
223 + return $template;
224 + }
229 225
230 - /**
231 - * Returns template titles.
232 - *
233 - * @param string $template_slug The templates slug (e.g. single-docs).
234 - * @return string Human friendly title.
235 - */
236 - public function get_block_template_title( $template_slug ) {
237 - $plugin_template_types = $this->get_plugin_block_template_types();
238 - if ( isset( $plugin_template_types[$template_slug] ) ) {
239 - return $plugin_template_types[$template_slug]['title'];
240 - } else {
241 - // Human friendly title converted from the slug.
242 - return ucwords( preg_replace( '/[\-_]/', ' ', $template_slug ) );
243 - }
244 - }
226 + /**
227 + * Build a new template object so that we can make BetterDocs Blocks default templates available in the current theme should they not have any.
228 + *
229 + * @param string $template_file Block template file path.
230 + * @param string $template_type wp_template or wp_template_part.
231 + * @param string $template_slug Block template slug e.g. single-docs.
232 + * @param bool $template_is_from_theme If the block template file is being loaded from the current theme instead of BetterDocs Blocks.
233 + *
234 + * @return object Block template object.
235 + */
236 + public function create_new_block_template_object( $template_file, $template_type, $template_slug, $template_is_from_theme = false ) {
237 + $theme_name = wp_get_theme()->get( 'TextDomain' );
245 238
246 - /**
247 - * Returns template descriptions.
248 - *
249 - * @param string $template_slug The templates slug (e.g. single-docs).
250 - * @return string Template description.
251 - */
252 - public function get_block_template_description( $template_slug ) {
253 - $plugin_template_types = $this->get_plugin_block_template_types();
254 - if ( isset( $plugin_template_types[$template_slug] ) ) {
255 - return $plugin_template_types[$template_slug]['description'];
256 - }
257 - return '';
258 - }
239 + $new_template_item = [
240 + 'slug' => $template_slug,
241 + 'id' => $template_is_from_theme ? $theme_name . '//' . $template_slug : self::PLUGIN_SLUG . '//' . $template_slug,
242 + 'path' => $template_file,
243 + 'type' => $template_type,
244 + 'theme' => $template_is_from_theme ? $theme_name : self::PLUGIN_SLUG,
245 + // Plugin was agreed as a valid source value despite existing inline docs at the time of creating: https://github.com/WordPress/gutenberg/issues/36597#issuecomment-976232909.
246 + 'source' => $template_is_from_theme ? 'theme' : 'plugin',
247 + 'title' => $this->convert_slug_to_title( $template_slug ),
248 + 'description' => '',
249 + 'post_types' => [] // Don't appear in any Edit Post template selector dropdown.
250 + ];
259 251
260 - public function get_templates_fils_from_betterdocs( $template_type ) {
261 - $directory = $this->get_templates_directory( $template_type );
262 - $template_files = $this->get_template_paths( $directory );
263 - // var_dump($template_files);
264 - return $template_files;
265 - }
252 + return (object) $new_template_item;
253 + }
266 254
267 - /**
268 - * Gets the templates from the BetterDocs blocks directory
269 - *
270 - * @param string[] $slugs An array of slugs to filter templates by. Templates whose slug does not match will not be returned.
271 - * @param array $already_found_templates Templates that have already been found, these are customised templates that are loaded from the database.
272 - * @param string $template_type wp_template or wp_template_part.
273 - *
274 - * @return array Templates from the BetterDocs blocks plugin directory.
275 - */
276 - public function get_block_templates_from_betterdocs( $slugs, $already_found_templates, $template_type = 'wp_template' ) {
255 + /**
256 + * Finds all nested template part file paths in a theme's directory.
257 + *
258 + * @param string $base_directory The theme's file path.
259 + * @return array $path_list A list of paths to all template part files.
260 + */
261 + public function get_template_paths( $base_directory ) {
262 + $path_list = [];
263 + if ( file_exists( $base_directory ) ) {
264 + $nested_files = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $base_directory ) );
265 + $nested_html_files = new \RegexIterator( $nested_files, '/^.+\.html$/i', \RecursiveRegexIterator::GET_MATCH );
266 + foreach ( $nested_html_files as $path => $file ) {
267 + $path_list[] = $path;
268 + }
269 + }
270 + return $path_list;
271 + }
277 272
278 - $template_files = $this->get_templates_fils_from_betterdocs( $template_type );
279 - $templates = [];
273 + /**
274 + * Returns template titles.
275 + *
276 + * @param string $template_slug The templates slug (e.g. single-docs).
277 + * @return string Human friendly title.
278 + */
279 + public function get_block_template_title( $template_slug ) {
280 + $plugin_template_types = $this->get_plugin_block_template_types();
281 + if ( isset( $plugin_template_types[ $template_slug ] ) ) {
282 + return $plugin_template_types[ $template_slug ]['title'];
283 + } else {
284 + // Human friendly title converted from the slug.
285 + return ucwords( preg_replace( '/[\-_]/', ' ', $template_slug ) );
286 + }
287 + }
280 288
281 - foreach ( $template_files as $template_file ) {
282 - $template_slug = $this->generate_template_slug_from_path( $template_file );
289 + /**
290 + * Returns template descriptions.
291 + *
292 + * @param string $template_slug The templates slug (e.g. single-docs).
293 + * @return string Template description.
294 + */
295 + public function get_block_template_description( $template_slug ) {
296 + $plugin_template_types = $this->get_plugin_block_template_types();
297 + if ( isset( $plugin_template_types[ $template_slug ] ) ) {
298 + return $plugin_template_types[ $template_slug ]['description'];
299 + }
300 + return '';
301 + }
283 302
284 - // This template does not have a slug we're looking for. Skip it.
285 - if ( is_array( $slugs ) && count( $slugs ) > 0 && ! in_array( $template_slug, $slugs, true ) ) {
286 - continue;
287 - }
303 + public function get_templates_fils_from_betterdocs( $template_type ) {
304 + $directory = $this->get_templates_directory( $template_type );
305 + $template_files = $this->get_template_paths( $directory );
306 + return $template_files;
307 + }
288 308
289 - // If the the template is already in the list (i.e. it came from the
290 - // database) then we should not overwrite it with the one from the filesystem.
291 - if (
292 - count(
293 - array_filter(
294 - $already_found_templates,
295 - function ( $template ) use ( $template_slug ) {
296 - $template_obj = (object) $template; //phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
297 - return $template_obj->slug === $template_slug;
298 - }
299 - )
300 - ) > 0 ) {
301 - continue;
302 - }
309 + /**
310 + * Gets the templates from the BetterDocs blocks directory
311 + *
312 + * @param string[] $slugs An array of slugs to filter templates by. Templates whose slug does not match will not be returned.
313 + * @param array $already_found_templates Templates that have already been found, these are customised templates that are loaded from the database.
314 + * @param string $template_type wp_template or wp_template_part.
315 + *
316 + * @return array Templates from the BetterDocs blocks plugin directory.
317 + */
318 + public function get_block_templates_from_betterdocs( $slugs, $already_found_templates, $template_type = 'wp_template' ) {
303 319
304 - // At this point the template only exists in the Blocks filesystem and has not been saved in the DB,
305 - // or superseded by the theme.
306 - $templates[] = $this->create_new_block_template_object( $template_file, $template_type, $template_slug );
307 - }
308 - return $templates;
309 - }
320 + $template_files = $this->get_templates_fils_from_betterdocs( $template_type );
321 + $templates = [];
310 322
311 - /**
312 - * Returns a filtered list of plugin template types, containing their
313 - * localized titles and descriptions.
314 - *
315 - * @return array The plugin template types.
316 - */
317 - public function get_plugin_block_template_types() {
318 - $plugin_template_types = [
319 - 'archive-docs' => [
320 - 'title' => _x( 'Docs Page', 'Template name', 'betterdocs' ),
321 - 'description' => __( 'Template used to display Docs Page.', 'betterdocs' )
322 - ],
323 - 'taxonomy-doc_category' => [
324 - 'title' => _x( 'Docs Category', 'Template name', 'betterdocs' ),
325 - 'description' => __( 'Template used to display docs by category.', 'betterdocs' )
326 - ],
327 - 'taxonomy-doc_tag' => [
328 - 'title' => _x( 'Docs Tag', 'Template name', 'betterdocs' ),
329 - 'description' => __( 'Template used to display docs by tag.', 'betterdocs' )
330 - ],
331 - 'single-docs' => [
332 - 'title' => _x( 'Single Docs', 'Template name', 'betterdocs' ),
333 - 'description' => __( 'Template used to display the single docs.', 'betterdocs' )
334 - ]
335 - ];
323 + // Check if BetterDocs Pro is not installed
324 + if ( ! betterdocs()->is_pro_active() || ( betterdocs()->is_pro_active() && betterdocs()->settings->get( 'multiple_kb' ) == false ) ) {
325 + // Remove the 'taxonomy-knowledge_base.html' file
326 + $template_files = array_filter(
327 + $template_files,
328 + function ( $file ) {
329 + return strpos( $file, 'taxonomy-knowledge_base.html' ) === false;
330 + }
331 + );
332 + }
336 333
337 - return $plugin_template_types;
338 - }
334 + foreach ( $template_files as $template_file ) {
335 + $template_slug = $this->generate_template_slug_from_path( $template_file );
339 336
340 - /**
341 - * Converts template slugs into readable titles.
342 - *
343 - * @param string $template_slug The templates slug (e.g. single-docs).
344 - * @return string Human friendly title converted from the slug.
345 - */
346 - public function convert_slug_to_title( $template_slug ) {
347 - switch ( $template_slug ) {
348 - case 'single-docs':
349 - return __( 'Single Docs', 'betterdocs' );
350 - case 'archive-docs':
351 - return __( 'Docs Page', 'betterdocs' );
352 - case 'taxonomy-doc_category':
353 - return __( 'Docs Category', 'betterdocs' );
354 - case 'taxonomy-doc_tag':
355 - return __( 'Docs Tag', 'betterdocs' );
356 - default:
357 - // Replace all hyphens and underscores with spaces.
358 - return ucwords( preg_replace( '/[\-_]/', ' ', $template_slug ) );
359 - }
360 - }
337 + // This template does not have a slug we're looking for. Skip it.
338 + if ( is_array( $slugs ) && count( $slugs ) > 0 && ! in_array( $template_slug, $slugs, true ) ) {
339 + continue;
340 + }
361 341
362 - /**
363 - * Converts template paths into a slug
364 - *
365 - * @param string $path The template's path.
366 - * @return string slug
367 - */
368 - public function generate_template_slug_from_path( $path ) {
369 - $template_extension = '.html';
342 + // If the the template is already in the list (i.e. it came from the
343 + // database) then we should not overwrite it with the one from the filesystem.
344 + if (
345 + count(
346 + array_filter(
347 + $already_found_templates,
348 + function ( $template ) use ( $template_slug ) {
349 + $template_obj = (object) $template; //phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.Found
350 + return $template_obj->slug === $template_slug;
351 + }
352 + )
353 + ) > 0 ) {
354 + continue;
355 + }
370 356
371 - return basename( $path, $template_extension );
372 - }
357 + // At this point the template only exists in the Blocks filesystem and has not been saved in the DB,
358 + // or superseded by the theme.
359 + $templates[] = $this->create_new_block_template_object( $template_file, $template_type, $template_slug );
360 + }
361 + return $templates;
362 + }
373 363
374 - /**
375 - * Checks to see if they are using a compatible version of WP, or if not they have a compatible version of the Gutenberg plugin installed.
376 - *
377 - * @return boolean
378 - */
379 - public function supports_block_templates() {
380 - if (
381 - ! betterdocs()->helper->current_theme_is_fse_theme() &&
382 - ( ! function_exists( 'gutenberg_supports_block_templates' ) || ! gutenberg_supports_block_templates() )
383 - ) {
384 - return false;
385 - }
364 + /**
365 + * Returns a filtered list of plugin template types, containing their
366 + * localized titles and descriptions.
367 + *
368 + * @return array The plugin template types.
369 + */
370 + public function get_plugin_block_template_types() {
371 + $plugin_template_types = [
372 + 'archive-docs' => [
373 + 'title' => _x( 'Docs Page', 'Template name', 'betterdocs' ),
374 + 'description' => __( 'Template used to display Docs Page.', 'betterdocs' )
375 + ],
376 + 'taxonomy-doc_category' => [
377 + 'title' => _x( 'Docs Category', 'Template name', 'betterdocs' ),
378 + 'description' => __( 'Template used to display docs by category.', 'betterdocs' )
379 + ],
380 + 'taxonomy-doc_tag' => [
381 + 'title' => _x( 'Docs Tag', 'Template name', 'betterdocs' ),
382 + 'description' => __( 'Template used to display docs by tag.', 'betterdocs' )
383 + ],
384 + 'single-docs' => [
385 + 'title' => _x( 'Single Docs', 'Template name', 'betterdocs' ),
386 + 'description' => __( 'Template used to display the single docs.', 'betterdocs' )
387 + ]
388 + ];
386 389
387 - return true;
388 - }
390 + return $plugin_template_types;
391 + }
389 392
390 - /**
391 - * Retrieves a single unified template object using its id.
392 - *
393 - * @param string $id Template unique identifier (example: theme_slug//template_slug).
394 - * @param string $template_type Optional. Template type: `'wp_template'` or '`wp_template_part'`.
395 - * Default `'wp_template'`.
396 - *
397 - * @return \WP_Block_Template|null Template.
398 - */
399 - public static function get_block_template( $id, $template_type ) {
400 - if ( function_exists( 'get_block_template' ) ) {
401 - return get_block_template( $id, $template_type );
402 - }
393 + /**
394 + * Converts template slugs into readable titles.
395 + *
396 + * @param string $template_slug The templates slug (e.g. single-docs).
397 + * @return string Human friendly title converted from the slug.
398 + */
399 + public function convert_slug_to_title( $template_slug ) {
400 + switch ( $template_slug ) {
401 + case 'single-docs':
402 + return __( 'Single Docs', 'betterdocs' );
403 + case 'archive-docs':
404 + return __( 'Docs Page', 'betterdocs' );
405 + case 'taxonomy-doc_category':
406 + return __( 'Docs Category', 'betterdocs' );
407 + case 'taxonomy-doc_tag':
408 + return __( 'Docs Tag', 'betterdocs' );
409 + default:
410 + // Replace all hyphens and underscores with spaces.
411 + return ucwords( preg_replace( '/[\-_]/', ' ', $template_slug ) );
412 + }
413 + }
403 414
404 - if ( function_exists( 'gutenberg_get_block_template' ) ) {
405 - return gutenberg_get_block_template( $id, $template_type );
406 - }
415 + /**
416 + * Converts template paths into a slug
417 + *
418 + * @param string $path The template's path.
419 + * @return string slug
420 + */
421 + public function generate_template_slug_from_path( $path ) {
422 + $template_extension = '.html';
407 423
408 - return null;
409 - }
424 + return basename( $path, $template_extension );
425 + }
410 426
411 - /**
412 - * Checks if we can fallback to the `archive-docs` template for a given slug
413 - *
414 - * `taxonomy-doc_category`, `taxonomy-knowledge_base` and `taxonomy-doc_tag` templates can generally use the
415 - * `archive-docs` as a fallback if there are no specific overrides.
416 - *
417 - * @param string $template_slug Slug to check for fallbacks.
418 - * @return boolean
419 - */
420 - public function template_is_eligible_for_docs_archive_fallback( $template_slug ) {
421 - return in_array( $template_slug, self::ELIGIBLE_FOR_DOC_ARCHIVE_FALLBACK, true );
422 - }
427 + /**
428 + * Checks to see if they are using a compatible version of WP, or if not they have a compatible version of the Gutenberg plugin installed.
429 + *
430 + * @return boolean
431 + */
432 + public function supports_block_templates() {
433 + if (
434 + ! betterdocs()->helper->current_theme_is_fse_theme() &&
435 + ( ! function_exists( 'gutenberg_supports_block_templates' ) || ! gutenberg_supports_block_templates() )
436 + ) {
437 + return false;
438 + }
423 439
424 - /**
425 - * Sets the `has_theme_file` to `true` for templates with fallbacks
426 - *
427 - * There are cases (such as tags and categories) in which fallback templates
428 - * can be used; so, while *technically* the theme doesn't have a specific file
429 - * for them, it is important that we tell Gutenberg that we do, in fact,
430 - * have a theme file (i.e. the fallback one).
431 - *
432 - * **Note:** this function changes the array that has been passed.
433 - *
434 - * It returns `true` if anything was changed, `false` otherwise.
435 - *
436 - * @param array $query_result Array of template objects.
437 - * @param object $template A specific template object which could have a fallback.
438 - *
439 - * @return boolean
440 - */
441 - public function set_has_theme_file_if_fallback_is_available( $query_result, $template ) {
442 - foreach ( $query_result as &$query_result_template ) {
443 - if (
444 - $query_result_template->slug === $template->slug
445 - && $query_result_template->theme === $template->theme
446 - ) {
447 - if ( $this->template_is_eligible_for_docs_archive_fallback( $template->slug ) ) {
448 - $query_result_template->has_theme_file = true;
449 - }
440 + return true;
441 + }
450 442
451 - return true;
452 - }
453 - }
443 + /**
444 + * Retrieves a single unified template object using its id.
445 + *
446 + * @param string $id Template unique identifier (example: theme_slug//template_slug).
447 + * @param string $template_type Optional. Template type: `'wp_template'` or '`wp_template_part'`.
448 + * Default `'wp_template'`.
449 + *
450 + * @return \WP_Block_Template|null Template.
451 + */
452 + public static function get_block_template( $id, $template_type ) {
453 + if ( function_exists( 'get_block_template' ) ) {
454 + return get_block_template( $id, $template_type );
455 + }
454 456
455 - return false;
456 - }
457 + if ( function_exists( 'gutenberg_get_block_template' ) ) {
458 + return gutenberg_get_block_template( $id, $template_type );
459 + }
457 460
458 - /**
459 - * Removes templates that were added to a theme's block-templates directory, but already had a customised version saved in the database.
460 - *
461 - * @param \WP_Block_Template[]|\stdClass[] $templates List of templates to run the filter on.
462 - *
463 - * @return array List of templates with duplicates removed. The customised alternative is preferred over the theme default.
464 - */
465 - public static function remove_theme_templates_with_custom_alternative( $templates ) {
461 + return null;
462 + }
466 463
467 - // Get the slugs of all templates that have been customised and saved in the database.
468 - $customised_template_slugs = array_map(
469 - function ( $template ) {
470 - return $template->slug;
471 - },
472 - array_values(
473 - array_filter(
474 - $templates,
475 - function ( $template ) {
476 - // This template has been customised and saved as a post.
477 - return 'custom' === $template->source;
478 - }
479 - )
480 - )
481 - );
464 + /**
465 + * Checks if we can fallback to the `archive-docs` template for a given slug
466 + *
467 + * `taxonomy-doc_category`, `taxonomy-knowledge_base` and `taxonomy-doc_tag` templates can generally use the
468 + * `archive-docs` as a fallback if there are no specific overrides.
469 + *
470 + * @param string $template_slug Slug to check for fallbacks.
471 + * @return boolean
472 + */
473 + public function template_is_eligible_for_docs_archive_fallback( $template_slug ) {
474 + return in_array( $template_slug, self::ELIGIBLE_FOR_DOC_ARCHIVE_FALLBACK, true );
475 + }
482 476
483 - // Remove theme (i.e. filesystem) templates that have the same slug as a customised one. We don't need to check
484 - // for `betterdocs` in $template->source here because betterdocs templates won't have been added to $templates
485 - // if a saved version was found in the db. This only affects saved templates that were saved BEFORE a theme
486 - // template with the same slug was added.
487 - return array_values(
488 - array_filter(
489 - $templates,
490 - function ( $template ) use ( $customised_template_slugs ) {
491 - // This template has been customised and saved as a post, so return it.
492 - return ! ( 'theme' === $template->source && in_array( $template->slug, $customised_template_slugs, true ) );
493 - }
494 - )
495 - );
496 - }
477 + /**
478 + * Sets the `has_theme_file` to `true` for templates with fallbacks
479 + *
480 + * There are cases (such as tags and categories) in which fallback templates
481 + * can be used; so, while *technically* the theme doesn't have a specific file
482 + * for them, it is important that we tell Gutenberg that we do, in fact,
483 + * have a theme file (i.e. the fallback one).
484 + *
485 + * **Note:** this function changes the array that has been passed.
486 + *
487 + * It returns `true` if anything was changed, `false` otherwise.
488 + *
489 + * @param array $query_result Array of template objects.
490 + * @param object $template A specific template object which could have a fallback.
491 + *
492 + * @return boolean
493 + */
494 + public function set_has_theme_file_if_fallback_is_available( $query_result, $template ) {
495 + foreach ( $query_result as &$query_result_template ) {
496 + if (
497 + $query_result_template->slug === $template->slug
498 + && $query_result_template->theme === $template->theme
499 + ) {
500 + if ( $this->template_is_eligible_for_docs_archive_fallback( $template->slug ) ) {
501 + $query_result_template->has_theme_file = true;
502 + }
503 +
504 + return true;
505 + }
506 + }
507 +
508 + return false;
509 + }
510 +
511 + /**
512 + * Removes templates that were added to a theme's block-templates directory, but already had a customised version saved in the database.
513 + *
514 + * @param \WP_Block_Template[]|\stdClass[] $templates List of templates to run the filter on.
515 + *
516 + * @return array List of templates with duplicates removed. The customised alternative is preferred over the theme default.
517 + */
518 + public static function remove_theme_templates_with_custom_alternative( $templates ) {
519 +
520 + // Get the slugs of all templates that have been customised and saved in the database.
521 + $customised_template_slugs = array_map(
522 + function ( $template ) {
523 + return $template->slug;
524 + },
525 + array_values(
526 + array_filter(
527 + $templates,
528 + function ( $template ) {
529 + // This template has been customised and saved as a post.
530 + return 'custom' === $template->source;
531 + }
532 + )
533 + )
534 + );
535 +
536 + // Remove theme (i.e. filesystem) templates that have the same slug as a customised one. We don't need to check
537 + // for `betterdocs` in $template->source here because betterdocs templates won't have been added to $templates
538 + // if a saved version was found in the db. This only affects saved templates that were saved BEFORE a theme
539 + // template with the same slug was added.
540 + return array_values(
541 + array_filter(
542 + $templates,
543 + function ( $template ) use ( $customised_template_slugs ) {
544 + // This template has been customised and saved as a post, so return it.
545 + return ! ( 'theme' === $template->source && in_array( $template->slug, $customised_template_slugs, true ) );
546 + }
547 + )
548 + );
549 + }
497 550 }