27 'query-offset-posts', |
28 'query-offset-posts', |
28 'social-links-shared-background-color', |
29 'social-links-shared-background-color', |
29 ); |
30 ); |
30 |
31 |
31 foreach ( $core_block_patterns as $core_block_pattern ) { |
32 foreach ( $core_block_patterns as $core_block_pattern ) { |
32 register_block_pattern( |
33 $pattern = require __DIR__ . '/block-patterns/' . $core_block_pattern . '.php'; |
33 'core/' . $core_block_pattern, |
34 $pattern['source'] = 'core'; |
34 require __DIR__ . '/block-patterns/' . $core_block_pattern . '.php' |
35 register_block_pattern( 'core/' . $core_block_pattern, $pattern ); |
35 ); |
36 } |
36 } |
37 } |
37 } |
38 |
38 |
39 register_block_pattern_category( 'banner', array( 'label' => _x( 'Banners', 'Block pattern category' ) ) ); |
39 register_block_pattern_category( 'buttons', array( 'label' => _x( 'Buttons', 'Block pattern category' ) ) ); |
40 register_block_pattern_category( |
40 register_block_pattern_category( 'columns', array( 'label' => _x( 'Columns', 'Block pattern category' ) ) ); |
41 'buttons', |
41 register_block_pattern_category( 'featured', array( 'label' => _x( 'Featured', 'Block pattern category' ) ) ); |
42 array( |
42 register_block_pattern_category( 'gallery', array( 'label' => _x( 'Gallery', 'Block pattern category' ) ) ); |
43 'label' => _x( 'Buttons', 'Block pattern category' ), |
43 register_block_pattern_category( 'header', array( 'label' => _x( 'Headers', 'Block pattern category' ) ) ); |
44 'description' => __( 'Patterns that contain buttons and call to actions.' ), |
44 register_block_pattern_category( 'text', array( 'label' => _x( 'Text', 'Block pattern category' ) ) ); |
45 ) |
45 register_block_pattern_category( 'query', array( 'label' => _x( 'Query', 'Block pattern category' ) ) ); |
46 ); |
|
47 register_block_pattern_category( |
|
48 'columns', |
|
49 array( |
|
50 'label' => _x( 'Columns', 'Block pattern category' ), |
|
51 'description' => __( 'Multi-column patterns with more complex layouts.' ), |
|
52 ) |
|
53 ); |
|
54 register_block_pattern_category( |
|
55 'text', |
|
56 array( |
|
57 'label' => _x( 'Text', 'Block pattern category' ), |
|
58 'description' => __( 'Patterns containing mostly text.' ), |
|
59 ) |
|
60 ); |
|
61 register_block_pattern_category( |
|
62 'query', |
|
63 array( |
|
64 'label' => _x( 'Posts', 'Block pattern category' ), |
|
65 'description' => __( 'Display your latest posts in lists, grids or other layouts.' ), |
|
66 ) |
|
67 ); |
|
68 register_block_pattern_category( |
|
69 'featured', |
|
70 array( |
|
71 'label' => _x( 'Featured', 'Block pattern category' ), |
|
72 'description' => __( 'A set of high quality curated patterns.' ), |
|
73 ) |
|
74 ); |
|
75 register_block_pattern_category( |
|
76 'call-to-action', |
|
77 array( |
|
78 'label' => _x( 'Call to Action', 'Block pattern category' ), |
|
79 'description' => __( 'Sections whose purpose is to trigger a specific action.' ), |
|
80 ) |
|
81 ); |
|
82 register_block_pattern_category( |
|
83 'team', |
|
84 array( |
|
85 'label' => _x( 'Team', 'Block pattern category' ), |
|
86 'description' => __( 'A variety of designs to display your team members.' ), |
|
87 ) |
|
88 ); |
|
89 register_block_pattern_category( |
|
90 'testimonials', |
|
91 array( |
|
92 'label' => _x( 'Testimonials', 'Block pattern category' ), |
|
93 'description' => __( 'Share reviews and feedback about your brand/business.' ), |
|
94 ) |
|
95 ); |
|
96 register_block_pattern_category( |
|
97 'services', |
|
98 array( |
|
99 'label' => _x( 'Services', 'Block pattern category' ), |
|
100 'description' => __( 'Briefly describe what your business does and how you can help.' ), |
|
101 ) |
|
102 ); |
|
103 register_block_pattern_category( |
|
104 'contact', |
|
105 array( |
|
106 'label' => _x( 'Contact', 'Block pattern category' ), |
|
107 'description' => __( 'Display your contact information.' ), |
|
108 ) |
|
109 ); |
|
110 register_block_pattern_category( |
|
111 'about', |
|
112 array( |
|
113 'label' => _x( 'About', 'Block pattern category' ), |
|
114 'description' => __( 'Introduce yourself.' ), |
|
115 ) |
|
116 ); |
|
117 register_block_pattern_category( |
|
118 'portfolio', |
|
119 array( |
|
120 'label' => _x( 'Portfolio', 'Block pattern category' ), |
|
121 'description' => __( 'Showcase your latest work.' ), |
|
122 ) |
|
123 ); |
|
124 register_block_pattern_category( |
|
125 'gallery', |
|
126 array( |
|
127 'label' => _x( 'Gallery', 'Block pattern category' ), |
|
128 'description' => __( 'Different layouts for displaying images.' ), |
|
129 ) |
|
130 ); |
|
131 register_block_pattern_category( |
|
132 'media', |
|
133 array( |
|
134 'label' => _x( 'Media', 'Block pattern category' ), |
|
135 'description' => __( 'Different layouts containing video or audio.' ), |
|
136 ) |
|
137 ); |
|
138 register_block_pattern_category( |
|
139 'videos', |
|
140 array( |
|
141 'label' => _x( 'Videos', 'Block pattern category' ), |
|
142 'description' => __( 'Different layouts containing videos.' ), |
|
143 ) |
|
144 ); |
|
145 register_block_pattern_category( |
|
146 'audio', |
|
147 array( |
|
148 'label' => _x( 'Audio', 'Block pattern category' ), |
|
149 'description' => __( 'Different layouts containing audio.' ), |
|
150 ) |
|
151 ); |
|
152 register_block_pattern_category( |
|
153 'posts', |
|
154 array( |
|
155 'label' => _x( 'Posts', 'Block pattern category' ), |
|
156 'description' => __( 'Display your latest posts in lists, grids or other layouts.' ), |
|
157 ) |
|
158 ); |
|
159 register_block_pattern_category( |
|
160 'footer', |
|
161 array( |
|
162 'label' => _x( 'Footers', 'Block pattern category' ), |
|
163 'description' => __( 'A variety of footer designs displaying information and site navigation.' ), |
|
164 ) |
|
165 ); |
|
166 register_block_pattern_category( |
|
167 'header', |
|
168 array( |
|
169 'label' => _x( 'Headers', 'Block pattern category' ), |
|
170 'description' => __( 'A variety of header designs displaying your site title and navigation.' ), |
|
171 ) |
|
172 ); |
|
173 } |
|
174 |
|
175 /** |
|
176 * Normalize the pattern properties to camelCase. |
|
177 * |
|
178 * The API's format is snake_case, `register_block_pattern()` expects camelCase. |
|
179 * |
|
180 * @since 6.2.0 |
|
181 * @access private |
|
182 * |
|
183 * @param array $pattern Pattern as returned from the Pattern Directory API. |
|
184 * @return array Normalized pattern. |
|
185 */ |
|
186 function wp_normalize_remote_block_pattern( $pattern ) { |
|
187 if ( isset( $pattern['block_types'] ) ) { |
|
188 $pattern['blockTypes'] = $pattern['block_types']; |
|
189 unset( $pattern['block_types'] ); |
|
190 } |
|
191 |
|
192 if ( isset( $pattern['viewport_width'] ) ) { |
|
193 $pattern['viewportWidth'] = $pattern['viewport_width']; |
|
194 unset( $pattern['viewport_width'] ); |
|
195 } |
|
196 |
|
197 return (array) $pattern; |
46 } |
198 } |
47 |
199 |
48 /** |
200 /** |
49 * Register Core's official patterns from wordpress.org/patterns. |
201 * Register Core's official patterns from wordpress.org/patterns. |
50 * |
202 * |
51 * @since 5.8.0 |
203 * @since 5.8.0 |
52 * @since 5.9.0 The $current_screen argument was removed. |
204 * @since 5.9.0 The $current_screen argument was removed. |
|
205 * @since 6.2.0 Normalize the pattern from the API (snake_case) to the |
|
206 * format expected by `register_block_pattern` (camelCase). |
|
207 * @since 6.3.0 Add 'pattern-directory/core' to the pattern's 'source'. |
53 * |
208 * |
54 * @param WP_Screen $deprecated Unused. Formerly the screen that the current request was triggered from. |
209 * @param WP_Screen $deprecated Unused. Formerly the screen that the current request was triggered from. |
55 */ |
210 */ |
56 function _load_remote_block_patterns( $deprecated = null ) { |
211 function _load_remote_block_patterns( $deprecated = null ) { |
57 if ( ! empty( $deprecated ) ) { |
212 if ( ! empty( $deprecated ) ) { |
111 $response = rest_do_request( $request ); |
271 $response = rest_do_request( $request ); |
112 if ( $response->is_error() ) { |
272 if ( $response->is_error() ) { |
113 return; |
273 return; |
114 } |
274 } |
115 $patterns = $response->get_data(); |
275 $patterns = $response->get_data(); |
116 |
276 $registry = WP_Block_Patterns_Registry::get_instance(); |
117 foreach ( $patterns as $pattern ) { |
277 foreach ( $patterns as $pattern ) { |
118 $pattern_name = sanitize_title( $pattern['title'] ); |
278 $pattern['source'] = 'pattern-directory/featured'; |
119 $registry = WP_Block_Patterns_Registry::get_instance(); |
279 $normalized_pattern = wp_normalize_remote_block_pattern( $pattern ); |
|
280 $pattern_name = sanitize_title( $normalized_pattern['title'] ); |
120 // Some patterns might be already registered as core patterns with the `core` prefix. |
281 // Some patterns might be already registered as core patterns with the `core` prefix. |
121 $is_registered = $registry->is_registered( $pattern_name ) || $registry->is_registered( "core/$pattern_name" ); |
282 $is_registered = $registry->is_registered( $pattern_name ) || $registry->is_registered( "core/$pattern_name" ); |
122 if ( ! $is_registered ) { |
283 if ( ! $is_registered ) { |
123 register_block_pattern( $pattern_name, (array) $pattern ); |
284 register_block_pattern( $pattern_name, $normalized_pattern ); |
124 } |
285 } |
125 } |
286 } |
126 } |
287 } |
127 |
288 |
128 /** |
289 /** |
129 * Registers patterns from Pattern Directory provided by a theme's |
290 * Registers patterns from Pattern Directory provided by a theme's |
130 * `theme.json` file. |
291 * `theme.json` file. |
131 * |
292 * |
132 * @since 6.0.0 |
293 * @since 6.0.0 |
|
294 * @since 6.2.0 Normalized the pattern from the API (snake_case) to the |
|
295 * format expected by `register_block_pattern()` (camelCase). |
|
296 * @since 6.3.0 Add 'pattern-directory/theme' to the pattern's 'source'. |
133 * @access private |
297 * @access private |
134 */ |
298 */ |
135 function _register_remote_theme_patterns() { |
299 function _register_remote_theme_patterns() { |
136 /** This filter is documented in wp-includes/block-patterns.php */ |
300 /** This filter is documented in wp-includes/block-patterns.php */ |
137 if ( ! apply_filters( 'should_load_remote_block_patterns', true ) ) { |
301 if ( ! apply_filters( 'should_load_remote_block_patterns', true ) ) { |
138 return; |
302 return; |
139 } |
303 } |
140 |
304 |
141 if ( ! WP_Theme_JSON_Resolver::theme_has_support() ) { |
305 if ( ! wp_theme_has_theme_json() ) { |
142 return; |
306 return; |
143 } |
307 } |
144 |
308 |
145 $pattern_settings = WP_Theme_JSON_Resolver::get_theme_data()->get_patterns(); |
309 $pattern_settings = wp_get_theme_directory_pattern_slugs(); |
146 if ( empty( $pattern_settings ) ) { |
310 if ( empty( $pattern_settings ) ) { |
147 return; |
311 return; |
148 } |
312 } |
149 |
313 |
150 $request = new WP_REST_Request( 'GET', '/wp/v2/pattern-directory/patterns' ); |
314 $request = new WP_REST_Request( 'GET', '/wp/v2/pattern-directory/patterns' ); |
154 return; |
318 return; |
155 } |
319 } |
156 $patterns = $response->get_data(); |
320 $patterns = $response->get_data(); |
157 $patterns_registry = WP_Block_Patterns_Registry::get_instance(); |
321 $patterns_registry = WP_Block_Patterns_Registry::get_instance(); |
158 foreach ( $patterns as $pattern ) { |
322 foreach ( $patterns as $pattern ) { |
159 $pattern_name = sanitize_title( $pattern['title'] ); |
323 $pattern['source'] = 'pattern-directory/theme'; |
|
324 $normalized_pattern = wp_normalize_remote_block_pattern( $pattern ); |
|
325 $pattern_name = sanitize_title( $normalized_pattern['title'] ); |
160 // Some patterns might be already registered as core patterns with the `core` prefix. |
326 // Some patterns might be already registered as core patterns with the `core` prefix. |
161 $is_registered = $patterns_registry->is_registered( $pattern_name ) || $patterns_registry->is_registered( "core/$pattern_name" ); |
327 $is_registered = $patterns_registry->is_registered( $pattern_name ) || $patterns_registry->is_registered( "core/$pattern_name" ); |
162 if ( ! $is_registered ) { |
328 if ( ! $is_registered ) { |
163 register_block_pattern( $pattern_name, (array) $pattern ); |
329 register_block_pattern( $pattern_name, $normalized_pattern ); |
164 } |
330 } |
165 } |
331 } |
166 } |
332 } |
167 |
333 |
168 /** |
334 /** |
169 * Register any patterns that the active theme may provide under its |
335 * Register any patterns that the active theme may provide under its |
170 * `./patterns/` directory. Each pattern is defined as a PHP file and defines |
336 * `./patterns/` directory. |
171 * its metadata using plugin-style headers. The minimum required definition is: |
|
172 * |
|
173 * /** |
|
174 * * Title: My Pattern |
|
175 * * Slug: my-theme/my-pattern |
|
176 * * |
|
177 * |
|
178 * The output of the PHP source corresponds to the content of the pattern, e.g.: |
|
179 * |
|
180 * <main><p><?php echo "Hello"; ?></p></main> |
|
181 * |
|
182 * If applicable, this will collect from both parent and child theme. |
|
183 * |
|
184 * Other settable fields include: |
|
185 * |
|
186 * - Description |
|
187 * - Viewport Width |
|
188 * - Categories (comma-separated values) |
|
189 * - Keywords (comma-separated values) |
|
190 * - Block Types (comma-separated values) |
|
191 * - Inserter (yes/no) |
|
192 * |
337 * |
193 * @since 6.0.0 |
338 * @since 6.0.0 |
|
339 * @since 6.1.0 The `postTypes` property was added. |
|
340 * @since 6.2.0 The `templateTypes` property was added. |
|
341 * @since 6.4.0 Uses the `WP_Theme::get_block_patterns` method. |
194 * @access private |
342 * @access private |
195 */ |
343 */ |
196 function _register_theme_block_patterns() { |
344 function _register_theme_block_patterns() { |
197 $default_headers = array( |
345 |
198 'title' => 'Title', |
346 /* |
199 'slug' => 'Slug', |
347 * During the bootstrap process, a check for active and valid themes is run. |
200 'description' => 'Description', |
348 * If no themes are returned, the theme's functions.php file will not be loaded, |
201 'viewportWidth' => 'Viewport Width', |
349 * which can lead to errors if patterns expect some variables or constants to |
202 'categories' => 'Categories', |
350 * already be set at this point, so bail early if that is the case. |
203 'keywords' => 'Keywords', |
351 */ |
204 'blockTypes' => 'Block Types', |
352 if ( empty( wp_get_active_and_valid_themes() ) ) { |
205 'inserter' => 'Inserter', |
353 return; |
206 ); |
354 } |
207 |
355 |
208 /* |
356 /* |
209 * Register patterns for the active theme. If the theme is a child theme, |
357 * Register patterns for the active theme. If the theme is a child theme, |
210 * let it override any patterns from the parent theme that shares the same slug. |
358 * let it override any patterns from the parent theme that shares the same slug. |
211 */ |
359 */ |
212 $themes = array(); |
360 $themes = array(); |
213 $stylesheet = get_stylesheet(); |
361 $theme = wp_get_theme(); |
214 $template = get_template(); |
362 $themes[] = $theme; |
215 if ( $stylesheet !== $template ) { |
363 if ( $theme->parent() ) { |
216 $themes[] = wp_get_theme( $stylesheet ); |
364 $themes[] = $theme->parent(); |
217 } |
365 } |
218 $themes[] = wp_get_theme( $template ); |
366 $registry = WP_Block_Patterns_Registry::get_instance(); |
219 |
367 |
220 foreach ( $themes as $theme ) { |
368 foreach ( $themes as $theme ) { |
221 $dirpath = $theme->get_stylesheet_directory() . '/patterns/'; |
369 $patterns = $theme->get_block_patterns(); |
222 if ( ! is_dir( $dirpath ) || ! is_readable( $dirpath ) ) { |
370 $dirpath = $theme->get_stylesheet_directory() . '/patterns/'; |
223 continue; |
371 $text_domain = $theme->get( 'TextDomain' ); |
224 } |
372 |
225 if ( file_exists( $dirpath ) ) { |
373 foreach ( $patterns as $file => $pattern_data ) { |
226 $files = glob( $dirpath . '*.php' ); |
374 if ( $registry->is_registered( $pattern_data['slug'] ) ) { |
227 if ( $files ) { |
375 continue; |
228 foreach ( $files as $file ) { |
|
229 $pattern_data = get_file_data( $file, $default_headers ); |
|
230 |
|
231 if ( empty( $pattern_data['slug'] ) ) { |
|
232 _doing_it_wrong( |
|
233 '_register_theme_block_patterns', |
|
234 sprintf( |
|
235 /* translators: %s: file name. */ |
|
236 __( 'Could not register file "%s" as a block pattern ("Slug" field missing)' ), |
|
237 $file |
|
238 ), |
|
239 '6.0.0' |
|
240 ); |
|
241 continue; |
|
242 } |
|
243 |
|
244 if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern_data['slug'] ) ) { |
|
245 _doing_it_wrong( |
|
246 '_register_theme_block_patterns', |
|
247 sprintf( |
|
248 /* translators: %1s: file name; %2s: slug value found. */ |
|
249 __( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")' ), |
|
250 $file, |
|
251 $pattern_data['slug'] |
|
252 ), |
|
253 '6.0.0' |
|
254 ); |
|
255 } |
|
256 |
|
257 if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_data['slug'] ) ) { |
|
258 continue; |
|
259 } |
|
260 |
|
261 // Title is a required property. |
|
262 if ( ! $pattern_data['title'] ) { |
|
263 _doing_it_wrong( |
|
264 '_register_theme_block_patterns', |
|
265 sprintf( |
|
266 /* translators: %1s: file name; %2s: slug value found. */ |
|
267 __( 'Could not register file "%s" as a block pattern ("Title" field missing)' ), |
|
268 $file |
|
269 ), |
|
270 '6.0.0' |
|
271 ); |
|
272 continue; |
|
273 } |
|
274 |
|
275 // For properties of type array, parse data as comma-separated. |
|
276 foreach ( array( 'categories', 'keywords', 'blockTypes' ) as $property ) { |
|
277 if ( ! empty( $pattern_data[ $property ] ) ) { |
|
278 $pattern_data[ $property ] = array_filter( |
|
279 preg_split( |
|
280 '/[\s,]+/', |
|
281 (string) $pattern_data[ $property ] |
|
282 ) |
|
283 ); |
|
284 } else { |
|
285 unset( $pattern_data[ $property ] ); |
|
286 } |
|
287 } |
|
288 |
|
289 // Parse properties of type int. |
|
290 foreach ( array( 'viewportWidth' ) as $property ) { |
|
291 if ( ! empty( $pattern_data[ $property ] ) ) { |
|
292 $pattern_data[ $property ] = (int) $pattern_data[ $property ]; |
|
293 } else { |
|
294 unset( $pattern_data[ $property ] ); |
|
295 } |
|
296 } |
|
297 |
|
298 // Parse properties of type bool. |
|
299 foreach ( array( 'inserter' ) as $property ) { |
|
300 if ( ! empty( $pattern_data[ $property ] ) ) { |
|
301 $pattern_data[ $property ] = in_array( |
|
302 strtolower( $pattern_data[ $property ] ), |
|
303 array( 'yes', 'true' ), |
|
304 true |
|
305 ); |
|
306 } else { |
|
307 unset( $pattern_data[ $property ] ); |
|
308 } |
|
309 } |
|
310 |
|
311 // Translate the pattern metadata. |
|
312 $text_domain = $theme->get( 'TextDomain' ); |
|
313 //phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText, WordPress.WP.I18n.NonSingularStringLiteralContext, WordPress.WP.I18n.NonSingularStringLiteralDomain, WordPress.WP.I18n.LowLevelTranslationFunction |
|
314 $pattern_data['title'] = translate_with_gettext_context( $pattern_data['title'], 'Pattern title', $text_domain ); |
|
315 if ( ! empty( $pattern_data['description'] ) ) { |
|
316 //phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText, WordPress.WP.I18n.NonSingularStringLiteralContext, WordPress.WP.I18n.NonSingularStringLiteralDomain, WordPress.WP.I18n.LowLevelTranslationFunction |
|
317 $pattern_data['description'] = translate_with_gettext_context( $pattern_data['description'], 'Pattern description', $text_domain ); |
|
318 } |
|
319 |
|
320 // The actual pattern content is the output of the file. |
|
321 ob_start(); |
|
322 include $file; |
|
323 $pattern_data['content'] = ob_get_clean(); |
|
324 if ( ! $pattern_data['content'] ) { |
|
325 continue; |
|
326 } |
|
327 |
|
328 register_block_pattern( $pattern_data['slug'], $pattern_data ); |
|
329 } |
|
330 } |
376 } |
|
377 |
|
378 $file_path = $dirpath . $file; |
|
379 |
|
380 if ( ! file_exists( $file_path ) ) { |
|
381 _doing_it_wrong( |
|
382 __FUNCTION__, |
|
383 sprintf( |
|
384 /* translators: %s: file name. */ |
|
385 __( 'Could not register file "%s" as a block pattern as the file does not exist.' ), |
|
386 $file |
|
387 ), |
|
388 '6.4.0' |
|
389 ); |
|
390 $theme->delete_pattern_cache(); |
|
391 continue; |
|
392 } |
|
393 |
|
394 $pattern_data['filePath'] = $file_path; |
|
395 |
|
396 // Translate the pattern metadata. |
|
397 // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction |
|
398 $pattern_data['title'] = translate_with_gettext_context( $pattern_data['title'], 'Pattern title', $text_domain ); |
|
399 if ( ! empty( $pattern_data['description'] ) ) { |
|
400 // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction |
|
401 $pattern_data['description'] = translate_with_gettext_context( $pattern_data['description'], 'Pattern description', $text_domain ); |
|
402 } |
|
403 |
|
404 register_block_pattern( $pattern_data['slug'], $pattern_data ); |
331 } |
405 } |
332 } |
406 } |
333 } |
407 } |
334 add_action( 'init', '_register_theme_block_patterns' ); |
408 add_action( 'init', '_register_theme_block_patterns' ); |