From ba1bbc6539318a8b4515816cffde6689fc44c1d9 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Mon, 22 Nov 2021 19:53:09 +0000 Subject: [PATCH 01/19] Try: Block Patterns: Automatically load headered files from a theme's /patterns directory --- lib/block-patterns.php | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/lib/block-patterns.php b/lib/block-patterns.php index d3585a774de27d..dc35cc20a28c44 100644 --- a/lib/block-patterns.php +++ b/lib/block-patterns.php @@ -209,3 +209,79 @@ function() { gutenberg_register_gutenberg_patterns(); } ); + +/** + * FIXME. + * + * @since 5.9.0 + */ +function _register_theme_block_patterns() { + $default_headers = array( + 'title' => 'Pattern Name', + 'description' => 'Description', + 'viewportWidth' => 'Viewport Width', + 'categories' => 'Categories', + 'keywords' => 'Keywords', + 'blockTypes' => 'Block Types', + ); + + // Register patterns for the active theme, for both parent and child theme, + // if applicable. + foreach ( wp_get_active_and_valid_themes() as $theme ) { + $dirpath = $theme . '/patterns/'; + if ( file_exists( $dirpath ) ) { + $files = glob( $dirpath . '*.php' ); + if ( $files ) { + foreach ( $files as $file ) { + + // Parse pattern slug from file name. + if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.php$#', $file, $matches ) ) { + continue; // FIXME: Consider logging notice. + } + // Example name: twentytwentytwo/query-grid-posts. + $pattern_name = get_stylesheet() . '/' . $matches['slug']; + + $pattern_data = get_file_data( $file, $default_headers ); + + // Title is a required property. + if ( ! $pattern_data['title'] ) { + continue; + } + + // For properties of type array, parse data as comma-separated. + foreach ( array( 'categories', 'keywords', 'blockTypes' ) as $property ) { + $pattern_data[ $property ] = array_filter( + preg_split( + '/[\s,]+/', + (string) $pattern_data[ $property ] + ) + ); + } + + // Parse properties of type int. + foreach ( array( 'viewportWidth' ) as $property ) { + $pattern_data[ $property ] = (int) $pattern_data[ $property ]; + } + + // Remove up empty values, so as not to override defaults. + foreach ( array_keys( $default_headers ) as $property ) { + if ( empty( $pattern_data[ $property ] ) ) { + unset( $pattern_data[ $property ] ); + } + } + + // The actual pattern content is the output of the file. + ob_start(); + include $file; + $pattern_data['content'] = ob_get_clean(); + if ( ! $pattern_data['content'] ) { + continue; + } + + register_block_pattern( $pattern_name, $pattern_data ); + } + } + } + } +} +add_action( 'init', '_register_theme_block_patterns' ); From 98fb90345be0a2c51b7cbeb55391b8f0d897ebc5 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Tue, 23 Nov 2021 16:49:01 +0000 Subject: [PATCH 02/19] Block Patterns: Also load patterns from bare HTML files Alternative take based on https://github.com/WordPress/gutenberg/pull/36751#issuecomment-976799185 --- lib/block-patterns.php | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/lib/block-patterns.php b/lib/block-patterns.php index dc35cc20a28c44..7cc56fdf003a28 100644 --- a/lib/block-patterns.php +++ b/lib/block-patterns.php @@ -229,6 +229,61 @@ function _register_theme_block_patterns() { // if applicable. foreach ( wp_get_active_and_valid_themes() as $theme ) { $dirpath = $theme . '/patterns/'; + if ( file_exists( $dirpath ) ) { + $files = glob( $dirpath . '*.html' ); + if ( $files ) { + foreach ( $files as $file ) { + // Parse pattern slug from file name. + if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.html$#', $file, $matches ) ) { + continue; // FIXME: Consider logging notice. + } + // Example name: twentytwentytwo/query-grid-posts. + $pattern_name = get_stylesheet() . '/' . $matches['slug']; + + $pattern_data = get_file_data( $file, $default_headers ); + + // Title is a required property. + if ( ! $pattern_data['title'] ) { + continue; + } + + // For properties of type array, parse data as comma-separated. + foreach ( array( 'categories', 'keywords', 'blockTypes' ) as $property ) { + $pattern_data[ $property ] = array_filter( + preg_split( + '/[\s,]+/', + (string) $pattern_data[ $property ] + ) + ); + } + + // Parse properties of type int. + foreach ( array( 'viewportWidth' ) as $property ) { + $pattern_data[ $property ] = (int) $pattern_data[ $property ]; + } + + // Remove up empty values, so as not to override defaults. + foreach ( array_keys( $default_headers ) as $property ) { + if ( empty( $pattern_data[ $property ] ) ) { + unset( $pattern_data[ $property ] ); + } + } + + // The actual pattern is everything following the leading comment. + $raw_content = file_get_contents( $file ); + $token = '-->'; + $pattern_data['content'] = substr( + $raw_content, + strpos( $raw_content, $token ) + strlen( $token ) + ); + if ( ! $pattern_data['content'] ) { + continue; + } + + register_block_pattern( $pattern_name, $pattern_data ); + } + } + } if ( file_exists( $dirpath ) ) { $files = glob( $dirpath . '*.php' ); if ( $files ) { From a585298b0dae467977309d086924f976f423fad3 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Tue, 8 Mar 2022 13:23:04 +0100 Subject: [PATCH 03/19] Remove support for HTML files, at least for this PR --- lib/block-patterns.php | 56 ------------------------------------------ 1 file changed, 56 deletions(-) diff --git a/lib/block-patterns.php b/lib/block-patterns.php index 7cc56fdf003a28..9f01bba1f37630 100644 --- a/lib/block-patterns.php +++ b/lib/block-patterns.php @@ -229,66 +229,10 @@ function _register_theme_block_patterns() { // if applicable. foreach ( wp_get_active_and_valid_themes() as $theme ) { $dirpath = $theme . '/patterns/'; - if ( file_exists( $dirpath ) ) { - $files = glob( $dirpath . '*.html' ); - if ( $files ) { - foreach ( $files as $file ) { - // Parse pattern slug from file name. - if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.html$#', $file, $matches ) ) { - continue; // FIXME: Consider logging notice. - } - // Example name: twentytwentytwo/query-grid-posts. - $pattern_name = get_stylesheet() . '/' . $matches['slug']; - - $pattern_data = get_file_data( $file, $default_headers ); - - // Title is a required property. - if ( ! $pattern_data['title'] ) { - continue; - } - - // For properties of type array, parse data as comma-separated. - foreach ( array( 'categories', 'keywords', 'blockTypes' ) as $property ) { - $pattern_data[ $property ] = array_filter( - preg_split( - '/[\s,]+/', - (string) $pattern_data[ $property ] - ) - ); - } - - // Parse properties of type int. - foreach ( array( 'viewportWidth' ) as $property ) { - $pattern_data[ $property ] = (int) $pattern_data[ $property ]; - } - - // Remove up empty values, so as not to override defaults. - foreach ( array_keys( $default_headers ) as $property ) { - if ( empty( $pattern_data[ $property ] ) ) { - unset( $pattern_data[ $property ] ); - } - } - - // The actual pattern is everything following the leading comment. - $raw_content = file_get_contents( $file ); - $token = '-->'; - $pattern_data['content'] = substr( - $raw_content, - strpos( $raw_content, $token ) + strlen( $token ) - ); - if ( ! $pattern_data['content'] ) { - continue; - } - - register_block_pattern( $pattern_name, $pattern_data ); - } - } - } if ( file_exists( $dirpath ) ) { $files = glob( $dirpath . '*.php' ); if ( $files ) { foreach ( $files as $file ) { - // Parse pattern slug from file name. if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.php$#', $file, $matches ) ) { continue; // FIXME: Consider logging notice. From fb8cce6282413f54a4ac2d65eb5482f9d9a5ab50 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Tue, 8 Mar 2022 13:23:41 +0100 Subject: [PATCH 04/19] register_theme_block_patterns: Document and mark as private --- lib/block-patterns.php | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/block-patterns.php b/lib/block-patterns.php index 9f01bba1f37630..8f6f06bcbc8694 100644 --- a/lib/block-patterns.php +++ b/lib/block-patterns.php @@ -211,11 +211,25 @@ function() { ); /** - * FIXME. + * Register any patterns that the active theme may provide under its + * `./patterns/` directory. Each pattern is defined as a PHP file and defines + * its metadata using plugin-style headers. The minimum required definition is: + * + * /** + * * Pattern Name: My Pattern + * * + * + * The output of the PHP source corresponds to the content of the pattern, e.g.: + * + *

+ * + * If applicable, this will collect from both parent and child theme. * * @since 5.9.0 + * @access private + * @internal */ -function _register_theme_block_patterns() { +function register_theme_block_patterns() { $default_headers = array( 'title' => 'Pattern Name', 'description' => 'Description', @@ -283,4 +297,4 @@ function _register_theme_block_patterns() { } } } -add_action( 'init', '_register_theme_block_patterns' ); +add_action( 'init', 'register_theme_block_patterns' ); From f666088436d5769b152d94e3728dce4b5d91e431 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Fri, 11 Mar 2022 13:22:55 +0000 Subject: [PATCH 05/19] Bump 'since' version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Props gziolo Co-authored-by: Greg Ziółkowski --- lib/block-patterns.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/block-patterns.php b/lib/block-patterns.php index 8f6f06bcbc8694..ab8d4f7c71702b 100644 --- a/lib/block-patterns.php +++ b/lib/block-patterns.php @@ -225,7 +225,7 @@ function() { * * If applicable, this will collect from both parent and child theme. * - * @since 5.9.0 + * @since 6.0.0 * @access private * @internal */ From 388e8d16baab99658c3cf4094a5b6a8375eff050 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Mon, 14 Mar 2022 11:20:46 +0000 Subject: [PATCH 06/19] Move to compat/wordpress-6.0, prefix with "gutenberg_" Per feedback from oandregal --- lib/block-patterns.php | 89 --------------------- lib/compat/wordpress-6.0/block-patterns.php | 89 +++++++++++++++++++++ 2 files changed, 89 insertions(+), 89 deletions(-) diff --git a/lib/block-patterns.php b/lib/block-patterns.php index ab8d4f7c71702b..d3585a774de27d 100644 --- a/lib/block-patterns.php +++ b/lib/block-patterns.php @@ -209,92 +209,3 @@ function() { gutenberg_register_gutenberg_patterns(); } ); - -/** - * Register any patterns that the active theme may provide under its - * `./patterns/` directory. Each pattern is defined as a PHP file and defines - * its metadata using plugin-style headers. The minimum required definition is: - * - * /** - * * Pattern Name: My Pattern - * * - * - * The output of the PHP source corresponds to the content of the pattern, e.g.: - * - *

- * - * If applicable, this will collect from both parent and child theme. - * - * @since 6.0.0 - * @access private - * @internal - */ -function register_theme_block_patterns() { - $default_headers = array( - 'title' => 'Pattern Name', - 'description' => 'Description', - 'viewportWidth' => 'Viewport Width', - 'categories' => 'Categories', - 'keywords' => 'Keywords', - 'blockTypes' => 'Block Types', - ); - - // Register patterns for the active theme, for both parent and child theme, - // if applicable. - foreach ( wp_get_active_and_valid_themes() as $theme ) { - $dirpath = $theme . '/patterns/'; - if ( file_exists( $dirpath ) ) { - $files = glob( $dirpath . '*.php' ); - if ( $files ) { - foreach ( $files as $file ) { - // Parse pattern slug from file name. - if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.php$#', $file, $matches ) ) { - continue; // FIXME: Consider logging notice. - } - // Example name: twentytwentytwo/query-grid-posts. - $pattern_name = get_stylesheet() . '/' . $matches['slug']; - - $pattern_data = get_file_data( $file, $default_headers ); - - // Title is a required property. - if ( ! $pattern_data['title'] ) { - continue; - } - - // For properties of type array, parse data as comma-separated. - foreach ( array( 'categories', 'keywords', 'blockTypes' ) as $property ) { - $pattern_data[ $property ] = array_filter( - preg_split( - '/[\s,]+/', - (string) $pattern_data[ $property ] - ) - ); - } - - // Parse properties of type int. - foreach ( array( 'viewportWidth' ) as $property ) { - $pattern_data[ $property ] = (int) $pattern_data[ $property ]; - } - - // Remove up empty values, so as not to override defaults. - foreach ( array_keys( $default_headers ) as $property ) { - if ( empty( $pattern_data[ $property ] ) ) { - unset( $pattern_data[ $property ] ); - } - } - - // The actual pattern content is the output of the file. - ob_start(); - include $file; - $pattern_data['content'] = ob_get_clean(); - if ( ! $pattern_data['content'] ) { - continue; - } - - register_block_pattern( $pattern_name, $pattern_data ); - } - } - } - } -} -add_action( 'init', 'register_theme_block_patterns' ); diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index d5b131c88f5300..96aa98540389e6 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -51,3 +51,92 @@ function( $current_screen ) { } } ); + +/** + * Register any patterns that the active theme may provide under its + * `./patterns/` directory. Each pattern is defined as a PHP file and defines + * its metadata using plugin-style headers. The minimum required definition is: + * + * /** + * * Pattern Name: My Pattern + * * + * + * The output of the PHP source corresponds to the content of the pattern, e.g.: + * + *

+ * + * If applicable, this will collect from both parent and child theme. + * + * @since 6.0.0 + * @access private + * @internal + */ +function gutenberg_register_theme_block_patterns() { + $default_headers = array( + 'title' => 'Pattern Name', + 'description' => 'Description', + 'viewportWidth' => 'Viewport Width', + 'categories' => 'Categories', + 'keywords' => 'Keywords', + 'blockTypes' => 'Block Types', + ); + + // Register patterns for the active theme, for both parent and child theme, + // if applicable. + foreach ( wp_get_active_and_valid_themes() as $theme ) { + $dirpath = $theme . '/patterns/'; + if ( file_exists( $dirpath ) ) { + $files = glob( $dirpath . '*.php' ); + if ( $files ) { + foreach ( $files as $file ) { + // Parse pattern slug from file name. + if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.php$#', $file, $matches ) ) { + continue; // FIXME: Consider logging notice. + } + // Example name: twentytwentytwo/query-grid-posts. + $pattern_name = get_stylesheet() . '/' . $matches['slug']; + + $pattern_data = get_file_data( $file, $default_headers ); + + // Title is a required property. + if ( ! $pattern_data['title'] ) { + continue; + } + + // For properties of type array, parse data as comma-separated. + foreach ( array( 'categories', 'keywords', 'blockTypes' ) as $property ) { + $pattern_data[ $property ] = array_filter( + preg_split( + '/[\s,]+/', + (string) $pattern_data[ $property ] + ) + ); + } + + // Parse properties of type int. + foreach ( array( 'viewportWidth' ) as $property ) { + $pattern_data[ $property ] = (int) $pattern_data[ $property ]; + } + + // Remove up empty values, so as not to override defaults. + foreach ( array_keys( $default_headers ) as $property ) { + if ( empty( $pattern_data[ $property ] ) ) { + unset( $pattern_data[ $property ] ); + } + } + + // The actual pattern content is the output of the file. + ob_start(); + include $file; + $pattern_data['content'] = ob_get_clean(); + if ( ! $pattern_data['content'] ) { + continue; + } + + register_block_pattern( $pattern_name, $pattern_data ); + } + } + } + } +} +add_action( 'init', 'gutenberg_register_theme_block_patterns' ); From 72ab5261816c8c674480e460e995e951a31a92b9 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Mon, 14 Mar 2022 18:14:29 +0000 Subject: [PATCH 07/19] Support "Inserter" param --- lib/compat/wordpress-6.0/block-patterns.php | 32 +++++++++++++-------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index 96aa98540389e6..700be10d1725a3 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -79,6 +79,7 @@ function gutenberg_register_theme_block_patterns() { 'categories' => 'Categories', 'keywords' => 'Keywords', 'blockTypes' => 'Block Types', + 'inserter' => 'Inserter', ); // Register patterns for the active theme, for both parent and child theme, @@ -105,24 +106,31 @@ function gutenberg_register_theme_block_patterns() { // For properties of type array, parse data as comma-separated. foreach ( array( 'categories', 'keywords', 'blockTypes' ) as $property ) { - $pattern_data[ $property ] = array_filter( - preg_split( - '/[\s,]+/', - (string) $pattern_data[ $property ] - ) - ); + if ( ! empty( $pattern_data[ $property ] ) ) { + $pattern_data[ $property ] = array_filter( + preg_split( + '/[\s,]+/', + (string) $pattern_data[ $property ] + ) + ); + } else unset( $pattern_data[ $property ] ); } // Parse properties of type int. foreach ( array( 'viewportWidth' ) as $property ) { - $pattern_data[ $property ] = (int) $pattern_data[ $property ]; + if ( ! empty( $pattern_data[ $property ] ) ) { + $pattern_data[ $property ] = (int) $pattern_data[ $property ]; + } else unset( $pattern_data[ $property ] ); } - // Remove up empty values, so as not to override defaults. - foreach ( array_keys( $default_headers ) as $property ) { - if ( empty( $pattern_data[ $property ] ) ) { - unset( $pattern_data[ $property ] ); - } + // Parse properties of type bool. + foreach ( array( 'inserter' ) as $property ) { + if ( ! empty( $pattern_data[ $property ] ) ) { + $pattern_data[ $property ] = in_array( + strtolower( $pattern_data[ $property ] ), + array( "yes", "true" ) + ); + } else unset( $pattern_data[ $property ] ); } // The actual pattern content is the output of the file. From a915083e3e91f3b3a4709e252537f24a5950f46e Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Mon, 14 Mar 2022 18:15:18 +0000 Subject: [PATCH 08/19] Let child themes override parent's patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … by simply letting children register theirs first. --- lib/compat/wordpress-6.0/block-patterns.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index 700be10d1725a3..83dfffa94129d8 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -82,9 +82,11 @@ function gutenberg_register_theme_block_patterns() { 'inserter' => 'Inserter', ); - // Register patterns for the active theme, for both parent and child theme, - // if applicable. - foreach ( wp_get_active_and_valid_themes() as $theme ) { + // Register patterns for the active theme. If the theme is a child theme, + // let it register its patterns last, thereby overriding any of the + // parent's patterns sharing the same slug. + $parent_then_child_themes = array_reverse( wp_get_active_and_valid_themes() ); + foreach ( $parent_then_child_themes as $theme ) { $dirpath = $theme . '/patterns/'; if ( file_exists( $dirpath ) ) { $files = glob( $dirpath . '*.php' ); From f06c16bda7793518f2d3d1a7b3fad995165d69c0 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Tue, 15 Mar 2022 11:53:07 +0000 Subject: [PATCH 09/19] Hook on 'admin_init' 'init' was a provisional hook. The idea was to eventually hook on something tied to the block editors' (post, site, etc.) loading cycles. `enqueue_block_editor_assets` is triggered too late; we would need a hook just before the `$editor_settings` array is computed. Perhaps we could introduce something like a `pre_block_editor_settings` hook. In the meantime, `admin_init` should be an acceptable choice that allows to avoid registering patterns in the front-end rendering pipeline. --- lib/compat/wordpress-6.0/block-patterns.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index 83dfffa94129d8..6baed124d2fbfa 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -149,4 +149,4 @@ function gutenberg_register_theme_block_patterns() { } } } -add_action( 'init', 'gutenberg_register_theme_block_patterns' ); +add_action( 'admin_init', 'gutenberg_register_theme_block_patterns' ); From 2e2fa4ce11fe3e9827fae6a7850e34a0f746428b Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Tue, 15 Mar 2022 12:20:17 +0000 Subject: [PATCH 10/19] Expand function documentation --- lib/compat/wordpress-6.0/block-patterns.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index 6baed124d2fbfa..93f9ba40951263 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -67,6 +67,15 @@ function( $current_screen ) { * * If applicable, this will collect from both parent and child theme. * + * Other settable fields include: + * + * - Description + * - Viewport Width + * - Categories (comma-separated values) + * - Keywords (comma-separated values) + * - Block Types (comma-separated values) + * - Inserter (yes/no) + * * @since 6.0.0 * @access private * @internal From 556e1e2eb5e76c89c170ab2355d65b23dcf7c550 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Tue, 15 Mar 2022 12:20:41 +0000 Subject: [PATCH 11/19] Remove FIXME note --- lib/compat/wordpress-6.0/block-patterns.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index 93f9ba40951263..b9c09d13de8512 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -103,7 +103,7 @@ function gutenberg_register_theme_block_patterns() { foreach ( $files as $file ) { // Parse pattern slug from file name. if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.php$#', $file, $matches ) ) { - continue; // FIXME: Consider logging notice. + continue; } // Example name: twentytwentytwo/query-grid-posts. $pattern_name = get_stylesheet() . '/' . $matches['slug']; From 7b5a0866355a648212ab39825a5b7da8540de570 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Tue, 15 Mar 2022 14:22:45 +0000 Subject: [PATCH 12/19] Revert to hooking to 'init' The Pattern block, which block templates can use, is dynamic, sourcing patterns from WP_Block_Patterns_Registry. So we need a much broader hook, such as `init`, to make sure file-based patterns are still registered on the front end. --- lib/compat/wordpress-6.0/block-patterns.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index b9c09d13de8512..3eb18a027d0796 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -158,4 +158,4 @@ function gutenberg_register_theme_block_patterns() { } } } -add_action( 'admin_init', 'gutenberg_register_theme_block_patterns' ); +add_action( 'init', 'gutenberg_register_theme_block_patterns' ); From 3df71d9d39acf4002892b63f8ac725fb946538ed Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Tue, 15 Mar 2022 15:17:26 +0000 Subject: [PATCH 13/19] Skip registration if pattern already registered Alternative to cb99311. In order for child themes to preserve the ability to override parent patterns, this commit also reverts to visiting `wp_get_active_and_valid_themes` in the canonical order (child then parent). --- lib/compat/wordpress-6.0/block-patterns.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index 3eb18a027d0796..a4a760b64416c8 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -92,10 +92,9 @@ function gutenberg_register_theme_block_patterns() { ); // Register patterns for the active theme. If the theme is a child theme, - // let it register its patterns last, thereby overriding any of the - // parent's patterns sharing the same slug. - $parent_then_child_themes = array_reverse( wp_get_active_and_valid_themes() ); - foreach ( $parent_then_child_themes as $theme ) { + // let it override any patterns from the parent theme that shares the same + // slug. + foreach ( wp_get_active_and_valid_themes() as $theme ) { $dirpath = $theme . '/patterns/'; if ( file_exists( $dirpath ) ) { $files = glob( $dirpath . '*.php' ); @@ -108,6 +107,10 @@ function gutenberg_register_theme_block_patterns() { // Example name: twentytwentytwo/query-grid-posts. $pattern_name = get_stylesheet() . '/' . $matches['slug']; + if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_name ) ) { + continue; + } + $pattern_data = get_file_data( $file, $default_headers ); // Title is a required property. From bc8471f23397c6f5b0a88107412626e606a268cb Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Wed, 16 Mar 2022 15:28:32 +0000 Subject: [PATCH 14/19] Add mandatory "Namespace" field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … as opposed to implicitly tying a pattern to the stylesheet as obtained via `get_stylesheet`. This may be heavy-handed, but it leaves it up to pattern and theme authors to define their naming schema. See discussion in https://github.com/WordPress/gutenberg/pull/36751 --- lib/compat/wordpress-6.0/block-patterns.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index a4a760b64416c8..b3dc0bd9f39c34 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -59,6 +59,7 @@ function( $current_screen ) { * * /** * * Pattern Name: My Pattern + * * Namespace: my-theme * * * * The output of the PHP source corresponds to the content of the pattern, e.g.: @@ -83,6 +84,7 @@ function( $current_screen ) { function gutenberg_register_theme_block_patterns() { $default_headers = array( 'title' => 'Pattern Name', + 'namespace' => 'Namespace', 'description' => 'Description', 'viewportWidth' => 'Viewport Width', 'categories' => 'Categories', @@ -104,15 +106,20 @@ function gutenberg_register_theme_block_patterns() { if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.php$#', $file, $matches ) ) { continue; } + + $pattern_data = get_file_data( $file, $default_headers ); + + if ( empty( $pattern_data[ 'namespace' ] ) ) { + continue; + } + // Example name: twentytwentytwo/query-grid-posts. - $pattern_name = get_stylesheet() . '/' . $matches['slug']; + $pattern_name = $pattern_data[ 'namespace' ] . "/" . $matches['slug']; if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_name ) ) { continue; } - $pattern_data = get_file_data( $file, $default_headers ); - // Title is a required property. if ( ! $pattern_data['title'] ) { continue; From 288e7f5eb673a6e73576f828fcc1b61b808a0a8d Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Thu, 17 Mar 2022 16:10:04 +0000 Subject: [PATCH 15/19] Replace mandatory Namespace field with optional Slug --- lib/compat/wordpress-6.0/block-patterns.php | 29 +++++++++++---------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index b3dc0bd9f39c34..18c2bb44ba264b 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -59,7 +59,6 @@ function( $current_screen ) { * * /** * * Pattern Name: My Pattern - * * Namespace: my-theme * * * * The output of the PHP source corresponds to the content of the pattern, e.g.: @@ -70,6 +69,7 @@ function( $current_screen ) { * * Other settable fields include: * + * - Slug (otherwise inferred from filename) * - Description * - Viewport Width * - Categories (comma-separated values) @@ -84,7 +84,7 @@ function( $current_screen ) { function gutenberg_register_theme_block_patterns() { $default_headers = array( 'title' => 'Pattern Name', - 'namespace' => 'Namespace', + 'slug' => 'Slug', 'description' => 'Description', 'viewportWidth' => 'Viewport Width', 'categories' => 'Categories', @@ -102,21 +102,22 @@ function gutenberg_register_theme_block_patterns() { $files = glob( $dirpath . '*.php' ); if ( $files ) { foreach ( $files as $file ) { - // Parse pattern slug from file name. - if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.php$#', $file, $matches ) ) { - continue; - } - $pattern_data = get_file_data( $file, $default_headers ); - if ( empty( $pattern_data[ 'namespace' ] ) ) { - continue; - } + $pattern_slug = $pattern_data[ 'slug' ]; + + // Compute slug from the theme and the basename of the pattern file. + // + // Example: twentytwentytwo/query-grid-posts. + if ( empty( $pattern_slug ) ) { + if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.php$#', $file, $matches ) ) { + continue; + } - // Example name: twentytwentytwo/query-grid-posts. - $pattern_name = $pattern_data[ 'namespace' ] . "/" . $matches['slug']; + $pattern_slug = get_stylesheet() . "/" . $matches[ 'slug' ]; + } - if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_name ) ) { + if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_slug ) ) { continue; } @@ -162,7 +163,7 @@ function gutenberg_register_theme_block_patterns() { continue; } - register_block_pattern( $pattern_name, $pattern_data ); + register_block_pattern( $pattern_slug, $pattern_data ); } } } From cd97aef7ef0656c5a142ce863404d892e54a5b4d Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Thu, 17 Mar 2022 16:26:56 +0000 Subject: [PATCH 16/19] Rename Pattern Name to Title --- lib/compat/wordpress-6.0/block-patterns.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index 18c2bb44ba264b..7d63dad1f9293a 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -58,7 +58,7 @@ function( $current_screen ) { * its metadata using plugin-style headers. The minimum required definition is: * * /** - * * Pattern Name: My Pattern + * * Title: My Pattern * * * * The output of the PHP source corresponds to the content of the pattern, e.g.: @@ -83,7 +83,7 @@ function( $current_screen ) { */ function gutenberg_register_theme_block_patterns() { $default_headers = array( - 'title' => 'Pattern Name', + 'title' => 'Title', 'slug' => 'Slug', 'description' => 'Description', 'viewportWidth' => 'Viewport Width', From 5d4594df1c7f6bb7edf9f2d4ca07679f61a77067 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Thu, 17 Mar 2022 16:40:23 +0000 Subject: [PATCH 17/19] Add notice reporting --- lib/compat/wordpress-6.0/block-patterns.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index 7d63dad1f9293a..68805ae94a7cc5 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -111,6 +111,12 @@ function gutenberg_register_theme_block_patterns() { // Example: twentytwentytwo/query-grid-posts. if ( empty( $pattern_slug ) ) { if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.php$#', $file, $matches ) ) { + trigger_error( + sprintf( + __( 'Could not register file "%s" as a block pattern ("Slug" field missing)', 'gutenberg' ), + $file + ) + ); continue; } @@ -123,6 +129,12 @@ function gutenberg_register_theme_block_patterns() { // Title is a required property. if ( ! $pattern_data['title'] ) { + trigger_error( + sprintf( + __( 'Could not register file "%s" as a block pattern ("Title" field missing)', 'gutenberg' ), + $file + ) + ); continue; } From 1f06b8a77f450eabef1b0bcccfae368ae2da1bd4 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Thu, 17 Mar 2022 17:39:08 +0000 Subject: [PATCH 18/19] Try making Slug mandatory instead --- lib/compat/wordpress-6.0/block-patterns.php | 39 +++++++++++---------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index 68805ae94a7cc5..7e9ac07fdf89a3 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -59,6 +59,7 @@ function( $current_screen ) { * * /** * * Title: My Pattern + * * Slug: my-theme/my-pattern * * * * The output of the PHP source corresponds to the content of the pattern, e.g.: @@ -69,7 +70,6 @@ function( $current_screen ) { * * Other settable fields include: * - * - Slug (otherwise inferred from filename) * - Description * - Viewport Width * - Categories (comma-separated values) @@ -104,26 +104,27 @@ function gutenberg_register_theme_block_patterns() { foreach ( $files as $file ) { $pattern_data = get_file_data( $file, $default_headers ); - $pattern_slug = $pattern_data[ 'slug' ]; - - // Compute slug from the theme and the basename of the pattern file. - // - // Example: twentytwentytwo/query-grid-posts. - if ( empty( $pattern_slug ) ) { - if ( ! preg_match( '#/(?P[A-z0-9_-]+)\.php$#', $file, $matches ) ) { - trigger_error( - sprintf( - __( 'Could not register file "%s" as a block pattern ("Slug" field missing)', 'gutenberg' ), - $file - ) - ); - continue; - } + if ( empty( $pattern_data[ 'slug' ] ) ) { + trigger_error( + sprintf( + __( 'Could not register file "%s" as a block pattern ("Slug" field missing)', 'gutenberg' ), + $file + ) + ); + continue; + } - $pattern_slug = get_stylesheet() . "/" . $matches[ 'slug' ]; + if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern_data[ 'slug' ] ) ) { + trigger_error( + sprintf( + __( 'Could not register file "%1s" as a block pattern (invalid slug "%2s")', 'gutenberg' ), + $file, + $pattern_data[ 'slug' ] + ) + ); } - if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_slug ) ) { + if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_data[ 'slug' ] ) ) { continue; } @@ -175,7 +176,7 @@ function gutenberg_register_theme_block_patterns() { continue; } - register_block_pattern( $pattern_slug, $pattern_data ); + register_block_pattern( $pattern_data[ 'slug' ], $pattern_data ); } } } From 4c48632d3eaab965d7b758241426f55b56957676 Mon Sep 17 00:00:00 2001 From: Miguel Fonseca Date: Thu, 17 Mar 2022 18:10:40 +0000 Subject: [PATCH 19/19] Misc. code quality and style fixes --- lib/compat/wordpress-6.0/block-patterns.php | 30 ++++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/compat/wordpress-6.0/block-patterns.php b/lib/compat/wordpress-6.0/block-patterns.php index 7e9ac07fdf89a3..b9ca9107fe413c 100644 --- a/lib/compat/wordpress-6.0/block-patterns.php +++ b/lib/compat/wordpress-6.0/block-patterns.php @@ -104,9 +104,10 @@ function gutenberg_register_theme_block_patterns() { foreach ( $files as $file ) { $pattern_data = get_file_data( $file, $default_headers ); - if ( empty( $pattern_data[ 'slug' ] ) ) { + if ( empty( $pattern_data['slug'] ) ) { trigger_error( sprintf( + /* translators: %s: file name. */ __( 'Could not register file "%s" as a block pattern ("Slug" field missing)', 'gutenberg' ), $file ) @@ -114,17 +115,18 @@ function gutenberg_register_theme_block_patterns() { continue; } - if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern_data[ 'slug' ] ) ) { + if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern_data['slug'] ) ) { trigger_error( sprintf( - __( 'Could not register file "%1s" as a block pattern (invalid slug "%2s")', 'gutenberg' ), + /* translators: %1s: file name; %2s: slug value found. */ + __( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")', 'gutenberg' ), $file, - $pattern_data[ 'slug' ] + $pattern_data['slug'] ) ); } - if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_data[ 'slug' ] ) ) { + if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_data['slug'] ) ) { continue; } @@ -132,6 +134,7 @@ function gutenberg_register_theme_block_patterns() { if ( ! $pattern_data['title'] ) { trigger_error( sprintf( + /* translators: %1s: file name; %2s: slug value found. */ __( 'Could not register file "%s" as a block pattern ("Title" field missing)', 'gutenberg' ), $file ) @@ -148,14 +151,18 @@ function gutenberg_register_theme_block_patterns() { (string) $pattern_data[ $property ] ) ); - } else unset( $pattern_data[ $property ] ); + } else { + unset( $pattern_data[ $property ] ); + } } // Parse properties of type int. foreach ( array( 'viewportWidth' ) as $property ) { if ( ! empty( $pattern_data[ $property ] ) ) { $pattern_data[ $property ] = (int) $pattern_data[ $property ]; - } else unset( $pattern_data[ $property ] ); + } else { + unset( $pattern_data[ $property ] ); + } } // Parse properties of type bool. @@ -163,9 +170,12 @@ function gutenberg_register_theme_block_patterns() { if ( ! empty( $pattern_data[ $property ] ) ) { $pattern_data[ $property ] = in_array( strtolower( $pattern_data[ $property ] ), - array( "yes", "true" ) + array( 'yes', 'true' ), + true ); - } else unset( $pattern_data[ $property ] ); + } else { + unset( $pattern_data[ $property ] ); + } } // The actual pattern content is the output of the file. @@ -176,7 +186,7 @@ function gutenberg_register_theme_block_patterns() { continue; } - register_block_pattern( $pattern_data[ 'slug' ], $pattern_data ); + register_block_pattern( $pattern_data['slug'], $pattern_data ); } } }