Make WordPress Core


Ignore:
Timestamp:
04/12/2022 09:24:51 AM (3 years ago)
Author:
gziolo
Message:

REST API: Bring new endpoints for Block Patterns from Gutenberg plugin

Related Gutenberg issue: https://github.com/WordPress/gutenberg/issues/39889.

Backporting changes from the Gutenberg plugin:

  • new Block Patterns REST API endpoint
  • new Block Pattern Categories REST API endpoint
  • updates to Query Loop related patterns
  • support for custom taxonomies in Query Loop block

Props hellofromtonya, peterwilsoncc, ntsekouras, zieladam, ironprogrammer, spacedmonkey, timothyblynjacobs, antonvlasenko, jsnajdr.
See #55505.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/block-patterns.php

    r52597 r53152  
    1313 *
    1414 * @since 5.5.0
    15  * @private
     15 * @access private
    1616 */
    1717function _register_core_block_patterns_and_categories() {
     
    128128    }
    129129}
     130
     131/**
     132 * Registers patterns from Pattern Directory provided by a theme's
     133 * `theme.json` file.
     134 *
     135 * @since 6.0.0
     136 * @access private
     137 */
     138function _register_remote_theme_patterns() {
     139    if ( ! get_theme_support( 'core-block-patterns' ) ) {
     140        return;
     141    }
     142
     143    /** This filter is documented in wp-includes/block-patterns.php */
     144    if ( ! apply_filters( 'should_load_remote_block_patterns', true ) ) {
     145        return;
     146    }
     147
     148    if ( ! WP_Theme_JSON_Resolver::theme_has_support() ) {
     149        return;
     150    }
     151
     152    $pattern_settings = WP_Theme_JSON_Resolver::get_theme_data()->get_patterns();
     153    if ( empty( $pattern_settings ) ) {
     154        return;
     155    }
     156
     157    $request         = new WP_REST_Request( 'GET', '/wp/v2/pattern-directory/patterns' );
     158    $request['slug'] = implode( ',', $pattern_settings );
     159    $response        = rest_do_request( $request );
     160    if ( $response->is_error() ) {
     161        return;
     162    }
     163    $patterns          = $response->get_data();
     164    $patterns_registry = WP_Block_Patterns_Registry::get_instance();
     165    foreach ( $patterns as $pattern ) {
     166        $pattern_name = sanitize_title( $pattern['title'] );
     167        // Some patterns might be already registered as core patterns with the `core` prefix.
     168        $is_registered = $patterns_registry->is_registered( $pattern_name ) || $patterns_registry->is_registered( "core/$pattern_name" );
     169        if ( ! $is_registered ) {
     170            register_block_pattern( $pattern_name, (array) $pattern );
     171        }
     172    }
     173}
     174
     175/**
     176 * Register any patterns that the active theme may provide under its
     177 * `./patterns/` directory. Each pattern is defined as a PHP file and defines
     178 * its metadata using plugin-style headers. The minimum required definition is:
     179 *
     180 *     /**
     181 *      * Title: My Pattern
     182 *      * Slug: my-theme/my-pattern
     183 *      *
     184 *
     185 * The output of the PHP source corresponds to the content of the pattern, e.g.:
     186 *
     187 *     <main><p><?php echo "Hello"; ?></p></main>
     188 *
     189 * If applicable, this will collect from both parent and child theme.
     190 *
     191 * Other settable fields include:
     192 *
     193 *   - Description
     194 *   - Viewport Width
     195 *   - Categories       (comma-separated values)
     196 *   - Keywords         (comma-separated values)
     197 *   - Block Types      (comma-separated values)
     198 *   - Inserter         (yes/no)
     199 *
     200 * @since 6.0.0
     201 * @access private
     202 */
     203function _register_theme_block_patterns() {
     204    $default_headers = array(
     205        'title'         => 'Title',
     206        'slug'          => 'Slug',
     207        'description'   => 'Description',
     208        'viewportWidth' => 'Viewport Width',
     209        'categories'    => 'Categories',
     210        'keywords'      => 'Keywords',
     211        'blockTypes'    => 'Block Types',
     212        'inserter'      => 'Inserter',
     213    );
     214
     215    /*
     216     * Register patterns for the active theme. If the theme is a child theme,
     217     * let it override any patterns from the parent theme that shares the same slug.
     218     */
     219    $themes     = array();
     220    $stylesheet = get_stylesheet();
     221    $template   = get_template();
     222    if ( $stylesheet !== $template ) {
     223        $themes[] = wp_get_theme( $stylesheet );
     224    }
     225    $themes[] = wp_get_theme( $template );
     226
     227    foreach ( $themes as $theme ) {
     228        $dirpath = $theme->get_stylesheet_directory() . '/patterns/';
     229        if ( ! is_dir( $dirpath ) || ! is_readable( $dirpath ) ) {
     230            continue;
     231        }
     232        if ( file_exists( $dirpath ) ) {
     233            $files = glob( $dirpath . '*.php' );
     234            if ( $files ) {
     235                foreach ( $files as $file ) {
     236                    $pattern_data = get_file_data( $file, $default_headers );
     237
     238                    if ( empty( $pattern_data['slug'] ) ) {
     239                        _doing_it_wrong(
     240                            '_register_theme_block_patterns',
     241                            sprintf(
     242                                /* translators: %s: file name. */
     243                                __( 'Could not register file "%s" as a block pattern ("Slug" field missing)' ),
     244                                $file
     245                            ),
     246                            '6.0.0'
     247                        );
     248                        continue;
     249                    }
     250
     251                    if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern_data['slug'] ) ) {
     252                        _doing_it_wrong(
     253                            '_register_theme_block_patterns',
     254                            sprintf(
     255                                /* translators: %1s: file name; %2s: slug value found. */
     256                                __( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")' ),
     257                                $file,
     258                                $pattern_data['slug']
     259                            ),
     260                            '6.0.0'
     261                        );
     262                    }
     263
     264                    if ( WP_Block_Patterns_Registry::get_instance()->is_registered( $pattern_data['slug'] ) ) {
     265                        continue;
     266                    }
     267
     268                    // Title is a required property.
     269                    if ( ! $pattern_data['title'] ) {
     270                        _doing_it_wrong(
     271                            '_register_theme_block_patterns',
     272                            sprintf(
     273                                /* translators: %1s: file name; %2s: slug value found. */
     274                                __( 'Could not register file "%s" as a block pattern ("Title" field missing)' ),
     275                                $file
     276                            ),
     277                            '6.0.0'
     278                        );
     279                        continue;
     280                    }
     281
     282                    // For properties of type array, parse data as comma-separated.
     283                    foreach ( array( 'categories', 'keywords', 'blockTypes' ) as $property ) {
     284                        if ( ! empty( $pattern_data[ $property ] ) ) {
     285                            $pattern_data[ $property ] = array_filter(
     286                                preg_split(
     287                                    '/[\s,]+/',
     288                                    (string) $pattern_data[ $property ]
     289                                )
     290                            );
     291                        } else {
     292                            unset( $pattern_data[ $property ] );
     293                        }
     294                    }
     295
     296                    // Parse properties of type int.
     297                    foreach ( array( 'viewportWidth' ) as $property ) {
     298                        if ( ! empty( $pattern_data[ $property ] ) ) {
     299                            $pattern_data[ $property ] = (int) $pattern_data[ $property ];
     300                        } else {
     301                            unset( $pattern_data[ $property ] );
     302                        }
     303                    }
     304
     305                    // Parse properties of type bool.
     306                    foreach ( array( 'inserter' ) as $property ) {
     307                        if ( ! empty( $pattern_data[ $property ] ) ) {
     308                            $pattern_data[ $property ] = in_array(
     309                                strtolower( $pattern_data[ $property ] ),
     310                                array( 'yes', 'true' ),
     311                                true
     312                            );
     313                        } else {
     314                            unset( $pattern_data[ $property ] );
     315                        }
     316                    }
     317
     318                    // Translate the pattern metadata.
     319                    $text_domain = $theme->get( 'TextDomain' );
     320                    //phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText, WordPress.WP.I18n.NonSingularStringLiteralContext, WordPress.WP.I18n.NonSingularStringLiteralDomain, WordPress.WP.I18n.LowLevelTranslationFunction
     321                    $pattern_data['title'] = translate_with_gettext_context( $pattern_data['title'], 'Pattern title', $text_domain );
     322                    if ( ! empty( $pattern_data['description'] ) ) {
     323                        //phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText, WordPress.WP.I18n.NonSingularStringLiteralContext, WordPress.WP.I18n.NonSingularStringLiteralDomain, WordPress.WP.I18n.LowLevelTranslationFunction
     324                        $pattern_data['description'] = translate_with_gettext_context( $pattern_data['description'], 'Pattern description', $text_domain );
     325                    }
     326
     327                    // The actual pattern content is the output of the file.
     328                    ob_start();
     329                    include $file;
     330                    $pattern_data['content'] = ob_get_clean();
     331                    if ( ! $pattern_data['content'] ) {
     332                        continue;
     333                    }
     334
     335                    register_block_pattern( $pattern_data['slug'], $pattern_data );
     336                }
     337            }
     338        }
     339    }
     340}
     341add_action( 'init', '_register_theme_block_patterns' );
Note: See TracChangeset for help on using the changeset viewer.