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.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 +436 -489 4.9.23.5.3 View file →
@@ -5,546 +5,493 @@
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 static 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 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 = array(); // 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 = []; // 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 - // 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 - }
143 + return $template;
144 + }
149 145
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 - }
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;
162 157
163 - return $template;
164 - }
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' );
165 162
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;
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 + }
178 182
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' );
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' );
183 195
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';
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 + ];
207 208
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 - }
209 + return (object) $new_template_item;
210 + }
222 211
223 - return $template;
224 - }
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 + }
225 229
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' );
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 + }
238 245
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 - ];
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 + }
251 259
252 - return (object) $new_template_item;
253 - }
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 + }
254 266
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 - }
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' ) {
272 277
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 - }
278 + $template_files = $this->get_templates_fils_from_betterdocs( $template_type );
279 + $templates = [];
288 280
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 - }
281 + foreach ( $template_files as $template_file ) {
282 + $template_slug = $this->generate_template_slug_from_path( $template_file );
302 283
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 - }
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 + }
308 288
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' ) {
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 + }
319 303
320 - $template_files = $this->get_templates_fils_from_betterdocs( $template_type );
321 - $templates = [];
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 + }
322 310
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 - }
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 + ];
333 336
334 - foreach ( $template_files as $template_file ) {
335 - $template_slug = $this->generate_template_slug_from_path( $template_file );
337 + return $plugin_template_types;
338 + }
336 339
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 - }
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 + }
341 361
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 - }
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';
356 370
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 - }
371 + return basename( $path, $template_extension );
372 + }
363 373
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 - ];
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 + }
389 386
390 - return $plugin_template_types;
391 - }
387 + return true;
388 + }
392 389
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 - }
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 + }
414 403
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';
404 + if ( function_exists( 'gutenberg_get_block_template' ) ) {
405 + return gutenberg_get_block_template( $id, $template_type );
406 + }
423 407
424 - return basename( $path, $template_extension );
425 - }
408 + return null;
409 + }
426 410
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 - }
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 + }
439 423
440 - return true;
441 - }
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 + }
442 450
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 - }
451 + return true;
452 + }
453 + }
456 454
457 - if ( function_exists( 'gutenberg_get_block_template' ) ) {
458 - return gutenberg_get_block_template( $id, $template_type );
459 - }
455 + return false;
456 + }
460 457
461 - return null;
462 - }
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 ) {
463 466
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 - }
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 + );
476 482
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 - }
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 + }
550 497 }