Add Site Editor and PHP changes from Gutenberg 10.1 - 11.9

- First pass at adding the site editor from the Gutenberg plugin to
  wp-admin/site-editor.php.
- Adds miscellaneous PHP changes from Gutenberg 10.1 - 11.9.

Follows [52042].
See #54337.
Props youknowriad, aristath, hellofromtonya, gziolo.


git-svn-id: https://develop.svn.wordpress.org/trunk@52069 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Robert Anderson
2021-11-09 02:15:23 +00:00
parent ecf1d6a158
commit f034bc832e
59 changed files with 4240 additions and 168 deletions

82
package-lock.json generated
View File

@@ -3888,6 +3888,44 @@
}
}
},
"@wordpress/edit-site": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@wordpress/edit-site/-/edit-site-3.0.5.tgz",
"integrity": "sha512-FYr6xwZcDaPxiOBsmzHnbaSdGeGD55I2j2flJPtLM4v7jzrIFVOBBaFtGUAUTtl0oagOlVNkQidL+/mVAcbIeQ==",
"requires": {
"@babel/runtime": "^7.13.10",
"@wordpress/a11y": "^3.2.3",
"@wordpress/api-fetch": "^5.2.5",
"@wordpress/block-editor": "^8.0.0",
"@wordpress/block-library": "^6.0.3",
"@wordpress/blocks": "^11.1.3",
"@wordpress/components": "^19.0.1",
"@wordpress/compose": "^5.0.5",
"@wordpress/core-data": "^4.0.5",
"@wordpress/data": "^6.1.3",
"@wordpress/data-controls": "^2.2.6",
"@wordpress/editor": "^12.0.2",
"@wordpress/element": "^4.0.3",
"@wordpress/hooks": "^3.2.1",
"@wordpress/html-entities": "^3.2.2",
"@wordpress/i18n": "^4.2.3",
"@wordpress/icons": "^6.1.0",
"@wordpress/interface": "^4.1.3",
"@wordpress/keyboard-shortcuts": "^3.0.5",
"@wordpress/keycodes": "^3.2.3",
"@wordpress/media-utils": "^3.0.4",
"@wordpress/notices": "^3.2.6",
"@wordpress/plugins": "^4.0.5",
"@wordpress/primitives": "^3.0.3",
"@wordpress/url": "^3.3.0",
"classnames": "^2.3.1",
"downloadjs": "^1.4.7",
"file-saver": "^2.0.2",
"jszip": "^3.2.2",
"lodash": "^4.17.21",
"rememo": "^3.0.0"
}
},
"@wordpress/edit-widgets": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@wordpress/edit-widgets/-/edit-widgets-3.1.0.tgz",
@@ -7438,7 +7476,7 @@
},
"browserify-aes": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"dev": true,
"requires": {
@@ -10097,6 +10135,11 @@
}
}
},
"downloadjs": {
"version": "1.4.7",
"resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz",
"integrity": "sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw="
},
"downshift": {
"version": "6.1.7",
"resolved": "https://registry.npmjs.org/downshift/-/downshift-6.1.7.tgz",
@@ -12061,6 +12104,11 @@
"flat-cache": "^3.0.4"
}
},
"file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
},
"file-sync-cmp": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz",
@@ -14372,6 +14420,11 @@
"ev-emitter": "^1.0.0"
}
},
"immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
},
"import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -16924,6 +16977,17 @@
"object.assign": "^4.1.2"
}
},
"jszip": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
"integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
"requires": {
"lie": "~3.3.0",
"pako": "~1.0.2",
"readable-stream": "~2.3.6",
"set-immediate-shim": "~1.0.1"
}
},
"just-extend": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz",
@@ -17202,6 +17266,14 @@
"type-check": "~0.4.0"
}
},
"lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"requires": {
"immediate": "~3.0.5"
}
},
"liftup": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz",
@@ -19837,8 +19909,7 @@
"pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"dev": true
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"parallel-transform": {
"version": "1.2.0",
@@ -22865,6 +22936,11 @@
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"set-immediate-shim": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
"integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
},
"set-value": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",

View File

@@ -99,6 +99,7 @@
"@wordpress/dom": "3.2.6",
"@wordpress/dom-ready": "3.2.2",
"@wordpress/edit-post": "5.0.5",
"@wordpress/edit-site": "3.0.5",
"@wordpress/edit-widgets": "3.1.0",
"@wordpress/editor": "12.0.2",
"@wordpress/element": "4.0.3",

View File

@@ -202,8 +202,24 @@ if ( ! is_multisite() && current_user_can( 'update_themes' ) ) {
/* translators: %s: Number of available theme updates. */
$submenu['themes.php'][5] = array( sprintf( __( 'Themes %s' ), $count ), $appearance_cap, 'themes.php' );
if ( wp_is_block_template_theme() ) {
$submenu['themes.php'][6] = array(
sprintf(
/* translators: %s: "beta" label */
__( 'Editor %s' ),
'<span class="awaiting-mod">' . __( 'beta' ) . '</span>'
),
'edit_theme_options',
'site-editor.php',
);
}
// Hide Customize link on block themes unless a plugin or theme is using
// customize_register to add a setting.
if ( ! wp_is_block_template_theme() || has_action( 'customize_register' ) ) {
$customize_url = add_query_arg( 'return', urlencode( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ), 'customize.php' );
$submenu['themes.php'][6] = array( __( 'Customize' ), 'customize', esc_url( $customize_url ), '', 'hide-if-no-customize' );
$submenu['themes.php'][7] = array( __( 'Customize' ), 'customize', esc_url( $customize_url ), '', 'hide-if-no-customize' );
}
if ( current_theme_supports( 'menus' ) || current_theme_supports( 'widgets' ) ) {
$submenu['themes.php'][10] = array( __( 'Menus' ), 'edit_theme_options', 'nav-menus.php' );

View File

@@ -0,0 +1,115 @@
<?php
/**
* Site Editor administration screen.
*
* @package WordPress
* @subpackage Administration
*/
global $post, $editor_styles;
/** WordPress Administration Bootstrap */
require_once __DIR__ . '/admin.php';
if ( ! current_user_can( 'edit_theme_options' ) ) {
wp_die(
'<h1>' . __( 'You need a higher level of permission.' ) . '</h1>' .
'<p>' . __( 'Sorry, you are not allowed to edit theme options on this site.' ) . '</p>',
403
);
}
if ( ! wp_is_block_template_theme() ) {
wp_die( __( 'The theme you are currently using is not compatible with Full Site Editing.' ) );
}
// Used in the HTML title tag.
$title = __( 'Editor (beta)' );
$parent_file = 'themes.php';
// Flag that we're loading the block editor.
$current_screen = get_current_screen();
$current_screen->is_block_editor( true );
$block_editor_context = new WP_Block_Editor_Context();
$active_global_styles_id = WP_Theme_JSON_Resolver::get_user_custom_post_type_id();
$active_theme = wp_get_theme()->get_stylesheet();
$preload_paths = array(
array( '/wp/v2/media', 'OPTIONS' ),
'/',
'/wp/v2/types?context=edit',
'/wp/v2/taxonomies?context=edit',
'/wp/v2/pages?context=edit',
'/wp/v2/categories?context=edit',
'/wp/v2/posts?context=edit',
'/wp/v2/tags?context=edit',
'/wp/v2/templates?context=edit',
'/wp/v2/template-parts?context=edit',
'/wp/v2/settings',
'/wp/v2/themes?context=edit&status=active',
'/wp/v2/global-styles/' . $active_global_styles_id . '?context=edit',
'/wp/v2/global-styles/' . $active_global_styles_id,
'/wp/v2/themes/' . $active_theme . '/global-styles',
);
block_editor_rest_api_preload( $preload_paths, $block_editor_context );
$editor_settings = get_block_editor_settings(
array(
'siteUrl' => site_url(),
'postsPerPage' => get_option( 'posts_per_page' ),
'styles' => get_block_editor_theme_styles(),
'defaultTemplateTypes' => get_default_block_template_types(),
'defaultTemplatePartAreas' => get_allowed_block_template_part_areas(),
'__experimentalBlockPatterns' => WP_Block_Patterns_Registry::get_instance()->get_all_registered(),
'__experimentalBlockPatternCategories' => WP_Block_Pattern_Categories_Registry::get_instance()->get_all_registered(),
),
$block_editor_context
);
wp_add_inline_script(
'wp-edit-site',
sprintf(
'wp.domReady( function() {
wp.editSite.initialize( "site-editor", %s );
} );',
wp_json_encode( $editor_settings )
)
);
// Preload server-registered block schemas.
wp_add_inline_script(
'wp-blocks',
'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');'
);
wp_add_inline_script(
'wp-blocks',
sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( get_block_categories( $post ) ) ),
'after'
);
wp_enqueue_script( 'wp-edit-site' );
wp_enqueue_script( 'wp-format-library' );
wp_enqueue_style( 'wp-edit-site' );
wp_enqueue_style( 'wp-format-library' );
wp_enqueue_media();
if (
current_theme_supports( 'wp-block-styles' ) ||
( ! is_array( $editor_styles ) || count( $editor_styles ) === 0 )
) {
wp_enqueue_style( 'wp-block-library-theme' );
}
/** This action is documented in wp-admin/edit-form-blocks.php */
do_action( 'enqueue_block_editor_assets' );
require_once ABSPATH . 'wp-admin/admin-header.php';
?>
<div id="site-editor" class="edit-site"></div>
<?php
require_once ABSPATH . 'wp-admin/admin-footer.php';

File diff suppressed because one or more lines are too long

View File

@@ -9,7 +9,7 @@ return array(
'title' => _x( 'Image at left', 'Block pattern title' ),
'blockTypes' => array( 'core/query' ),
'categories' => array( 'query' ),
'content' => '<!-- wp:query {"query":{"perPage":1,"pages":0,"offset":0,"postType":"post","categoryIds":[],"tagIds":[],"order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":false}} -->
'content' => '<!-- wp:query {"query":{"perPage":3,"pages":0,"offset":0,"postType":"post","categoryIds":[],"tagIds":[],"order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":false}} -->
<div class="wp-block-query">
<!-- wp:post-template -->
<!-- wp:columns {"align":"wide"} -->

View File

@@ -9,7 +9,7 @@ return array(
'title' => _x( 'Small image and title', 'Block pattern title' ),
'blockTypes' => array( 'core/query' ),
'categories' => array( 'query' ),
'content' => '<!-- wp:query {"query":{"perPage":1,"pages":0,"offset":0,"postType":"post","categoryIds":[],"tagIds":[],"order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":false}} -->
'content' => '<!-- wp:query {"query":{"perPage":3,"pages":0,"offset":0,"postType":"post","categoryIds":[],"tagIds":[],"order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":false}} -->
<div class="wp-block-query">
<!-- wp:post-template -->
<!-- wp:columns {"verticalAlignment":"center"} -->

View File

@@ -9,7 +9,7 @@ return array(
'title' => _x( 'Standard', 'Block pattern title' ),
'blockTypes' => array( 'core/query' ),
'categories' => array( 'query' ),
'content' => '<!-- wp:query {"query":{"perPage":1,"pages":0,"offset":0,"postType":"post","categoryIds":[],"tagIds":[],"order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":false}} -->
'content' => '<!-- wp:query {"query":{"perPage":3,"pages":0,"offset":0,"postType":"post","categoryIds":[],"tagIds":[],"order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":false}} -->
<div class="wp-block-query">
<!-- wp:post-template -->
<!-- wp:post-title {"isLink":true} /-->

View File

@@ -63,8 +63,23 @@ function wp_apply_border_support( $block_type, $block_attributes ) {
wp_has_border_feature_support( $block_type, 'radius' ) &&
isset( $block_attributes['style']['border']['radius'] )
) {
$border_radius = (int) $block_attributes['style']['border']['radius'];
$styles[] = sprintf( 'border-radius: %dpx;', $border_radius );
$border_radius = $block_attributes['style']['border']['radius'];
if ( is_array( $border_radius ) ) {
// We have individual border radius corner values.
foreach ( $border_radius as $key => $radius ) {
// Convert CamelCase corner name to kebab-case.
$corner = strtolower( preg_replace( '/(?<!^)[A-Z]/', '-$0', $key ) );
$styles[] = sprintf( 'border-%s-radius: %s;', $corner, $radius );
}
} else {
// This check handles original unitless implementation.
if ( is_numeric( $border_radius ) ) {
$border_radius .= 'px';
}
$styles[] = sprintf( 'border-radius: %s;', $border_radius );
}
}
// Border style.
@@ -81,8 +96,14 @@ function wp_apply_border_support( $block_type, $block_attributes ) {
wp_has_border_feature_support( $block_type, 'width' ) &&
isset( $block_attributes['style']['border']['width'] )
) {
$border_width = intval( $block_attributes['style']['border']['width'] );
$styles[] = sprintf( 'border-width: %dpx;', $border_width );
$border_width = $block_attributes['style']['border']['width'];
// This check handles original unitless implementation.
if ( is_numeric( $border_width ) ) {
$border_width .= 'px';
}
$styles[] = sprintf( 'border-width: %s;', $border_width );
}
// Border color.

View File

@@ -99,7 +99,7 @@ function wp_apply_colors_support( $block_type, $block_attributes ) {
}
// Apply color class or inline style.
if ( $has_named_text_color ) {
$classes[] = sprintf( 'has-%s-color', $block_attributes['textColor'] );
$classes[] = sprintf( 'has-%s-color', _wp_to_kebab_case( $block_attributes['textColor'] ) );
} elseif ( $has_custom_text_color ) {
$styles[] = sprintf( 'color: %s;', $block_attributes['style']['color']['text'] );
}
@@ -116,7 +116,7 @@ function wp_apply_colors_support( $block_type, $block_attributes ) {
}
// Apply background color classes or styles.
if ( $has_named_background_color ) {
$classes[] = sprintf( 'has-%s-background-color', $block_attributes['backgroundColor'] );
$classes[] = sprintf( 'has-%s-background-color', _wp_to_kebab_case( $block_attributes['backgroundColor'] ) );
} elseif ( $has_custom_background_color ) {
$styles[] = sprintf( 'background-color: %s;', $block_attributes['style']['color']['background'] );
}
@@ -132,7 +132,7 @@ function wp_apply_colors_support( $block_type, $block_attributes ) {
}
// Apply required background class.
if ( $has_named_gradient ) {
$classes[] = sprintf( 'has-%s-gradient-background', $block_attributes['gradient'] );
$classes[] = sprintf( 'has-%s-gradient-background', _wp_to_kebab_case( $block_attributes['gradient'] ) );
} elseif ( $has_custom_gradient ) {
$styles[] = sprintf( 'background: %s;', $block_attributes['style']['color']['gradient'] );
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* Dimensions block support flag.
*
* This does not include the `spacing` block support even though that visually
* appears under the "Dimensions" panel in the editor. It remains in its
* original `spacing.php` file for backwards compatibility.
*
* @package WordPress
* @since 5.9.0
*/
/**
* Registers the style block attribute for block types that support it.
*
* @since 5.9.0
* @access private
*
* @param WP_Block_Type $block_type Block Type.
*/
function wp_register_dimensions_support( $block_type ) {
// Setup attributes and styles within that if needed.
if ( ! $block_type->attributes ) {
$block_type->attributes = array();
}
// Check for existing style attribute definition e.g. from block.json.
if ( array_key_exists( 'style', $block_type->attributes ) ) {
return;
}
$has_dimensions_support = block_has_support( $block_type, array( '__experimentalDimensions' ), false );
// Future block supports such as height & width will be added here.
if ( $has_dimensions_support ) {
$block_type->attributes['style'] = array(
'type' => 'object',
);
}
}
/**
* Add CSS classes for block dimensions to the incoming attributes array.
* This will be applied to the block markup in the front-end.
*
* @since 5.9.0
* @access private
*
* @param WP_Block_Type $block_type Block Type.
* @param array $block_attributes Block attributes.
*
* @return array Block dimensions CSS classes and inline styles.
*/
function wp_apply_dimensions_support( $block_type, $block_attributes ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
if ( wp_skip_dimensions_serialization( $block_type ) ) {
return array();
}
$styles = array();
// Height support to be added in near future.
// Width support to be added in near future.
return empty( $styles ) ? array() : array( 'style' => implode( ' ', $styles ) );
}
/**
* Checks whether serialization of the current block's dimensions properties
* should occur.
*
* @since 5.9.0
* @access private
*
* @param WP_Block_type $block_type Block type.
*
* @return boolean Whether to serialize spacing support styles & classes.
*/
function wp_skip_dimensions_serialization( $block_type ) {
$dimensions_support = _wp_array_get( $block_type->supports, array( '__experimentalDimensions' ), false );
return is_array( $dimensions_support ) &&
array_key_exists( '__experimentalSkipSerialization', $dimensions_support ) &&
$dimensions_support['__experimentalSkipSerialization'];
}
// Register the block support.
WP_Block_Supports::get_instance()->register(
'dimensions',
array(
'register_attribute' => 'wp_register_dimensions_support',
'apply' => 'wp_apply_dimensions_support',
)
);

View File

@@ -266,7 +266,7 @@ function wp_tinycolor_string_to_rgb( $color_str ) {
$hsla_regexp = '/^hsla' . $permissive_match4 . '$/';
if ( preg_match( $hsla_regexp, $color_str, $match ) ) {
return wp_tinycolor_hsl_to_rgb(
$rgb = wp_tinycolor_hsl_to_rgb(
array(
'h' => $match[1],
's' => $match[2],
@@ -384,13 +384,16 @@ function wp_register_duotone_support( $block_type ) {
}
}
}
/**
* Renders the duotone filter SVG and returns the CSS filter property to
* reference the rendered SVG.
*
* @since 5.9.0
*
* @access private
* @param array $preset Duotone preset value as seen in theme.json.
*
* @return string Duotone CSS filter property.
*/
function wp_render_duotone_filter_preset( $preset ) {
@@ -460,13 +463,11 @@ function wp_render_duotone_filter_preset( $preset ) {
}
add_action(
/*
* Safari doesn't render SVG filters defined in data URIs,
* and SVG filters won't render in the head of a document,
* so the next best place to put the SVG is in the footer.
*/
// Safari doesn't render SVG filters defined in data URIs,
// and SVG filters won't render in the head of a document,
// so the next best place to put the SVG is in the footer.
is_admin() ? 'admin_footer' : 'wp_footer',
static function () use ( $svg ) {
function () use ( $svg ) {
echo $svg;
}
);
@@ -502,84 +503,38 @@ function wp_render_duotone_support( $block_content, $block ) {
return $block_content;
}
$duotone_colors = $block['attrs']['style']['color']['duotone'];
$duotone_values = array(
'r' => array(),
'g' => array(),
'b' => array(),
$filter_preset = array(
'slug' => uniqid(),
'colors' => $block['attrs']['style']['color']['duotone'],
);
foreach ( $duotone_colors as $color_str ) {
$color = wp_tinycolor_string_to_rgb( $color_str );
$filter_property = wp_render_duotone_filter_preset( $filter_preset );
$filter_id = 'wp-duotone-' . $filter_preset['slug'];
$duotone_values['r'][] = $color['r'] / 255;
$duotone_values['g'][] = $color['g'] / 255;
$duotone_values['b'][] = $color['b'] / 255;
$scope = '.' . $filter_id;
$selectors = explode( ',', $duotone_support );
$scoped = array();
foreach ( $selectors as $sel ) {
$scoped[] = $scope . ' ' . trim( $sel );
}
$selector = implode( ', ', $scoped );
$duotone_id = 'wp-duotone-filter-' . uniqid();
// !important is needed because these styles render before global styles,
// and they should be overriding the duotone filters set by global styles.
$filter_style = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG
? $selector . " {\n\tfilter: " . $filter_property . " !important;\n}\n"
: $selector . '{filter:' . $filter_property . ' !important;}';
$selectors = explode( ',', $duotone_support );
$selectors_scoped = array_map(
static function ( $selector ) use ( $duotone_id ) {
return '.' . $duotone_id . ' ' . trim( $selector );
},
$selectors
);
$selectors_group = implode( ', ', $selectors_scoped );
ob_start();
?>
<style>
<?php echo $selectors_group; ?> {
filter: url( <?php echo esc_url( '#' . $duotone_id ); ?> );
}
</style>
<svg
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 0 0"
width="0"
height="0"
focusable="false"
role="none"
style="visibility: hidden; position: absolute; left: -9999px; overflow: hidden;"
>
<defs>
<filter id="<?php echo esc_attr( $duotone_id ); ?>">
<feColorMatrix
type="matrix"
<?php // phpcs:disable Generic.WhiteSpace.DisallowSpaceIndent ?>
values=".299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
0 0 0 1 0"
<?php // phpcs:enable Generic.WhiteSpace.DisallowSpaceIndent ?>
/>
<feComponentTransfer color-interpolation-filters="sRGB" >
<feFuncR type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['r'] ) ); ?>" />
<feFuncG type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['g'] ) ); ?>" />
<feFuncB type="table" tableValues="<?php echo esc_attr( implode( ' ', $duotone_values['b'] ) ); ?>" />
</feComponentTransfer>
</filter>
</defs>
</svg>
<?php
$duotone = ob_get_clean();
wp_register_style( $filter_id, false, array(), true, true );
wp_add_inline_style( $filter_id, $filter_style );
wp_enqueue_style( $filter_id );
// Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
$content = preg_replace(
return preg_replace(
'/' . preg_quote( 'class="', '/' ) . '/',
'class="' . $duotone_id . ' ',
'class="' . $filter_id . ' ',
$block_content,
1
);
return $content . $duotone;
}
// Register the block support.

View File

@@ -17,6 +17,10 @@
* @return string Filtered block content.
*/
function wp_render_elements_support( $block_content, $block ) {
if ( ! $block_content ) {
return $block_content;
}
$link_color = null;
if ( ! empty( $block['attrs'] ) ) {
$link_color = _wp_array_get( $block['attrs'], array( 'style', 'elements', 'link', 'color', 'text' ), null );
@@ -42,7 +46,7 @@ function wp_render_elements_support( $block_content, $block ) {
}
$link_color_declaration = esc_html( safecss_filter_attr( "color: $link_color" ) );
$style = "<style>.$class_name a{" . $link_color_declaration . " !important;}</style>\n";
$style = "<style>.$class_name a{" . $link_color_declaration . ";}</style>\n";
// Like the layout hook this assumes the hook only applies to blocks with a single wrapper.
// Retrieve the opening tag of the first HTML element.
@@ -64,8 +68,19 @@ function wp_render_elements_support( $block_content, $block ) {
$content = substr_replace( $block_content, ' class="' . $class_name . '"', $first_element_offset + strlen( $first_element ) - 1, 0 );
}
return $content . $style;
/*
* Ideally styles should be loaded in the head, but blocks may be parsed
* after that, so loading in the footer for now.
* See https://core.trac.wordpress.org/ticket/53494.
*/
add_action(
'wp_footer',
static function () use ( $style ) {
echo $style;
}
);
return $content;
}
add_filter( 'render_block', 'wp_render_elements_support', 10, 2 );

View File

@@ -29,6 +29,106 @@ function wp_register_layout_support( $block_type ) {
}
}
/**
* Generates the CSS corresponding to the provided layout.
*
* @since 5.9.0
* @access private
*
* @param string $selector CSS selector.
* @param array $layout Layout object. The one that is passed has already checked the existance of default block layout.
* @param boolean $has_block_gap_support Whether the theme has support for the block gap.
*
* @return string CSS style.
*/
function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false ) {
$layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default';
$style = '';
if ( 'default' === $layout_type ) {
$content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : null;
$wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : null;
$all_max_width_value = $content_size ? $content_size : $wide_size;
$wide_max_width_value = $wide_size ? $wide_size : $content_size;
// Make sure there is a single CSS rule, and all tags are stripped for security.
// TODO: Use `safecss_filter_attr` instead - once https://core.trac.wordpress.org/ticket/46197 is patched.
$all_max_width_value = wp_strip_all_tags( explode( ';', $all_max_width_value )[0] );
$wide_max_width_value = wp_strip_all_tags( explode( ';', $wide_max_width_value )[0] );
$style = '';
if ( $content_size || $wide_size ) {
$style = "$selector > * {";
$style .= 'max-width: ' . esc_html( $all_max_width_value ) . ';';
$style .= 'margin-left: auto !important;';
$style .= 'margin-right: auto !important;';
$style .= '}';
$style .= "$selector > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}';
$style .= "$selector .alignfull { max-width: none; }";
}
$style .= "$selector .alignleft { float: left; margin-right: 2em; }";
$style .= "$selector .alignright { float: right; margin-left: 2em; }";
if ( $has_block_gap_support ) {
$style .= "$selector > * { margin-top: 0; margin-bottom: 0; }";
$style .= "$selector > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }";
}
} elseif ( 'flex' === $layout_type ) {
$layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal';
$justify_content_options = array(
'left' => 'flex-start',
'right' => 'flex-end',
'center' => 'center',
);
if ( 'horizontal' === $layout_orientation ) {
$justify_content_options += array( 'space-between' => 'space-between' );
}
$flex_wrap_options = array( 'wrap', 'nowrap' );
$flex_wrap = ! empty( $layout['flexWrap'] ) && in_array( $layout['flexWrap'], $flex_wrap_options, true ) ?
$layout['flexWrap'] :
'wrap';
$style = "$selector {";
$style .= 'display: flex;';
if ( $has_block_gap_support ) {
$style .= 'gap: var( --wp--style--block-gap, 0.5em );';
} else {
$style .= 'gap: 0.5em;';
}
$style .= "flex-wrap: $flex_wrap;";
$style .= 'align-items: center;';
if ( 'horizontal' === $layout_orientation ) {
$style .= 'align-items: center;';
/**
* Add this style only if is not empty for backwards compatibility,
* since we intend to convert blocks that had flex layout implemented
* by custom css.
*/
if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
$style .= "justify-content: {$justify_content_options[ $layout['justifyContent'] ]};";
// --justification-setting allows children to inherit the value regardless or row or column direction.
$style .= "--justification-setting: {$justify_content_options[ $layout['justifyContent'] ]};";
}
} else {
$style .= 'flex-direction: column;';
if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
$style .= "align-items: {$justify_content_options[ $layout['justifyContent'] ]};";
$style .= "--justification-setting: {$justify_content_options[ $layout['justifyContent'] ]};";
}
}
$style .= '}';
$style .= "$selector > * { margin: 0; }";
}
return $style;
}
/**
* Renders the layout config to the block wrapper.
*
@@ -42,47 +142,25 @@ function wp_register_layout_support( $block_type ) {
function wp_render_layout_support_flag( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$support_layout = block_has_support( $block_type, array( '__experimentalLayout' ), false );
if ( ! $support_layout || ! isset( $block['attrs']['layout'] ) ) {
if ( ! $support_layout ) {
return $block_content;
}
$used_layout = $block['attrs']['layout'];
$block_gap = wp_get_global_settings( array( 'spacing', 'blockGap' ) );
$default_layout = wp_get_global_settings( array( 'layout' ) );
$has_block_gap_support = isset( $block_gap ) ? null !== $block_gap : false;
$default_block_layout = _wp_array_get( $block_type->supports, array( '__experimentalLayout', 'default' ), array() );
$used_layout = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $default_block_layout;
if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] ) {
$tree = WP_Theme_JSON_Resolver::get_merged_data();
$default_layout = _wp_array_get( $tree->get_settings(), array( 'layout' ) );
if ( ! $default_layout ) {
return $block_content;
}
$used_layout = $default_layout;
}
$id = uniqid();
$content_size = isset( $used_layout['contentSize'] ) ? $used_layout['contentSize'] : null;
$wide_size = isset( $used_layout['wideSize'] ) ? $used_layout['wideSize'] : null;
$all_max_width_value = $content_size ? $content_size : $wide_size;
$wide_max_width_value = $wide_size ? $wide_size : $content_size;
// Make sure there is a single CSS rule, and all tags are stripped for security.
$all_max_width_value = safecss_filter_attr( explode( ';', $all_max_width_value )[0] );
$wide_max_width_value = safecss_filter_attr( explode( ';', $wide_max_width_value )[0] );
$style = '';
if ( $content_size || $wide_size ) {
$style = ".wp-container-$id > * {";
$style .= 'max-width: ' . esc_html( $all_max_width_value ) . ';';
$style .= 'margin-left: auto !important;';
$style .= 'margin-right: auto !important;';
$style .= '}';
$style .= ".wp-container-$id > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}';
$style .= ".wp-container-$id .alignfull { max-width: none; }";
}
$style .= ".wp-container-$id .alignleft { float: left; margin-right: 2em; }";
$style .= ".wp-container-$id .alignright { float: right; margin-left: 2em; }";
$id = uniqid();
$style = wp_get_layout_style( ".wp-container-$id", $used_layout, $has_block_gap_support );
// This assumes the hook only applies to blocks with a single wrapper.
// I think this is a reasonable limitation for that particular hook.
$content = preg_replace(
@@ -92,7 +170,19 @@ function wp_render_layout_support_flag( $block_content, $block ) {
1
);
return $content . '<style>' . $style . '</style>';
/*
* Ideally styles should be loaded in the head, but blocks may be parsed
* after that, so loading in the footer for now.
* See https://core.trac.wordpress.org/ticket/53494.
*/
add_action(
'wp_footer',
static function () use ( $style ) {
echo '<style>' . $style . '</style>';
}
);
return $content;
}
// Register the block support.
@@ -123,7 +213,8 @@ function wp_restore_group_inner_container( $block_content, $block ) {
if (
'core/group' !== $block['blockName'] ||
WP_Theme_JSON_Resolver::theme_has_support() ||
1 === preg_match( $group_with_inner_container_regex, $block_content )
1 === preg_match( $group_with_inner_container_regex, $block_content ) ||
( isset( $block['attrs']['layout']['type'] ) && 'default' !== $block['attrs']['layout']['type'] )
) {
return $block_content;
}

View File

@@ -1,6 +1,9 @@
<?php
/**
* Spacing block support flag.
* For backwards compatibility, this remains separate to the dimensions.php
* block support despite both belonging under a single panel in the editor.
*
* @package WordPress
* @since 5.8.0
@@ -42,25 +45,33 @@ function wp_register_spacing_support( $block_type ) {
* @return array Block spacing CSS classes and inline styles.
*/
function wp_apply_spacing_support( $block_type, $block_attributes ) {
$has_padding_support = wp_has_spacing_feature_support( $block_type, 'padding' );
$has_margin_support = wp_has_spacing_feature_support( $block_type, 'margin' );
if ( wp_skip_spacing_serialization( $block_type ) ) {
return array();
}
$has_padding_support = block_has_support( $block_type, array( 'spacing', 'padding' ), false );
$has_margin_support = block_has_support( $block_type, array( 'spacing', 'margin' ), false );
$styles = array();
if ( $has_padding_support ) {
$padding_value = _wp_array_get( $block_attributes, array( 'style', 'spacing', 'padding' ), null );
if ( null !== $padding_value ) {
if ( is_array( $padding_value ) ) {
foreach ( $padding_value as $key => $value ) {
$styles[] = sprintf( 'padding-%s: %s;', $key, $value );
}
} elseif ( null !== $padding_value ) {
$styles[] = sprintf( 'padding: %s;', $padding_value );
}
}
if ( $has_margin_support ) {
$margin_value = _wp_array_get( $block_attributes, array( 'style', 'spacing', 'margin' ), null );
if ( null !== $margin_value ) {
if ( is_array( $margin_value ) ) {
foreach ( $margin_value as $key => $value ) {
$styles[] = sprintf( 'margin-%s: %s;', $key, $value );
}
} elseif ( null !== $margin_value ) {
$styles[] = sprintf( 'margin: %s;', $margin_value );
}
}
@@ -68,20 +79,75 @@ function wp_apply_spacing_support( $block_type, $block_attributes ) {
}
/**
* Checks whether the current block type supports the spacing feature requested.
* Checks whether serialization of the current block's spacing properties should
* occur.
*
* @since 5.8.0
* @since 5.9.0
* @access private
*
* @param WP_Block_Type $block_type Block type to check for support.
* @param string $feature Name of the feature to check support for.
* @param mixed $default Fallback value for feature support. Default false.
* @return bool Whether the feature is supported.
* @param WP_Block_Type $block_type Block type.
*
* @return boolean Whether to serialize spacing support styles & classes.
*/
function wp_has_spacing_feature_support( $block_type, $feature, $default = false ) {
// Check if the specific feature has been opted into individually
// via nested flag under `spacing`.
return block_has_support( $block_type, array( 'spacing', $feature ), $default );
function wp_skip_spacing_serialization( $block_type ) {
$spacing_support = _wp_array_get( $block_type->supports, array( 'spacing' ), false );
return is_array( $spacing_support ) &&
array_key_exists( '__experimentalSkipSerialization', $spacing_support ) &&
$spacing_support['__experimentalSkipSerialization'];
}
/**
* Renders the spacing gap support to the block wrapper, to ensure
* that the CSS variable is rendered in all environments.
*
* @since 5.9.0
* @access private
*
* @param string $block_content Rendered block content.
* @param array $block Block object.
* @return string Filtered block content.
*/
function wp_render_spacing_gap_support( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$has_gap_support = block_has_support( $block_type, array( 'spacing', 'blockGap' ), false );
if ( ! $has_gap_support || ! isset( $block['attrs']['style']['spacing']['blockGap'] ) ) {
return $block_content;
}
$gap_value = $block['attrs']['style']['spacing']['blockGap'];
// Skip if gap value contains unsupported characters.
// Regex for CSS value borrowed from `safecss_filter_attr`, and used here
// because we only want to match against the value, not the CSS attribute.
if ( preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ) {
return $block_content;
}
$style = sprintf(
'--wp--style--block-gap: %s',
esc_attr( $gap_value )
);
// Attempt to update an existing style attribute on the wrapper element.
$injected_style = preg_replace(
'/^([^>.]+?)(' . preg_quote( 'style="', '/' ) . ')(?=.+?>)/',
'$1$2' . $style . '; ',
$block_content,
1
);
// If there is no existing style attribute, add one to the wrapper element.
if ( $injected_style === $block_content ) {
$injected_style = preg_replace(
'/<([a-zA-Z0-9]+)([ >])/',
'<$1 style="' . $style . '"$2',
$block_content,
1
);
};
return $injected_style;
}
// Register the block support.
@@ -92,3 +158,5 @@ WP_Block_Supports::get_instance()->register(
'apply' => 'wp_apply_spacing_support',
)
);
add_filter( 'render_block', 'wp_render_spacing_gap_support', 10, 2 );

View File

@@ -28,6 +28,7 @@ function wp_register_typography_support( $block_type ) {
$has_font_size_support = _wp_array_get( $typography_supports, array( 'fontSize' ), false );
$has_font_style_support = _wp_array_get( $typography_supports, array( '__experimentalFontStyle' ), false );
$has_font_weight_support = _wp_array_get( $typography_supports, array( '__experimentalFontWeight' ), false );
$has_letter_spacing_support = _wp_array_get( $typography_supports, array( '__experimentalLetterSpacing' ), false );
$has_line_height_support = _wp_array_get( $typography_supports, array( 'lineHeight' ), false );
$has_text_decoration_support = _wp_array_get( $typography_supports, array( '__experimentalTextDecoration' ), false );
$has_text_transform_support = _wp_array_get( $typography_supports, array( '__experimentalTextTransform' ), false );
@@ -36,6 +37,7 @@ function wp_register_typography_support( $block_type ) {
|| $has_font_size_support
|| $has_font_style_support
|| $has_font_weight_support
|| $has_letter_spacing_support
|| $has_line_height_support
|| $has_text_decoration_support
|| $has_text_transform_support;
@@ -93,6 +95,7 @@ function wp_apply_typography_support( $block_type, $block_attributes ) {
$has_font_size_support = _wp_array_get( $typography_supports, array( 'fontSize' ), false );
$has_font_style_support = _wp_array_get( $typography_supports, array( '__experimentalFontStyle' ), false );
$has_font_weight_support = _wp_array_get( $typography_supports, array( '__experimentalFontWeight' ), false );
$has_letter_spacing_support = _wp_array_get( $typography_supports, array( '__experimentalLetterSpacing' ), false );
$has_line_height_support = _wp_array_get( $typography_supports, array( 'lineHeight' ), false );
$has_text_decoration_support = _wp_array_get( $typography_supports, array( '__experimentalTextDecoration' ), false );
$has_text_transform_support = _wp_array_get( $typography_supports, array( '__experimentalTextTransform' ), false );
@@ -102,24 +105,28 @@ function wp_apply_typography_support( $block_type, $block_attributes ) {
$has_custom_font_size = isset( $block_attributes['style']['typography']['fontSize'] );
if ( $has_named_font_size ) {
$classes[] = sprintf( 'has-%s-font-size', $block_attributes['fontSize'] );
$classes[] = sprintf( 'has-%s-font-size', _wp_to_kebab_case( $block_attributes['fontSize'] ) );
} elseif ( $has_custom_font_size ) {
$styles[] = sprintf( 'font-size: %s;', $block_attributes['style']['typography']['fontSize'] );
}
}
if ( $has_font_family_support ) {
$has_font_family = isset( $block_attributes['style']['typography']['fontFamily'] );
if ( $has_font_family ) {
$font_family = $block_attributes['style']['typography']['fontFamily'];
if ( strpos( $font_family, 'var:preset|font-family' ) !== false ) {
// Get the name from the string and add proper styles.
$index_to_splice = strrpos( $font_family, '|' ) + 1;
$font_family_name = substr( $font_family, $index_to_splice );
$styles[] = sprintf( 'font-family: var(--wp--preset--font-family--%s);', $font_family_name );
} else {
$styles[] = sprintf( 'font-family: %s;', $block_attributes['style']['typography']['fontFamily'] );
$has_named_font_family = array_key_exists( 'fontFamily', $block_attributes );
$has_custom_font_family = isset( $block_attributes['style']['typography']['fontFamily'] );
if ( $has_named_font_family ) {
$classes[] = sprintf( 'has-%s-font-family', _wp_to_kebab_case( $block_attributes['fontFamily'] ) );
} elseif ( $has_custom_font_family ) {
// Before using classes, the value was serialized as a CSS Custom Property.
// We don't need this code path when it lands in core.
$font_family_custom = $block_attributes['style']['typography']['fontFamily'];
if ( strpos( $font_family_custom, 'var:preset|font-family' ) !== false ) {
$index_to_splice = strrpos( $font_family_custom, '|' ) + 1;
$font_family_slug = _wp_to_kebab_case( substr( $font_family_custom, $index_to_splice ) );
$font_family_custom = sprintf( 'var(--wp--preset--font-family--%s)', $font_family_slug );
}
$styles[] = sprintf( 'font-family: %s;', $font_family_custom );
}
}
@@ -158,6 +165,13 @@ function wp_apply_typography_support( $block_type, $block_attributes ) {
}
}
if ( $has_letter_spacing_support ) {
$letter_spacing_style = wp_typography_get_css_variable_inline_style( $block_attributes, 'letterSpacing', 'letter-spacing' );
if ( $letter_spacing_style ) {
$styles[] = $letter_spacing_style;
}
}
if ( ! empty( $classes ) ) {
$attributes['class'] = implode( ' ', $classes );
}

View File

@@ -1182,3 +1182,117 @@ function get_query_pagination_arrow( $block, $is_next ) {
}
return null;
}
/**
* Enqueue a stylesheet for a specific block.
*
* If the theme has opted-in to separate-styles loading,
* then the stylesheet will be enqueued on-render,
* otherwise when the block inits.
*
* @param string $block_name The block-name, including namespace.
* @param array $args An array of arguments [handle,src,deps,ver,media].
*
* @return void
*/
function wp_enqueue_block_style( $block_name, $args ) {
$args = wp_parse_args(
$args,
array(
'handle' => '',
'src' => '',
'deps' => array(),
'ver' => false,
'media' => 'all',
)
);
/**
* Callback function to register and enqueue styles.
*
* @param string $content When the callback is used for the render_block filter,
* the content needs to be returned so the function parameter
* is to ensure the content exists.
*
* @return string
*/
$callback = static function( $content ) use ( $args ) {
// Register the stylesheet.
if ( ! empty( $args['src'] ) ) {
wp_register_style( $args['handle'], $args['src'], $args['deps'], $args['ver'], $args['media'] );
}
// Add `path` data if provided.
if ( isset( $args['path'] ) ) {
wp_style_add_data( $args['handle'], 'path', $args['path'] );
// Get the RTL file path.
$rtl_file_path = str_replace( '.css', '-rtl.css', $args['path'] );
// Add RTL stylesheet.
if ( file_exists( $rtl_file_path ) ) {
wp_style_add_data( $args['hanle'], 'rtl', 'replace' );
if ( is_rtl() ) {
wp_style_add_data( $args['handle'], 'path', $rtl_file_path );
}
}
}
// Enqueue the stylesheet.
wp_enqueue_style( $args['handle'] );
return $content;
};
$hook = did_action( 'wp_enqueue_scripts' ) ? 'wp_footer' : 'wp_enqueue_scripts';
if ( wp_should_load_separate_core_block_assets() ) {
$hook = "render_block_$block_name";
}
/*
* The filter's callback here is an anonymous function because
* using a named function in this case is not possible.
*
* The function cannot be unhooked, however, users are still able
* to dequeue the stylesheets registered/enqueued by the callback
* which is why in this case, using an anonymous function
* was deemed acceptable.
*/
add_filter( $hook, $callback );
// Enqueue assets in the editor.
add_action( 'enqueue_block_assets', $callback );
}
/**
* Allow multiple block styles.
*
* @param array $metadata Metadata for registering a block type.
*
* @return array
*/
function _wp_multiple_block_styles( $metadata ) {
foreach ( array( 'style', 'editorStyle' ) as $key ) {
if ( ! empty( $metadata[ $key ] ) && is_array( $metadata[ $key ] ) ) {
$default_style = array_shift( $metadata[ $key ] );
foreach ( $metadata[ $key ] as $handle ) {
$args = array( 'handle' => $handle );
if ( 0 === strpos( $handle, 'file:' ) && isset( $metadata['file'] ) ) {
$style_path = remove_block_asset_path_prefix( $handle );
$args = array(
'handle' => sanitize_key( "{$metadata['name']}-{$style_path}" ),
'src' => plugins_url( $style_path, $metadata['file'] ),
);
}
wp_enqueue_block_style( $metadata['name'], $args );
}
// Only return the 1st item in the array.
$metadata[ $key ] = $default_style;
}
}
return $metadata;
}
add_filter( 'block_type_metadata', '_wp_multiple_block_styles' );

View File

@@ -15,29 +15,38 @@ require ABSPATH . WPINC . '/blocks/latest-comments.php';
require ABSPATH . WPINC . '/blocks/latest-posts.php';
require ABSPATH . WPINC . '/blocks/legacy-widget.php';
require ABSPATH . WPINC . '/blocks/loginout.php';
require ABSPATH . WPINC . '/blocks/navigation-area.php';
require ABSPATH . WPINC . '/blocks/navigation-link.php';
require ABSPATH . WPINC . '/blocks/navigation-submenu.php';
require ABSPATH . WPINC . '/blocks/navigation.php';
require ABSPATH . WPINC . '/blocks/page-list.php';
require ABSPATH . WPINC . '/blocks/pattern.php';
require ABSPATH . WPINC . '/blocks/post-author.php';
require ABSPATH . WPINC . '/blocks/post-comments.php';
require ABSPATH . WPINC . '/blocks/post-content.php';
require ABSPATH . WPINC . '/blocks/post-date.php';
require ABSPATH . WPINC . '/blocks/post-excerpt.php';
require ABSPATH . WPINC . '/blocks/post-featured-image.php';
require ABSPATH . WPINC . '/blocks/post-navigation-link.php';
require ABSPATH . WPINC . '/blocks/post-template.php';
require ABSPATH . WPINC . '/blocks/post-terms.php';
require ABSPATH . WPINC . '/blocks/post-title.php';
require ABSPATH . WPINC . '/blocks/post-template.php';
require ABSPATH . WPINC . '/blocks/query.php';
require ABSPATH . WPINC . '/blocks/query-pagination.php';
require ABSPATH . WPINC . '/blocks/query-pagination-next.php';
require ABSPATH . WPINC . '/blocks/query-pagination-numbers.php';
require ABSPATH . WPINC . '/blocks/query-pagination-previous.php';
require ABSPATH . WPINC . '/blocks/query-pagination.php';
require ABSPATH . WPINC . '/blocks/query-title.php';
require ABSPATH . WPINC . '/blocks/query.php';
require ABSPATH . WPINC . '/blocks/rss.php';
require ABSPATH . WPINC . '/blocks/search.php';
require ABSPATH . WPINC . '/blocks/shortcode.php';
require ABSPATH . WPINC . '/blocks/site-tagline.php';
require ABSPATH . WPINC . '/blocks/site-logo.php';
require ABSPATH . WPINC . '/blocks/site-tagline.php';
require ABSPATH . WPINC . '/blocks/site-title.php';
require ABSPATH . WPINC . '/blocks/social-link.php';
require ABSPATH . WPINC . '/blocks/tag-cloud.php';
require ABSPATH . WPINC . '/blocks/template-part.php';
require ABSPATH . WPINC . '/blocks/term-description.php';
/**
* Registers core block types using metadata files.

View File

@@ -0,0 +1,21 @@
<?php
/**
* Server-side rendering of the `core/navigation-area` block.
*
* @package gutenberg
*/
/**
* Registers the `core/navigation-area` block on the server.
*/
function register_block_core_navigation_area() {
register_block_type_from_metadata(
__DIR__ . '/navigation-area',
array(
'provides_context' => array(
'navigationArea' => 'area',
),
)
);
}
add_action( 'init', 'register_block_core_navigation_area' );

View File

@@ -0,0 +1,27 @@
{
"apiVersion": 2,
"name": "core/navigation-area",
"title": "Navigation Area",
"category": "theme",
"description": "Define a navigation area for your theme. The navigation block associated with this area will be automatically displayed.",
"keywords": [
"menu",
"navigation",
"links",
"location"
],
"textdomain": "default",
"attributes": {
"area": {
"type": "string",
"default": "primary"
}
},
"providesContext": {
"navigationArea": "area"
},
"supports": {
"html": false,
"inserter": true
}
}

View File

@@ -0,0 +1,350 @@
<?php
/**
* Server-side rendering of the `core/navigation-link` block.
*
* @package gutenberg
*/
/**
* Build an array with CSS classes and inline styles defining the colors
* which will be applied to the navigation markup in the front-end.
*
* @param array $context Navigation block context.
* @param array $attributes Block attributes.
* @return array Colors CSS classes and inline styles.
*/
function block_core_navigation_link_build_css_colors( $context, $attributes ) {
$colors = array(
'css_classes' => array(),
'inline_styles' => '',
);
$is_sub_menu = isset( $attributes['isTopLevelLink'] ) ? ( ! $attributes['isTopLevelLink'] ) : false;
// Text color.
$named_text_color = null;
$custom_text_color = null;
if ( $is_sub_menu && array_key_exists( 'customOverlayTextColor', $context ) ) {
$custom_text_color = $context['customOverlayTextColor'];
} elseif ( $is_sub_menu && array_key_exists( 'overlayTextColor', $context ) ) {
$named_text_color = $context['overlayTextColor'];
} elseif ( array_key_exists( 'customTextColor', $context ) ) {
$custom_text_color = $context['customTextColor'];
} elseif ( array_key_exists( 'textColor', $context ) ) {
$named_text_color = $context['textColor'];
} elseif ( isset( $context['style']['color']['text'] ) ) {
$custom_text_color = $context['style']['color']['text'];
}
// If has text color.
if ( ! is_null( $named_text_color ) ) {
// Add the color class.
array_push( $colors['css_classes'], 'has-text-color', sprintf( 'has-%s-color', $named_text_color ) );
} elseif ( ! is_null( $custom_text_color ) ) {
// Add the custom color inline style.
$colors['css_classes'][] = 'has-text-color';
$colors['inline_styles'] .= sprintf( 'color: %s;', $custom_text_color );
}
// Background color.
$named_background_color = null;
$custom_background_color = null;
if ( $is_sub_menu && array_key_exists( 'customOverlayBackgroundColor', $context ) ) {
$custom_background_color = $context['customOverlayBackgroundColor'];
} elseif ( $is_sub_menu && array_key_exists( 'overlayBackgroundColor', $context ) ) {
$named_background_color = $context['overlayBackgroundColor'];
} elseif ( array_key_exists( 'customBackgroundColor', $context ) ) {
$custom_background_color = $context['customBackgroundColor'];
} elseif ( array_key_exists( 'backgroundColor', $context ) ) {
$named_background_color = $context['backgroundColor'];
} elseif ( isset( $context['style']['color']['background'] ) ) {
$custom_background_color = $context['style']['color']['background'];
}
// If has background color.
if ( ! is_null( $named_background_color ) ) {
// Add the background-color class.
array_push( $colors['css_classes'], 'has-background', sprintf( 'has-%s-background-color', $named_background_color ) );
} elseif ( ! is_null( $custom_background_color ) ) {
// Add the custom background-color inline style.
$colors['css_classes'][] = 'has-background';
$colors['inline_styles'] .= sprintf( 'background-color: %s;', $custom_background_color );
}
return $colors;
}
/**
* Build an array with CSS classes and inline styles defining the font sizes
* which will be applied to the navigation markup in the front-end.
*
* @param array $context Navigation block context.
* @return array Font size CSS classes and inline styles.
*/
function block_core_navigation_link_build_css_font_sizes( $context ) {
// CSS classes.
$font_sizes = array(
'css_classes' => array(),
'inline_styles' => '',
);
$has_named_font_size = array_key_exists( 'fontSize', $context );
$has_custom_font_size = isset( $context['style']['typography']['fontSize'] );
if ( $has_named_font_size ) {
// Add the font size class.
$font_sizes['css_classes'][] = sprintf( 'has-%s-font-size', $context['fontSize'] );
} elseif ( $has_custom_font_size ) {
// Add the custom font size inline style.
$font_sizes['inline_styles'] = sprintf( 'font-size: %spx;', $context['style']['typography']['fontSize'] );
}
return $font_sizes;
}
/**
* Returns the top-level submenu SVG chevron icon.
*
* @return string
*/
function block_core_navigation_link_render_submenu_icon() {
return '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none" role="img" aria-hidden="true" focusable="false"><path d="M1.50002 4L6.00002 8L10.5 4" stroke-width="1.5"></path></svg>';
}
/**
* Renders the `core/navigation-link` block.
*
* @param array $attributes The block attributes.
* @param array $content The saved content.
* @param array $block The parsed block.
*
* @return string Returns the post content with the legacy widget added.
*/
function render_block_core_navigation_link( $attributes, $content, $block ) {
$navigation_link_has_id = isset( $attributes['id'] ) && is_numeric( $attributes['id'] );
$is_post_type = isset( $attributes['kind'] ) && 'post-type' === $attributes['kind'];
$is_post_type = $is_post_type || isset( $attributes['type'] ) && ( 'post' === $attributes['type'] || 'page' === $attributes['type'] );
// Don't render the block's subtree if it is a draft or if the ID does not exist.
if ( $is_post_type && $navigation_link_has_id ) {
$post = get_post( $attributes['id'] );
if ( ! $post || 'publish' !== $post->post_status ) {
return '';
}
}
// Don't render the block's subtree if it has no label.
if ( empty( $attributes['label'] ) ) {
return '';
}
$colors = block_core_navigation_link_build_css_colors( $block->context, $attributes );
$font_sizes = block_core_navigation_link_build_css_font_sizes( $block->context );
$classes = array_merge(
$colors['css_classes'],
$font_sizes['css_classes']
);
$style_attribute = ( $colors['inline_styles'] . $font_sizes['inline_styles'] );
$css_classes = trim( implode( ' ', $classes ) );
$has_submenu = count( $block->inner_blocks ) > 0;
$is_active = ! empty( $attributes['id'] ) && ( get_the_ID() === $attributes['id'] );
$wrapper_attributes = get_block_wrapper_attributes(
array(
'class' => $css_classes . ' wp-block-navigation-item' . ( $has_submenu ? ' has-child' : '' ) .
( $is_active ? ' current-menu-item' : '' ),
'style' => $style_attribute,
)
);
$html = '<li ' . $wrapper_attributes . '>' .
'<a class="wp-block-navigation-item__content" ';
// Start appending HTML attributes to anchor tag.
if ( isset( $attributes['url'] ) ) {
$html .= ' href="' . esc_url( $attributes['url'] ) . '"';
}
if ( $is_active ) {
$html .= ' aria-current="page"';
}
if ( isset( $attributes['opensInNewTab'] ) && true === $attributes['opensInNewTab'] ) {
$html .= ' target="_blank" ';
}
if ( isset( $attributes['rel'] ) ) {
$html .= ' rel="' . esc_attr( $attributes['rel'] ) . '"';
} elseif ( isset( $attributes['nofollow'] ) && $attributes['nofollow'] ) {
$html .= ' rel="nofollow"';
}
if ( isset( $attributes['title'] ) ) {
$html .= ' title="' . esc_attr( $attributes['title'] ) . '"';
}
// End appending HTML attributes to anchor tag.
// Start anchor tag content.
$html .= '>' .
// Wrap title with span to isolate it from submenu icon.
'<span class="wp-block-navigation-item__label">';
if ( isset( $attributes['label'] ) ) {
$html .= wp_kses(
$attributes['label'],
array(
'code' => array(),
'em' => array(),
'img' => array(
'scale' => array(),
'class' => array(),
'style' => array(),
'src' => array(),
'alt' => array(),
),
's' => array(),
'span' => array(
'style' => array(),
),
'strong' => array(),
)
);
}
$html .= '</span>';
if ( isset( $block->context['showSubmenuIcon'] ) && $block->context['showSubmenuIcon'] && $has_submenu ) {
// The submenu icon can be hidden by a CSS rule on the Navigation Block.
$html .= '<span class="wp-block-navigation__submenu-icon">' . block_core_navigation_link_render_submenu_icon() . '</span>';
}
$html .= '</a>';
// End anchor tag content.
if ( $has_submenu ) {
$inner_blocks_html = '';
foreach ( $block->inner_blocks as $inner_block ) {
$inner_blocks_html .= $inner_block->render();
}
$html .= sprintf(
'<ul class="wp-block-navigation__submenu-container">%s</ul>',
$inner_blocks_html
);
}
$html .= '</li>';
return $html;
}
/**
* Returns a navigation link variation
*
* @param WP_Taxonomy|WP_Post_Type $entity post type or taxonomy entity.
* @param string $kind string of value 'taxonomy' or 'post-type'.
*
* @return array
*/
function build_variation_for_navigation_link( $entity, $kind ) {
$title = '';
$description = '';
if ( property_exists( $entity->labels, 'item_link' ) ) {
$title = $entity->labels->item_link;
}
if ( property_exists( $entity->labels, 'item_link_description' ) ) {
$description = $entity->labels->item_link_description;
}
$variation = array(
'name' => $entity->name,
'title' => $title,
'description' => $description,
'attributes' => array(
'type' => $entity->name,
'kind' => $kind,
),
);
// Tweak some value for the variations.
$variation_overrides = array(
'post_tag' => array(
'name' => 'tag',
'attributes' => array(
'type' => 'tag',
'kind' => $kind,
),
),
'post_format' => array(
// The item_link and item_link_description for post formats is the
// same as for tags, so need to be overridden.
'title' => __( 'Post Format Link' ),
'description' => __( 'A link to a post format' ),
'attributes' => array(
'type' => 'post_format',
'kind' => $kind,
),
),
);
if ( array_key_exists( $entity->name, $variation_overrides ) ) {
$variation = array_merge(
$variation,
$variation_overrides[ $entity->name ]
);
}
return $variation;
}
/**
* Register the navigation link block.
*
* @uses render_block_core_navigation()
* @throws WP_Error An WP_Error exception parsing the block definition.
*/
function register_block_core_navigation_link() {
$post_types = get_post_types( array( 'show_in_nav_menus' => true ), 'objects' );
$taxonomies = get_taxonomies( array( 'show_in_nav_menus' => true ), 'objects' );
// Use two separate arrays as a way to order the variations in the UI.
// Known variations (like Post Link and Page Link) are added to the
// `built_ins` array. Variations for custom post types and taxonomies are
// added to the `variations` array and will always appear after `built-ins.
$built_ins = array();
$variations = array();
if ( $post_types ) {
foreach ( $post_types as $post_type ) {
$variation = build_variation_for_navigation_link( $post_type, 'post-type' );
if ( $post_type->_builtin ) {
$built_ins[] = $variation;
} else {
$variations[] = $variation;
}
}
}
if ( $taxonomies ) {
foreach ( $taxonomies as $taxonomy ) {
$variation = build_variation_for_navigation_link( $taxonomy, 'taxonomy' );
if ( $taxonomy->_builtin ) {
$built_ins[] = $variation;
} else {
$variations[] = $variation;
}
}
}
register_block_type_from_metadata(
__DIR__ . '/navigation-link',
array(
'render_callback' => 'render_block_core_navigation_link',
'variations' => array_merge( $built_ins, $variations ),
)
);
}
add_action( 'init', 'register_block_core_navigation_link' );

View File

@@ -0,0 +1,65 @@
{
"apiVersion": 2,
"name": "core/navigation-link",
"title": "Custom Link",
"category": "design",
"parent": [
"core/navigation"
],
"description": "Add a page, link, or another item to your navigation.",
"textdomain": "default",
"attributes": {
"label": {
"type": "string"
},
"type": {
"type": "string"
},
"description": {
"type": "string"
},
"rel": {
"type": "string"
},
"id": {
"type": "number"
},
"opensInNewTab": {
"type": "boolean",
"default": false
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"kind": {
"type": "string"
},
"isTopLevelLink": {
"type": "boolean"
}
},
"usesContext": [
"textColor",
"customTextColor",
"backgroundColor",
"customBackgroundColor",
"overlayTextColor",
"customOverlayTextColor",
"overlayBackgroundColor",
"customOverlayBackgroundColor",
"fontSize",
"customFontSize",
"showSubmenuIcon",
"style"
],
"supports": {
"reusable": false,
"html": false,
"__experimentalSlashInserter": true
},
"editorStyle": "wp-block-navigation-link-editor",
"style": "wp-block-navigation-link"
}

View File

@@ -0,0 +1,300 @@
<?php
/**
* Server-side rendering of the `core/navigation-submenu` block.
*
* @package gutenberg
*/
/**
* Build an array with CSS classes and inline styles defining the colors
* which will be applied to the navigation markup in the front-end.
*
* @param array $context Navigation block context.
* @param array $attributes Block attributes.
* @return array Colors CSS classes and inline styles.
*/
function block_core_navigation_submenu_build_css_colors( $context, $attributes ) {
$colors = array(
'css_classes' => array(),
'inline_styles' => '',
);
$is_sub_menu = isset( $attributes['isTopLevelLink'] ) ? ( ! $attributes['isTopLevelLink'] ) : false;
// Text color.
$named_text_color = null;
$custom_text_color = null;
if ( $is_sub_menu && array_key_exists( 'customOverlayTextColor', $context ) ) {
$custom_text_color = $context['customOverlayTextColor'];
} elseif ( $is_sub_menu && array_key_exists( 'overlayTextColor', $context ) ) {
$named_text_color = $context['overlayTextColor'];
} elseif ( array_key_exists( 'customTextColor', $context ) ) {
$custom_text_color = $context['customTextColor'];
} elseif ( array_key_exists( 'textColor', $context ) ) {
$named_text_color = $context['textColor'];
} elseif ( isset( $context['style']['color']['text'] ) ) {
$custom_text_color = $context['style']['color']['text'];
}
// If has text color.
if ( ! is_null( $named_text_color ) ) {
// Add the color class.
array_push( $colors['css_classes'], 'has-text-color', sprintf( 'has-%s-color', $named_text_color ) );
} elseif ( ! is_null( $custom_text_color ) ) {
// Add the custom color inline style.
$colors['css_classes'][] = 'has-text-color';
$colors['inline_styles'] .= sprintf( 'color: %s;', $custom_text_color );
}
// Background color.
$named_background_color = null;
$custom_background_color = null;
if ( $is_sub_menu && array_key_exists( 'customOverlayBackgroundColor', $context ) ) {
$custom_background_color = $context['customOverlayBackgroundColor'];
} elseif ( $is_sub_menu && array_key_exists( 'overlayBackgroundColor', $context ) ) {
$named_background_color = $context['overlayBackgroundColor'];
} elseif ( array_key_exists( 'customBackgroundColor', $context ) ) {
$custom_background_color = $context['customBackgroundColor'];
} elseif ( array_key_exists( 'backgroundColor', $context ) ) {
$named_background_color = $context['backgroundColor'];
} elseif ( isset( $context['style']['color']['background'] ) ) {
$custom_background_color = $context['style']['color']['background'];
}
// If has background color.
if ( ! is_null( $named_background_color ) ) {
// Add the background-color class.
array_push( $colors['css_classes'], 'has-background', sprintf( 'has-%s-background-color', $named_background_color ) );
} elseif ( ! is_null( $custom_background_color ) ) {
// Add the custom background-color inline style.
$colors['css_classes'][] = 'has-background';
$colors['inline_styles'] .= sprintf( 'background-color: %s;', $custom_background_color );
}
return $colors;
}
/**
* Build an array with CSS classes and inline styles defining the font sizes
* which will be applied to the navigation markup in the front-end.
*
* @param array $context Navigation block context.
* @return array Font size CSS classes and inline styles.
*/
function block_core_navigation_submenu_build_css_font_sizes( $context ) {
// CSS classes.
$font_sizes = array(
'css_classes' => array(),
'inline_styles' => '',
);
$has_named_font_size = array_key_exists( 'fontSize', $context );
$has_custom_font_size = isset( $context['style']['typography']['fontSize'] );
if ( $has_named_font_size ) {
// Add the font size class.
$font_sizes['css_classes'][] = sprintf( 'has-%s-font-size', $context['fontSize'] );
} elseif ( $has_custom_font_size ) {
// Add the custom font size inline style.
$font_sizes['inline_styles'] = sprintf( 'font-size: %spx;', $context['style']['typography']['fontSize'] );
}
return $font_sizes;
}
/**
* Returns the top-level submenu SVG chevron icon.
*
* @return string
*/
function block_core_navigation_submenu_render_submenu_icon() {
return '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none" role="img" aria-hidden="true" focusable="false"><path d="M1.50002 4L6.00002 8L10.5 4" stroke-width="1.5"></path></svg>';
}
/**
* Renders the `core/navigation-submenu` block.
*
* @param array $attributes The block attributes.
* @param string $content The saved content.
* @param object $block The parsed block.
*
* @return string Returns the post content with the legacy widget added.
*/
function render_block_core_navigation_submenu( $attributes, $content, $block ) {
$navigation_link_has_id = isset( $attributes['id'] ) && is_numeric( $attributes['id'] );
$is_post_type = isset( $attributes['kind'] ) && 'post-type' === $attributes['kind'];
$is_post_type = $is_post_type || isset( $attributes['type'] ) && ( 'post' === $attributes['type'] || 'page' === $attributes['type'] );
// Don't render the block's subtree if it is a draft.
if ( $is_post_type && $navigation_link_has_id && 'publish' !== get_post_status( $attributes['id'] ) ) {
return '';
}
// Don't render the block's subtree if it has no label.
if ( empty( $attributes['label'] ) ) {
return '';
}
$colors = block_core_navigation_submenu_build_css_colors( $block->context, $attributes );
$font_sizes = block_core_navigation_submenu_build_css_font_sizes( $block->context );
$classes = array_merge(
$colors['css_classes'],
$font_sizes['css_classes']
);
$style_attribute = ( $colors['inline_styles'] . $font_sizes['inline_styles'] );
$css_classes = trim( implode( ' ', $classes ) );
$has_submenu = count( $block->inner_blocks ) > 0;
$is_active = ! empty( $attributes['id'] ) && ( get_the_ID() === $attributes['id'] );
$class_name = ! empty( $attributes['className'] ) ? implode( ' ', (array) $attributes['className'] ) : false;
if ( false !== $class_name ) {
$css_classes .= ' ' . $class_name;
}
$show_submenu_indicators = isset( $block->context['showSubmenuIcon'] ) && $block->context['showSubmenuIcon'];
$open_on_click = isset( $block->context['openSubmenusOnClick'] ) && $block->context['openSubmenusOnClick'];
$open_on_hover_and_click = isset( $block->context['openSubmenusOnClick'] ) && ! $block->context['openSubmenusOnClick'] &&
$show_submenu_indicators;
$wrapper_attributes = get_block_wrapper_attributes(
array(
'class' => $css_classes . ' wp-block-navigation-item' . ( $has_submenu ? ' has-child' : '' ) .
( $open_on_click ? ' open-on-click' : '' ) . ( $open_on_hover_and_click ? ' open-on-hover-click' : '' ) .
( $is_active ? ' current-menu-item' : '' ),
'style' => $style_attribute,
)
);
$html = '<li ' . $wrapper_attributes . '>';
// If Submenus open on hover, we render an anchor tag with attributes.
// If submenu icons are set to show, we also render a submenu button, so the submenu can be opened on click.
if ( ! $open_on_click ) {
$item_url = isset( $attributes['url'] ) ? esc_url( $attributes['url'] ) : '';
// Start appending HTML attributes to anchor tag.
$html .= '<a class="wp-block-navigation-item__content" href="' . $item_url . '"';
if ( $is_active ) {
$html .= ' aria-current="page"';
}
if ( isset( $attributes['opensInNewTab'] ) && true === $attributes['opensInNewTab'] ) {
$html .= ' target="_blank" ';
}
if ( isset( $attributes['rel'] ) ) {
$html .= ' rel="' . esc_attr( $attributes['rel'] ) . '"';
} elseif ( isset( $attributes['nofollow'] ) && $attributes['nofollow'] ) {
$html .= ' rel="nofollow"';
}
if ( isset( $attributes['title'] ) ) {
$html .= ' title="' . esc_attr( $attributes['title'] ) . '"';
}
$html .= '>';
// End appending HTML attributes to anchor tag.
if ( isset( $attributes['label'] ) ) {
$html .= wp_kses(
$attributes['label'],
array(
'code' => array(),
'em' => array(),
'img' => array(
'scale' => array(),
'class' => array(),
'style' => array(),
'src' => array(),
'alt' => array(),
),
's' => array(),
'span' => array(
'style' => array(),
),
'strong' => array(),
)
);
}
$html .= '</a>';
// End anchor tag content.
if ( $show_submenu_indicators ) {
// The submenu icon is rendered in a button here
// so that there's a clickable elment to open the submenu.
$html .= '<button class="wp-block-navigation__submenu-icon wp-block-navigation-submenu__toggle" aria-expanded="false">' . block_core_navigation_submenu_render_submenu_icon() . '</button>';
}
} else {
// If menus open on click, we render the parent as a button.
$html .= '<button class="wp-block-navigation-item__content wp-block-navigation-submenu__toggle" aria-expanded="false">';
// Wrap title with span to isolate it from submenu icon.
$html .= '<span class="wp-block-navigation-item__label">';
if ( isset( $attributes['label'] ) ) {
$html .= wp_kses(
$attributes['label'],
array(
'code' => array(),
'em' => array(),
'img' => array(
'scale' => array(),
'class' => array(),
'style' => array(),
'src' => array(),
'alt' => array(),
),
's' => array(),
'span' => array(
'style' => array(),
),
'strong' => array(),
)
);
}
$html .= '</span>';
$html .= '<span class="wp-block-navigation__submenu-icon">' . block_core_navigation_submenu_render_submenu_icon() . '</span>';
$html .= '</button>';
}
if ( $has_submenu ) {
$inner_blocks_html = '';
foreach ( $block->inner_blocks as $inner_block ) {
$inner_blocks_html .= $inner_block->render();
}
$html .= sprintf(
'<ul class="wp-block-navigation__submenu-container">%s</ul>',
$inner_blocks_html
);
}
$html .= '</li>';
return $html;
}
/**
* Register the navigation submenu block.
*
* @uses render_block_core_navigation_submenu()
* @throws WP_Error An WP_Error exception parsing the block definition.
*/
function register_block_core_navigation_submenu() {
register_block_type_from_metadata(
__DIR__ . '/navigation-submenu',
array(
'render_callback' => 'render_block_core_navigation_submenu',
)
);
}
add_action( 'init', 'register_block_core_navigation_submenu' );

View File

@@ -0,0 +1,65 @@
{
"apiVersion": 2,
"name": "core/navigation-submenu",
"title": "Submenu",
"category": "design",
"parent": [
"core/navigation"
],
"description": "Add a submenu to your navigation.",
"textdomain": "default",
"attributes": {
"label": {
"type": "string"
},
"type": {
"type": "string"
},
"description": {
"type": "string"
},
"rel": {
"type": "string"
},
"id": {
"type": "number"
},
"opensInNewTab": {
"type": "boolean",
"default": false
},
"url": {
"type": "string"
},
"title": {
"type": "string"
},
"kind": {
"type": "string"
},
"isTopLevelItem": {
"type": "boolean"
}
},
"usesContext": [
"textColor",
"customTextColor",
"backgroundColor",
"customBackgroundColor",
"overlayTextColor",
"customOverlayTextColor",
"overlayBackgroundColor",
"customOverlayBackgroundColor",
"fontSize",
"customFontSize",
"showSubmenuIcon",
"openSubmenusOnClick",
"style"
],
"supports": {
"reusable": false,
"html": false
},
"editorStyle": "wp-block-navigation-submenu-editor",
"style": "wp-block-navigation-submenu"
}

View File

@@ -0,0 +1,429 @@
<?php
/**
* Server-side rendering of the `core/navigation` block.
*
* @package gutenberg
*/
/**
* Build an array with CSS classes and inline styles defining the colors
* which will be applied to the navigation markup in the front-end.
*
* @param array $attributes Navigation block attributes.
* @return array Colors CSS classes and inline styles.
*/
function block_core_navigation_build_css_colors( $attributes ) {
$colors = array(
'css_classes' => array(),
'inline_styles' => '',
);
// Text color.
$has_named_text_color = array_key_exists( 'textColor', $attributes );
$has_custom_text_color = array_key_exists( 'customTextColor', $attributes );
// If has text color.
if ( $has_custom_text_color || $has_named_text_color ) {
// Add has-text-color class.
$colors['css_classes'][] = 'has-text-color';
}
if ( $has_named_text_color ) {
// Add the color class.
$colors['css_classes'][] = sprintf( 'has-%s-color', $attributes['textColor'] );
} elseif ( $has_custom_text_color ) {
// Add the custom color inline style.
$colors['inline_styles'] .= sprintf( 'color: %s;', $attributes['customTextColor'] );
}
// Background color.
$has_named_background_color = array_key_exists( 'backgroundColor', $attributes );
$has_custom_background_color = array_key_exists( 'customBackgroundColor', $attributes );
// If has background color.
if ( $has_custom_background_color || $has_named_background_color ) {
// Add has-background class.
$colors['css_classes'][] = 'has-background';
}
if ( $has_named_background_color ) {
// Add the background-color class.
$colors['css_classes'][] = sprintf( 'has-%s-background-color', $attributes['backgroundColor'] );
} elseif ( $has_custom_background_color ) {
// Add the custom background-color inline style.
$colors['inline_styles'] .= sprintf( 'background-color: %s;', $attributes['customBackgroundColor'] );
}
return $colors;
}
/**
* Build an array with CSS classes and inline styles defining the font sizes
* which will be applied to the navigation markup in the front-end.
*
* @param array $attributes Navigation block attributes.
* @return array Font size CSS classes and inline styles.
*/
function block_core_navigation_build_css_font_sizes( $attributes ) {
// CSS classes.
$font_sizes = array(
'css_classes' => array(),
'inline_styles' => '',
);
$has_named_font_size = array_key_exists( 'fontSize', $attributes );
$has_custom_font_size = array_key_exists( 'customFontSize', $attributes );
if ( $has_named_font_size ) {
// Add the font size class.
$font_sizes['css_classes'][] = sprintf( 'has-%s-font-size', $attributes['fontSize'] );
} elseif ( $has_custom_font_size ) {
// Add the custom font size inline style.
$font_sizes['inline_styles'] = sprintf( 'font-size: %spx;', $attributes['customFontSize'] );
}
return $font_sizes;
}
/**
* Returns the menu items for a WordPress menu location.
*
* @param string $location The menu location.
* @return array Menu items for the location.
*/
function gutenberg_get_menu_items_at_location( $location ) {
if ( empty( $location ) ) {
return;
}
// Build menu data. The following approximates the code in
// `wp_nav_menu()` and `gutenberg_output_block_nav_menu`.
// Find the location in the list of locations, returning early if the
// location can't be found.
$locations = get_nav_menu_locations();
if ( ! isset( $locations[ $location ] ) ) {
return;
}
// Get the menu from the location, returning early if there is no
// menu or there was an error.
$menu = wp_get_nav_menu_object( $locations[ $location ] );
if ( ! $menu || is_wp_error( $menu ) ) {
return;
}
$menu_items = wp_get_nav_menu_items( $menu->term_id, array( 'update_post_term_cache' => false ) );
_wp_menu_item_classes_by_context( $menu_items );
return $menu_items;
}
/**
* Sorts a standard array of menu items into a nested structure keyed by the
* id of the parent menu.
*
* @param array $menu_items Menu items to sort.
* @return array An array keyed by the id of the parent menu where each element
* is an array of menu items that belong to that parent.
*/
function gutenberg_sort_menu_items_by_parent_id( $menu_items ) {
$sorted_menu_items = array();
foreach ( (array) $menu_items as $menu_item ) {
$sorted_menu_items[ $menu_item->menu_order ] = $menu_item;
}
unset( $menu_items, $menu_item );
$menu_items_by_parent_id = array();
foreach ( $sorted_menu_items as $menu_item ) {
$menu_items_by_parent_id[ $menu_item->menu_item_parent ][] = $menu_item;
}
return $menu_items_by_parent_id;
}
/**
* Turns menu item data into a nested array of parsed blocks
*
* @param array $menu_items An array of menu items that represent
* an individual level of a menu.
* @param array $menu_items_by_parent_id An array keyed by the id of the
* parent menu where each element is an
* array of menu items that belong to
* that parent.
* @return array An array of parsed block data.
*/
function gutenberg_parse_blocks_from_menu_items( $menu_items, $menu_items_by_parent_id ) {
if ( empty( $menu_items ) ) {
return array();
}
$blocks = array();
foreach ( $menu_items as $menu_item ) {
$class_name = ! empty( $menu_item->classes ) ? implode( ' ', (array) $menu_item->classes ) : null;
$id = ( null !== $menu_item->object_id && 'custom' !== $menu_item->object ) ? $menu_item->object_id : null;
$opens_in_new_tab = null !== $menu_item->target && '_blank' === $menu_item->target;
$rel = ( null !== $menu_item->xfn && '' !== $menu_item->xfn ) ? $menu_item->xfn : null;
$kind = null !== $menu_item->type ? str_replace( '_', '-', $menu_item->type ) : 'custom';
$block = array(
'blockName' => 'core/navigation-link',
'attrs' => array(
'className' => $class_name,
'description' => $menu_item->description,
'id' => $id,
'kind' => $kind,
'label' => $menu_item->title,
'opensInNewTab' => $opens_in_new_tab,
'rel' => $rel,
'title' => $menu_item->attr_title,
'type' => $menu_item->object,
'url' => $menu_item->url,
),
);
$block['innerBlocks'] = gutenberg_parse_blocks_from_menu_items(
isset( $menu_items_by_parent_id[ $menu_item->ID ] )
? $menu_items_by_parent_id[ $menu_item->ID ]
: array(),
$menu_items_by_parent_id
);
$blocks[] = $block;
}
return $blocks;
}
/**
* Returns the top-level submenu SVG chevron icon.
*
* @return string
*/
function block_core_navigation_render_submenu_icon() {
return '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12" fill="none" role="img" aria-hidden="true" focusable="false"><path d="M1.50002 4L6.00002 8L10.5 4" stroke-width="1.5"></path></svg>';
}
/**
* Renders the `core/navigation` block on server.
*
* @param array $attributes The block attributes.
* @param array $content The saved content.
* @param array $block The parsed block.
*
* @return string Returns the post content with the legacy widget added.
*/
function render_block_core_navigation( $attributes, $content, $block ) {
/**
* Deprecated:
* The rgbTextColor and rgbBackgroundColor attributes
* have been deprecated in favor of
* customTextColor and customBackgroundColor ones.
* Move the values from old attrs to the new ones.
*/
if ( isset( $attributes['rgbTextColor'] ) && empty( $attributes['textColor'] ) ) {
$attributes['customTextColor'] = $attributes['rgbTextColor'];
}
if ( isset( $attributes['rgbBackgroundColor'] ) && empty( $attributes['backgroundColor'] ) ) {
$attributes['customBackgroundColor'] = $attributes['rgbBackgroundColor'];
}
unset( $attributes['rgbTextColor'], $attributes['rgbBackgroundColor'] );
/**
* This is for backwards compatibility after `isResponsive` attribute has been removed.
*/
$has_old_responsive_attribute = ! empty( $attributes['isResponsive'] ) && $attributes['isResponsive'];
$is_responsive_menu = isset( $attributes['overlayMenu'] ) && 'never' !== $attributes['overlayMenu'] || $has_old_responsive_attribute;
$should_load_view_script = ! wp_script_is( 'wp-block-navigation-view' ) && ( $is_responsive_menu || $attributes['openSubmenusOnClick'] || $attributes['showSubmenuIcon'] );
if ( $should_load_view_script ) {
wp_enqueue_script( 'wp-block-navigation-view' );
}
$inner_blocks = $block->inner_blocks;
// If `__unstableLocation` is defined, create inner blocks from the classic menu assigned to that location.
if ( empty( $inner_blocks ) && array_key_exists( '__unstableLocation', $attributes ) ) {
$menu_items = gutenberg_get_menu_items_at_location( $attributes['__unstableLocation'] );
if ( empty( $menu_items ) ) {
return '';
}
$menu_items_by_parent_id = gutenberg_sort_menu_items_by_parent_id( $menu_items );
$parsed_blocks = gutenberg_parse_blocks_from_menu_items( $menu_items_by_parent_id[0], $menu_items_by_parent_id );
$inner_blocks = new WP_Block_List( $parsed_blocks, $attributes );
}
if ( ! empty( $block->context['navigationArea'] ) ) {
$area = $block->context['navigationArea'];
$mapping = get_option( 'fse_navigation_areas', array() );
if ( ! empty( $mapping[ $area ] ) ) {
$attributes['navigationMenuId'] = $mapping[ $area ];
}
}
// Load inner blocks from the navigation post.
if ( array_key_exists( 'navigationMenuId', $attributes ) ) {
$navigation_post = get_post( $attributes['navigationMenuId'] );
if ( ! isset( $navigation_post ) ) {
return '';
}
$parsed_blocks = parse_blocks( $navigation_post->post_content );
// 'parse_blocks' includes a null block with '\n\n' as the content when
// it encounters whitespace. This code strips it.
$compacted_blocks = array_filter(
$parsed_blocks,
function( $block ) {
return isset( $block['blockName'] );
}
);
// TODO - this uses the full navigation block attributes for the
// context which could be refined.
$inner_blocks = new WP_Block_List( $compacted_blocks, $attributes );
}
if ( empty( $inner_blocks ) ) {
return '';
}
$colors = block_core_navigation_build_css_colors( $attributes );
$font_sizes = block_core_navigation_build_css_font_sizes( $attributes );
$classes = array_merge(
$colors['css_classes'],
$font_sizes['css_classes'],
$is_responsive_menu ? array( 'is-responsive' ) : array()
);
$inner_blocks_html = '';
$is_list_open = false;
foreach ( $inner_blocks as $inner_block ) {
if ( ( 'core/navigation-link' === $inner_block->name || 'core/home-link' === $inner_block->name || 'core/site-title' === $inner_block->name || 'core/site-logo' === $inner_block->name || 'core/navigation-submenu' === $inner_block->name ) && ! $is_list_open ) {
$is_list_open = true;
$inner_blocks_html .= '<ul class="wp-block-navigation__container">';
}
if ( 'core/navigation-link' !== $inner_block->name && 'core/home-link' !== $inner_block->name && 'core/site-title' !== $inner_block->name && 'core/site-logo' !== $inner_block->name && 'core/navigation-submenu' !== $inner_block->name && $is_list_open ) {
$is_list_open = false;
$inner_blocks_html .= '</ul>';
}
if ( 'core/site-title' === $inner_block->name || 'core/site-logo' === $inner_block->name ) {
$inner_blocks_html .= '<li class="wp-block-navigation-item">' . $inner_block->render() . '</li>';
} else {
$inner_blocks_html .= $inner_block->render();
}
}
if ( $is_list_open ) {
$inner_blocks_html .= '</ul>';
}
$block_styles = isset( $attributes['styles'] ) ? $attributes['styles'] : '';
$wrapper_attributes = get_block_wrapper_attributes(
array(
'class' => implode( ' ', $classes ),
'style' => $block_styles . $colors['inline_styles'] . $font_sizes['inline_styles'],
)
);
$modal_unique_id = uniqid();
// Determine whether or not navigation elements should be wrapped in the markup required to make it responsive,
// return early if they don't.
if ( ! $is_responsive_menu ) {
return sprintf(
'<nav %1$s>%2$s</nav>',
$wrapper_attributes,
$inner_blocks_html
);
}
$is_hidden_by_default = isset( $attributes['overlayMenu'] ) && 'always' === $attributes['overlayMenu'];
$responsive_container_classes = array(
'wp-block-navigation__responsive-container',
$is_hidden_by_default ? 'hidden-by-default' : '',
);
$open_button_classes = array(
'wp-block-navigation__responsive-container-open',
$is_hidden_by_default ? 'always-shown' : '',
);
$responsive_container_markup = sprintf(
'<button aria-expanded="false" aria-haspopup="true" aria-label="%3$s" class="%6$s" data-micromodal-trigger="modal-%1$s"><svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-hidden="true" focusable="false"><rect x="4" y="7.5" width="16" height="1.5" /><rect x="4" y="15" width="16" height="1.5" /></svg></button>
<div class="%5$s" id="modal-%1$s" aria-hidden="true">
<div class="wp-block-navigation__responsive-close" tabindex="-1" data-micromodal-close>
<div class="wp-block-navigation__responsive-dialog" role="dialog" aria-modal="true" aria-labelledby="modal-%1$s-title" >
<button aria-label="%4$s" data-micromodal-close class="wp-block-navigation__responsive-container-close"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" role="img" aria-hidden="true" focusable="false"><path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path></svg></button>
<div class="wp-block-navigation__responsive-container-content" id="modal-%1$s-content">
%2$s
</div>
</div>
</div>
</div>',
$modal_unique_id,
$inner_blocks_html,
__( 'Open menu' ), // Open button label.
__( 'Close menu' ), // Close button label.
implode( ' ', $responsive_container_classes ),
implode( ' ', $open_button_classes )
);
return sprintf(
'<nav %1$s>%2$s</nav>',
$wrapper_attributes,
$responsive_container_markup
);
}
/**
* Register the navigation block.
*
* @uses render_block_core_navigation()
* @throws WP_Error An WP_Error exception parsing the block definition.
*/
function register_block_core_navigation() {
register_block_type_from_metadata(
__DIR__ . '/navigation',
array(
'render_callback' => 'render_block_core_navigation',
)
);
}
add_action( 'init', 'register_block_core_navigation' );
/**
* Filter that changes the parsed attribute values of navigation blocks contain typographic presets to contain the values directly.
*
* @param array $parsed_block The block being rendered.
* @return array The block being rendered without typographic presets.
*/
function block_core_navigation_typographic_presets_backcompatibility( $parsed_block ) {
if ( 'core/navigation' === $parsed_block['blockName'] ) {
$attribute_to_prefix_map = array(
'fontStyle' => 'var:preset|font-style|',
'fontWeight' => 'var:preset|font-weight|',
'textDecoration' => 'var:preset|text-decoration|',
'textTransform' => 'var:preset|text-transform|',
);
foreach ( $attribute_to_prefix_map as $style_attribute => $prefix ) {
if ( ! empty( $parsed_block['attrs']['style']['typography'][ $style_attribute ] ) ) {
$prefix_len = strlen( $prefix );
$attribute_value = &$parsed_block['attrs']['style']['typography'][ $style_attribute ];
if ( 0 === strncmp( $attribute_value, $prefix, $prefix_len ) ) {
$attribute_value = substr( $attribute_value, $prefix_len );
}
if ( 'textDecoration' === $style_attribute && 'strikethrough' === $attribute_value ) {
$attribute_value = 'line-through';
}
}
}
}
return $parsed_block;
}
add_filter( 'render_block_data', 'block_core_navigation_typographic_presets_backcompatibility' );

View File

@@ -0,0 +1,124 @@
{
"apiVersion": 2,
"name": "core/navigation",
"title": "Navigation",
"category": "theme",
"description": "A collection of blocks that allow visitors to get around your site.",
"keywords": [
"menu",
"navigation",
"links"
],
"textdomain": "default",
"attributes": {
"navigationMenuId": {
"type": "number"
},
"textColor": {
"type": "string"
},
"customTextColor": {
"type": "string"
},
"rgbTextColor": {
"type": "string"
},
"backgroundColor": {
"type": "string"
},
"customBackgroundColor": {
"type": "string"
},
"rgbBackgroundColor": {
"type": "string"
},
"showSubmenuIcon": {
"type": "boolean",
"default": true
},
"openSubmenusOnClick": {
"type": "boolean",
"default": false
},
"overlayMenu": {
"type": "string",
"default": "mobile"
},
"__unstableLocation": {
"type": "string"
},
"overlayBackgroundColor": {
"type": "string"
},
"customOverlayBackgroundColor": {
"type": "string"
},
"overlayTextColor": {
"type": "string"
},
"customOverlayTextColor": {
"type": "string"
}
},
"usesContext": [ "navigationArea" ],
"providesContext": {
"textColor": "textColor",
"customTextColor": "customTextColor",
"backgroundColor": "backgroundColor",
"customBackgroundColor": "customBackgroundColor",
"overlayTextColor": "overlayTextColor",
"customOverlayTextColor": "customOverlayTextColor",
"overlayBackgroundColor": "overlayBackgroundColor",
"customOverlayBackgroundColor": "customOverlayBackgroundColor",
"fontSize": "fontSize",
"customFontSize": "customFontSize",
"showSubmenuIcon": "showSubmenuIcon",
"openSubmenusOnClick": "openSubmenusOnClick",
"style": "style",
"orientation": "orientation"
},
"supports": {
"align": [
"wide",
"full"
],
"anchor": true,
"html": false,
"inserter": true,
"typography": {
"fontSize": true,
"lineHeight": true,
"__experimentalFontStyle": true,
"__experimentalFontWeight": true,
"__experimentalTextTransform": true,
"__experimentalFontFamily": true,
"__experimentalTextDecoration": true,
"__experimentalDefaultControls": {
"fontSize": true
}
},
"spacing": {
"blockGap": true,
"units": [
"px",
"em",
"rem",
"vh",
"vw"
],
"__experimentalDefaultControls": {
"blockGap": true
}
},
"__experimentalLayout": {
"allowSwitching": false,
"allowInheriting": false,
"default": {
"type": "flex"
}
}
},
"viewScript": "file:./view.min.js",
"editorStyle": "wp-block-navigation-editor",
"style": "wp-block-navigation"
}

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array(), 'version' => '86538493346805d860c94eb70dd1323d');

View File

@@ -0,0 +1 @@
<?php return array('dependencies' => array(), 'version' => '7b2c5174a07c417dc3db6f1d9a9c3f78');

View File

@@ -0,0 +1,44 @@
<?php
/**
* Server-side rendering of the `core/pattern` block.
*
* @package WordPress
*/
/**
* Registers the `core/pattern` block on the server.
*
* @return void
*/
function register_block_core_pattern() {
register_block_type_from_metadata(
__DIR__ . '/pattern',
array(
'render_callback' => 'render_block_core_pattern',
)
);
}
/**
* Renders the `core/pattern` block on the server.
*
* @param array $attributes Block attributes.
*
* @return string Returns the output of the pattern.
*/
function render_block_core_pattern( $attributes ) {
if ( empty( $attributes['slug'] ) ) {
return '';
}
$slug = $attributes['slug'];
$registry = WP_Block_Patterns_Registry::get_instance();
if ( ! $registry->is_registered( $slug ) ) {
return '';
}
$pattern = $registry->get_registered( $slug );
return do_blocks( $pattern['content'] );
}
add_action( 'init', 'register_block_core_pattern' );

View File

@@ -0,0 +1,17 @@
{
"apiVersion": 2,
"name": "core/pattern",
"title": "Pattern",
"category": "design",
"description": "Show a block pattern.",
"supports": {
"html": false,
"inserter": false
},
"textdomain": "default",
"attributes": {
"slug": {
"type": "string"
}
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* Server-side rendering of the `core/post-author` block.
*
* @package WordPress
*/
/**
* Renders the `core/post-author` block on the server.
*
* @param array $attributes Block attributes.
* @param string $content Block default content.
* @param WP_Block $block Block instance.
* @return string Returns the rendered author block.
*/
function render_block_core_post_author( $attributes, $content, $block ) {
if ( ! isset( $block->context['postId'] ) ) {
return '';
}
$author_id = get_post_field( 'post_author', $block->context['postId'] );
if ( empty( $author_id ) ) {
return '';
}
$avatar = ! empty( $attributes['avatarSize'] ) ? get_avatar(
$author_id,
$attributes['avatarSize']
) : null;
$byline = ! empty( $attributes['byline'] ) ? $attributes['byline'] : false;
$classes = array_merge(
isset( $attributes['className'] ) ? array( $attributes['className'] ) : array(),
isset( $attributes['itemsJustification'] ) ? array( 'items-justified-' . $attributes['itemsJustification'] ) : array(),
isset( $attributes['textAlign'] ) ? array( 'has-text-align-' . $attributes['textAlign'] ) : array()
);
$wrapper_attributes = get_block_wrapper_attributes( array( 'class' => implode( ' ', $classes ) ) );
return sprintf( '<div %1$s>', $wrapper_attributes ) .
( ! empty( $attributes['showAvatar'] ) ? '<div class="wp-block-post-author__avatar">' . $avatar . '</div>' : '' ) .
'<div class="wp-block-post-author__content">' .
( ! empty( $byline ) ? '<p class="wp-block-post-author__byline">' . $byline . '</p>' : '' ) .
'<p class="wp-block-post-author__name">' . get_the_author_meta( 'display_name', $author_id ) . '</p>' .
( ! empty( $attributes['showBio'] ) ? '<p class="wp-block-post-author__bio">' . get_the_author_meta( 'user_description', $author_id ) . '</p>' : '' ) .
'</div>' .
'</div>';
}
/**
* Registers the `core/post-author` block on the server.
*/
function register_block_core_post_author() {
register_block_type_from_metadata(
__DIR__ . '/post-author',
array(
'render_callback' => 'render_block_core_post_author',
)
);
}
add_action( 'init', 'register_block_core_post_author' );

View File

@@ -0,0 +1,53 @@
{
"apiVersion": 2,
"name": "core/post-author",
"title": "Post Author",
"category": "theme",
"description": "Add the author of this post.",
"textdomain": "default",
"attributes": {
"textAlign": {
"type": "string"
},
"avatarSize": {
"type": "number",
"default": 48
},
"showAvatar": {
"type": "boolean",
"default": true
},
"showBio": {
"type": "boolean"
},
"byline": {
"type": "string"
}
},
"usesContext": [ "postType", "postId", "queryId" ],
"supports": {
"html": false,
"spacing": {
"margin": true,
"padding": true
},
"typography": {
"fontSize": true,
"lineHeight": true,
"__experimentalFontStyle": true,
"__experimentalFontWeight": true,
"__experimentalLetterSpacing": true,
"__experimentalTextTransform": true,
"__experimentalDefaultControls": {
"fontSize": true
}
},
"color": {
"gradients": true,
"link": true,
"__experimentalDuotone": ".wp-block-post-author__avatar img"
}
},
"editorStyle": "wp-block-post-author-editor",
"style": "wp-block-post-author"
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* Server-side rendering of the `core/post-comments` block.
*
* @package WordPress
*/
/**
* Renders the `core/post-comments` block on the server.
*
* @param array $attributes Block attributes.
* @param string $content Block default content.
* @param WP_Block $block Block instance.
* @return string Returns the filtered post comments for the current post wrapped inside "p" tags.
*/
function render_block_core_post_comments( $attributes, $content, $block ) {
global $post;
$post_id = $block->context['postId'];
if ( ! isset( $post_id ) ) {
return '';
}
$comment_args = array(
'post_id' => $post_id,
'count' => true,
);
// Return early if there are no comments and comments are closed.
if ( ! comments_open( $post_id ) && get_comments( $comment_args ) === 0 ) {
return '';
}
$post_before = $post;
$post = get_post( $post_id );
setup_postdata( $post );
ob_start();
// There's a deprecation warning generated by WP Core.
// Ideally this deprecation is removed from Core.
// In the meantime, this removes it from the output.
add_filter( 'deprecated_file_trigger_error', '__return_false' );
comments_template();
remove_filter( 'deprecated_file_trigger_error', '__return_false' );
$post = $post_before;
$classes = '';
if ( isset( $attributes['textAlign'] ) ) {
$classes .= 'has-text-align-' . $attributes['textAlign'];
}
$wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $classes ) );
$output = ob_get_clean();
return sprintf( '<div %1$s>%2$s</div>', $wrapper_attributes, $output );
}
/**
* Registers the `core/post-comments` block on the server.
*/
function register_block_core_post_comments() {
register_block_type_from_metadata(
__DIR__ . '/post-comments',
array(
'render_callback' => 'render_block_core_post_comments',
)
);
}
add_action( 'init', 'register_block_core_post_comments' );

View File

@@ -0,0 +1,34 @@
{
"apiVersion": 2,
"name": "core/post-comments",
"title": "Post Comments",
"category": "theme",
"description": "Display a post's comments.",
"textdomain": "default",
"attributes": {
"textAlign": {
"type": "string"
}
},
"usesContext": [ "postId", "postType" ],
"supports": {
"html": false,
"align": [ "wide", "full" ],
"typography": {
"fontSize": true,
"lineHeight": true,
"__experimentalFontStyle": true,
"__experimentalFontWeight": true,
"__experimentalLetterSpacing": true,
"__experimentalTextTransform": true,
"__experimentalDefaultControls": {
"fontSize": true
}
},
"color": {
"gradients": true,
"link": true
}
},
"style": "wp-block-post-comments"
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* Server-side rendering of the `core/post-navigation-link` block.
*
* @package WordPress
*/
/**
* Renders the `core/post-navigation-link` block on the server.
*
* @param array $attributes Block attributes.
* @param string $content Block default content.
*
* @return string Returns the next or previous post link that is adjacent to the current post.
*/
function render_block_core_post_navigation_link( $attributes, $content ) {
if ( ! is_singular() ) {
return '';
}
// Get the nagigation type to show the proper link. Available options are `next|previous`.
$navigation_type = isset( $attributes['type'] ) ? $attributes['type'] : 'next';
// Allow only `next` and `previous` in `$navigation_type`.
if ( ! in_array( $navigation_type, array( 'next', 'previous' ), true ) ) {
return '';
}
$classes = "post-navigation-link-$navigation_type";
if ( isset( $attributes['textAlign'] ) ) {
$classes .= " has-text-align-{$attributes['textAlign']}";
}
$wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $classes ) );
// Set default values.
$format = '%link';
$link = 'next' === $navigation_type ? _x( 'Next', 'label for next post link' ) : _x( 'Previous', 'label for previous post link' );
$label = '';
// If a custom label is provided, make this a link.
// `$label` is used to prepend the provided label, if we want to show the page title as well.
if ( isset( $attributes['label'] ) && ! empty( $attributes['label'] ) ) {
$label = "{$attributes['label']}";
$link = $label;
}
// If we want to also show the page title, make the page title a link and prepend the label.
if ( isset( $attributes['showTitle'] ) && $attributes['showTitle'] ) {
/*
* If the label link option is not enabled but there is a custom label,
* display the custom label as text before the linked title.
*/
if ( ! $attributes['linkLabel'] ) {
if ( $label ) {
$format = '<span class="post-navigation-link__label">' . $label . '</span> %link';
}
$link = '%title';
} elseif ( isset( $attributes['linkLabel'] ) && $attributes['linkLabel'] ) {
// If the label link option is enabled and there is a custom label, display it before the title.
if ( $label ) {
$link = '<span class="post-navigation-link__label">' . $label . '</span> <span class="post-navigation-link__title">%title</title>';
} else {
/*
* If the label link option is enabled and there is no custom label,
* add a colon between the label and the post title.
*/
$label = 'next' === $navigation_type ? _x( 'Next:', 'label before the title of the next post' ) : _x( 'Previous:', 'label before the title of the previous post' );
$link = sprintf(
'<span class="post-navigation-link__label">%1$s</span> <span class="post-navigation-link__title">%2$s</span>',
$label,
'%title'
);
}
}
}
// The dynamic portion of the function name, `$navigation_type`,
// refers to the type of adjacency, 'next' or 'previous'.
$get_link_function = "get_{$navigation_type}_post_link";
$content = $get_link_function( $format, $link );
return sprintf(
'<div %1$s>%2$s</div>',
$wrapper_attributes,
$content
);
}
/**
* Registers the `core/post-navigation-link` block on the server.
*/
function register_block_core_post_navigation_link() {
register_block_type_from_metadata(
__DIR__ . '/post-navigation-link',
array(
'render_callback' => 'render_block_core_post_navigation_link',
)
);
}
add_action( 'init', 'register_block_core_post_navigation_link' );

View File

@@ -0,0 +1,43 @@
{
"apiVersion": 2,
"name": "core/post-navigation-link",
"title": "Post Navigation Link",
"category": "theme",
"description": "Displays the next or previous post link that is adjacent to the current post.",
"textdomain": "default",
"attributes": {
"textAlign": {
"type": "string"
},
"type": {
"type": "string",
"default": "next"
},
"label": {
"type": "string"
},
"showTitle": {
"type": "boolean",
"default": false
},
"linkLabel": {
"type": "boolean",
"default": false
}
},
"supports": {
"reusable": false,
"html": false,
"typography": {
"fontSize": true,
"lineHeight": true,
"__experimentalFontStyle": true,
"__experimentalFontWeight": true,
"__experimentalLetterSpacing": true,
"__experimentalTextTransform": true,
"__experimentalDefaultControls": {
"fontSize": true
}
}
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Server-side rendering of the `core/term-description` block.
*
* @package WordPress
*/
/**
* Renders the `core/term-description` block on the server.
*
* @param array $attributes Block attributes.
*
* @return string Returns the description of the current taxonomy term, if available
*/
function render_block_core_term_description( $attributes ) {
$term_description = '';
if ( is_category() || is_tag() || is_tax() ) {
$term_description = term_description();
}
if ( empty( $term_description ) ) {
return '';
}
$extra_attributes = ( isset( $attributes['textAlign'] ) )
? array( 'class' => 'has-text-align-' . $attributes['textAlign'] )
: array();
$wrapper_attributes = get_block_wrapper_attributes( $extra_attributes );
return '<div ' . $wrapper_attributes . '>' . $term_description . '</div>';
}
/**
* Registers the `core/term-description` block on the server.
*/
function register_block_core_term_description() {
register_block_type_from_metadata(
__DIR__ . '/term-description',
array(
'render_callback' => 'render_block_core_term_description',
)
);
}
add_action( 'init', 'register_block_core_term_description' );

View File

@@ -0,0 +1,28 @@
{
"apiVersion": 2,
"name": "core/term-description",
"title": "Term Description",
"category": "theme",
"description": "Display the description of categories, tags and custom taxonomies when viewing an archive.",
"textdomain": "default",
"attributes": {
"textAlign": {
"type": "string"
}
},
"supports": {
"align": [ "wide", "full" ],
"html": false,
"color": {
"link": true
},
"typography": {
"fontSize": true,
"lineHeight": true,
"__experimentalDefaultControls": {
"fontSize": true
}
}
},
"editorStyle": "wp-block-term-description-editor"
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* Server-side rendering of the `core/widget-group` block.
*
* @package WordPress
*/
/**
* Renders the 'core/widget-group' block.
*
* @param array $attributes The block attributes.
* @param string $content The block content.
* @param WP_Block $block The block.
*
* @return string Rendered block.
*/
function render_block_core_widget_group( $attributes, $content, $block ) {
global $wp_registered_sidebars, $_sidebar_being_rendered;
if ( isset( $wp_registered_sidebars[ $_sidebar_being_rendered ] ) ) {
$before_title = $wp_registered_sidebars[ $_sidebar_being_rendered ]['before_title'];
$after_title = $wp_registered_sidebars[ $_sidebar_being_rendered ]['after_title'];
} else {
$before_title = '<h2 class="widget-title">';
$after_title = '</h2>';
}
$html = '';
if ( ! empty( $attributes['title'] ) ) {
$html .= $before_title . $attributes['title'] . $after_title;
}
$html .= '<div class="wp-widget-group__inner-blocks">';
foreach ( $block->inner_blocks as $inner_block ) {
$html .= $inner_block->render();
}
$html .= '</div>';
return $html;
}
/**
* Registers the 'core/widget-group' block.
*/
function register_block_core_widget_group() {
register_block_type_from_metadata(
__DIR__ . '/widget-group',
array(
'render_callback' => 'render_block_core_widget_group',
)
);
}
add_action( 'init', 'register_block_core_widget_group' );
/**
* Make a note of the sidebar being rendered before WordPress starts rendering
* it. This lets us get to the current sidebar in
* render_block_core_widget_group().
*
* @param int|string $index Index, name, or ID of the dynamic sidebar.
*/
function note_sidebar_being_rendered( $index ) {
global $_sidebar_being_rendered;
$_sidebar_being_rendered = $index;
}
add_action( 'dynamic_sidebar_before', 'note_sidebar_being_rendered' );
/**
* Clear whatever we set in note_sidebar_being_rendered() after WordPress
* finishes rendering a sidebar.
*/
function discard_sidebar_being_rendered() {
global $_sidebar_being_rendered;
unset( $_sidebar_being_rendered );
}
add_action( 'dynamic_sidebar_after', 'discard_sidebar_being_rendered' );

View File

@@ -0,0 +1,18 @@
{
"apiVersion": 2,
"name": "core/widget-group",
"category": "widgets",
"attributes": {
"title": {
"type": "string"
}
},
"supports": {
"html": false,
"inserter": true,
"customClassName": true,
"reusable": false
},
"editorStyle": "wp-block-widget-group-editor",
"style": "wp-block-widget-group"
}

View File

@@ -581,6 +581,7 @@ add_action( 'wp_footer', 'wp_maybe_inline_styles', 1 ); // Run for late-loaded s
add_action( 'admin_footer-post.php', 'wp_add_iframed_editor_assets_html' );
add_action( 'admin_footer-post-new.php', 'wp_add_iframed_editor_assets_html' );
add_action( 'admin_footer-widgets.php', 'wp_add_iframed_editor_assets_html' );
// Taxonomy.
add_action( 'init', 'create_initial_taxonomies', 0 ); // Highest priority.

View File

@@ -2287,6 +2287,8 @@ function safecss_filter_attr( $css, $deprecated = '' ) {
'border-right-width',
'border-bottom',
'border-bottom-color',
'border-bottom-left-radius',
'border-bottom-right-radius',
'border-bottom-style',
'border-bottom-width',
'border-bottom-right-radius',
@@ -2297,6 +2299,8 @@ function safecss_filter_attr( $css, $deprecated = '' ) {
'border-left-width',
'border-top',
'border-top-color',
'border-top-left-radius',
'border-top-right-radius',
'border-top-style',
'border-top-width',
'border-top-left-radius',

View File

@@ -450,6 +450,51 @@ function create_initial_post_types() {
)
);
register_post_type(
'wp_navigation',
array(
'labels' => array(
'name' => __( 'Navigation Menus', 'gutenberg' ),
'singular_name' => __( 'Navigation Menu', 'gutenberg' ),
'menu_name' => _x( 'Navigation Menus', 'Admin Menu text', 'gutenberg' ),
'add_new' => _x( 'Add New', 'Navigation Menu', 'gutenberg' ),
'add_new_item' => __( 'Add New Navigation Menu', 'gutenberg' ),
'new_item' => __( 'New Navigation Menu', 'gutenberg' ),
'edit_item' => __( 'Edit Navigation Menu', 'gutenberg' ),
'view_item' => __( 'View Navigation Menu', 'gutenberg' ),
'all_items' => __( 'All Navigation Menus', 'gutenberg' ),
'search_items' => __( 'Search Navigation Menus', 'gutenberg' ),
'parent_item_colon' => __( 'Parent Navigation Menu:', 'gutenberg' ),
'not_found' => __( 'No Navigation Menu found.', 'gutenberg' ),
'not_found_in_trash' => __( 'No Navigation Menu found in Trash.', 'gutenberg' ),
'archives' => __( 'Navigation Menu archives', 'gutenberg' ),
'insert_into_item' => __( 'Insert into Navigation Menu', 'gutenberg' ),
'uploaded_to_this_item' => __( 'Uploaded to this Navigation Menu', 'gutenberg' ),
// Some of these are a bit weird, what are they for?
'filter_items_list' => __( 'Filter Navigation Menu list', 'gutenberg' ),
'items_list_navigation' => __( 'Navigation Menus list navigation', 'gutenberg' ),
'items_list' => __( 'Navigation Menus list', 'gutenberg' ),
),
'description' => __( 'Navigation menus.', 'gutenberg' ),
'public' => false,
'_builtin' => true, /* internal use only. don't use this when registering your own post type. */
'has_archive' => false,
'show_ui' => false,
'show_in_menu' => 'themes.php',
'show_in_admin_bar' => false,
'show_in_rest' => true,
'rewrite' => false,
'map_meta_cap' => true,
'rest_base' => 'navigation',
'rest_controller_class' => 'WP_REST_Posts_Controller',
'supports' => array(
'title',
'editor',
'revisions',
),
)
);
register_post_status(
'publish',
array(

View File

@@ -311,6 +311,12 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
$prepared_args['has_published_posts'] = get_post_types( array( 'show_in_rest' => true ), 'names' );
}
if ( ! empty( $request['has_published_posts'] ) ) {
$prepared_args['has_published_posts'] = ( true === $request['has_published_posts'] )
? get_post_types( array( 'show_in_rest' => true ), 'names' )
: (array) $request['has_published_posts'];
}
if ( ! empty( $prepared_args['search'] ) ) {
$prepared_args['search'] = '*' . $prepared_args['search'] . '*';
}
@@ -1580,6 +1586,15 @@ class WP_REST_Users_Controller extends WP_REST_Controller {
),
);
$query_params['has_published_posts'] = array(
'description' => __( 'Limit result set to users who have published posts.' ),
'type' => array( 'boolean', 'array' ),
'items' => array(
'type' => 'string',
'enum' => get_post_types( array( 'show_in_rest' => true ), 'names' ),
),
);
/**
* Filters REST API collection parameters for the users controller.
*

View File

@@ -99,6 +99,29 @@ class WP_REST_Widget_Types_Controller extends WP_REST_Controller {
),
)
);
register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<id>[a-zA-Z0-9_-]+)/render',
array(
array(
'methods' => WP_REST_Server::CREATABLE,
'permission_callback' => array( $this, 'get_item_permissions_check' ),
'callback' => array( $this, 'render' ),
'args' => array(
'id' => array(
'description' => __( 'The widget type id.', 'default' ),
'type' => 'string',
'required' => true,
),
'instance' => array(
'description' => __( 'Current instance settings of the widget.', 'default' ),
'type' => 'object',
),
),
),
)
);
}
/**
@@ -559,6 +582,78 @@ class WP_REST_Widget_Types_Controller extends WP_REST_Controller {
return ob_get_clean();
}
/**
* Renders a single Legacy Widget and wraps it in a JSON-encodable array.
*
* @since 5.9.0
*
* @param WP_REST_Request $request Full details about the request.
*
* @return array An array with rendered Legacy Widget HTML.
*/
public function render( $request ) {
return array(
'preview' => $this->render_legacy_widget_preview_iframe(
$request['id'],
isset( $request['instance'] ) ? $request['instance'] : null
),
);
}
/**
* Renders a page containing a preview of the requested Legacy Widget block.
*
* @since 5.9.0
*
* @param string $id_base The id base of the requested widget.
* @param array $instance The widget instance attributes.
*
* @return string Rendered Legacy Widget block preview.
*/
private function render_legacy_widget_preview_iframe( $id_base, $instance ) {
if ( ! defined( 'IFRAME_REQUEST' ) ) {
define( 'IFRAME_REQUEST', true );
}
ob_start();
?>
<!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="profile" href="https://gmpg.org/xfn/11" />
<?php wp_head(); ?>
<style>
/* Reset theme styles */
html, body, #page, #content {
padding: 0 !important;
margin: 0 !important;
}
</style>
</head>
<body <?php body_class(); ?>>
<div id="page" class="site">
<div id="content" class="site-content">
<?php
$registry = WP_Block_Type_Registry::get_instance();
$block = $registry->get_registered( 'core/legacy-widget' );
echo $block->render(
array(
'idBase' => $id_base,
'instance' => $instance,
)
);
?>
</div><!-- #content -->
</div><!-- #page -->
<?php wp_footer(); ?>
</body>
</html>
<?php
return ob_get_clean();
}
/**
* Retrieves the query params for collections.
*

View File

@@ -103,8 +103,8 @@ function wp_default_packages_vendor( $scripts ) {
);
$vendor_scripts_versions = array(
'react' => '16.13.1',
'react-dom' => '16.13.1',
'react' => '17.0.1',
'react-dom' => '17.0.1',
'regenerator-runtime' => '0.13.7',
'moment' => '2.29.1',
'lodash' => '4.17.19',
@@ -1598,6 +1598,11 @@ function wp_default_styles( $styles ) {
'wp-block-library',
'wp-reusable-blocks',
),
'edit-site' => array(
'wp-components',
'wp-block-editor',
'wp-edit-blocks',
),
);
foreach ( $package_styles as $package => $dependencies ) {
@@ -1655,6 +1660,7 @@ function wp_default_styles( $styles ) {
'wp-components',
'wp-customize-widgets',
'wp-edit-post',
'wp-edit-site',
'wp-edit-widgets',
'wp-editor',
'wp-format-library',
@@ -2265,7 +2271,19 @@ function wp_common_block_scripts_and_styles() {
wp_enqueue_style( 'wp-block-library' );
if ( current_theme_supports( 'wp-block-styles' ) ) {
wp_enqueue_style( 'wp-block-library-theme' );
if ( wp_should_load_separate_core_block_assets() ) {
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? 'css' : 'min.css';
$files = glob( __DIR__ . "/blocks/**/theme.$suffix" );
foreach ( $files as $path ) {
$block_name = basename( dirname( $path ) );
if ( is_rtl() && file_exists( __DIR__ . "/blocks/$block_name/theme-rtl.$suffix" ) ) {
$path = __DIR__ . "/blocks/$block_name/theme-rtl.$suffix";
}
wp_add_inline_style( "wp-block-{$block_name}", file_get_contents( $path ) );
}
} else {
wp_enqueue_style( 'wp-block-library-theme' );
}
}
/**
@@ -2773,6 +2791,8 @@ function _wp_normalize_relative_css_links( $css, $stylesheet_url ) {
* @since 5.8.0
*/
function wp_add_iframed_editor_assets_html() {
global $pagenow;
if ( ! wp_should_load_block_editor_scripts_and_styles() ) {
return;
}
@@ -2785,6 +2805,11 @@ function wp_add_iframed_editor_assets_html() {
'wp-edit-blocks',
);
if ( 'widgets.php' === $pagenow || 'customize.php' === $pagenow ) {
$style_handles[] = 'wp-widgets';
$style_handles[] = 'wp-edit-widgets';
}
$block_registry = WP_Block_Type_Registry::get_instance();
foreach ( $block_registry->get_all_registered() as $block_type ) {
@@ -2806,7 +2831,8 @@ function wp_add_iframed_editor_assets_html() {
ob_start();
wp_styles()->done = array();
// We do not need reset styles for the iframed editor.
wp_styles()->done = array( 'wp-reset-editor-styles' );
wp_styles()->do_items( $style_handles );
wp_styles()->done = $done;

View File

@@ -10,8 +10,7 @@
* This needs to run before <head> so that blocks can add scripts and styles in wp_head().
*/
$template_html = get_the_block_template_html();
?>
<!DOCTYPE html>
?><!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>" />

View File

@@ -4075,3 +4075,14 @@ function create_initial_theme_features() {
)
);
}
/**
* Returns whether the current theme is a block-based theme or not.
*
* @since 5.9.0
*
* @return boolean Whether the current theme is a block-based theme or not.
*/
function wp_is_block_template_theme() {
return is_readable( get_theme_file_path( '/block-templates/index.html' ) );
}

View File

@@ -317,6 +317,7 @@ require ABSPATH . WPINC . '/block-supports/align.php';
require ABSPATH . WPINC . '/block-supports/border.php';
require ABSPATH . WPINC . '/block-supports/colors.php';
require ABSPATH . WPINC . '/block-supports/custom-classname.php';
require ABSPATH . WPINC . '/block-supports/dimensions.php';
require ABSPATH . WPINC . '/block-supports/duotone.php';
require ABSPATH . WPINC . '/block-supports/elements.php';
require ABSPATH . WPINC . '/block-supports/generated-classname.php';

View File

@@ -307,20 +307,27 @@ function _unhook_block_registration() {
remove_action( 'init', 'register_block_core_calendar' );
remove_action( 'init', 'register_block_core_categories' );
remove_action( 'init', 'register_block_core_file' );
remove_action( 'init', 'register_block_core_loginout' );
remove_action( 'init', 'register_block_core_latest_comments' );
remove_action( 'init', 'register_block_core_latest_posts' );
remove_action( 'init', 'register_block_core_legacy_widget' );
remove_action( 'init', 'register_block_core_loginout' );
remove_action( 'init', 'register_block_core_navigation' );
remove_action( 'init', 'register_block_core_navigation_area' );
remove_action( 'init', 'register_block_core_navigation_link' );
remove_action( 'init', 'register_block_core_navigation_submenu' );
remove_action( 'init', 'register_block_core_page_list' );
remove_action( 'init', 'register_block_core_pattern' );
remove_action( 'init', 'register_block_core_post_author' );
remove_action( 'init', 'register_block_core_post_comments' );
remove_action( 'init', 'register_block_core_post_content' );
remove_action( 'init', 'register_block_core_post_date' );
remove_action( 'init', 'register_block_core_post_excerpt' );
remove_action( 'init', 'register_block_core_post_featured_image' );
remove_action( 'init', 'register_block_core_post_navigation_link' );
remove_action( 'init', 'register_block_core_post_template' );
remove_action( 'init', 'register_block_core_post_terms' );
remove_action( 'init', 'register_block_core_post_title' );
remove_action( 'init', 'register_block_core_query' );
remove_action( 'init', 'register_block_core_post_template' );
remove_action( 'init', 'gutenberg_register_legacy_query_loop_block' );
remove_action( 'init', 'register_block_core_query_pagination' );
remove_action( 'init', 'register_block_core_query_pagination_next' );
remove_action( 'init', 'register_block_core_query_pagination_numbers' );
@@ -329,14 +336,14 @@ function _unhook_block_registration() {
remove_action( 'init', 'register_block_core_rss' );
remove_action( 'init', 'register_block_core_search' );
remove_action( 'init', 'register_block_core_shortcode' );
remove_action( 'init', 'register_block_core_site_tagline' );
remove_action( 'init', 'register_block_core_site_logo' );
remove_action( 'init', 'register_block_core_site_tagline' );
remove_action( 'init', 'register_block_core_site_title' );
remove_action( 'init', 'register_block_core_social_link' );
remove_action( 'init', 'register_block_core_social_link' );
remove_action( 'init', 'register_block_core_tag_cloud' );
remove_action( 'init', 'register_core_block_types_from_metadata' );
remove_action( 'init', 'register_block_core_legacy_widget' );
remove_action( 'init', 'register_block_core_template_part' );
remove_action( 'init', 'register_block_core_term_description' );
remove_action( 'init', 'register_core_block_types_from_metadata' );
}
tests_add_filter( 'init', '_unhook_block_registration', 1000 );

View File

@@ -0,0 +1,47 @@
<?php
/**
* @group block-supports
*/
class WP_Block_Supports_Colors_Test extends WP_UnitTestCase {
function test_color_slugs_with_numbers_are_kebab_cased_properly() {
register_block_type(
'test/color-slug-with-numbers',
array(
'api_version' => 2,
'attributes' => array(
'textColor' => array(
'type' => 'string',
),
'backgroundColor' => array(
'type' => 'string',
),
'gradient' => array(
'type' => 'string',
),
),
'supports' => array(
'color' => array(
'text' => true,
'background' => true,
'gradients' => true,
),
),
)
);
$registry = WP_Block_Type_Registry::get_instance();
$block_type = $registry->get_registered( 'test/color-slug-with-numbers' );
$block_atts = array(
'textColor' => 'fg1',
'backgroundColor' => 'bg2',
'gradient' => 'gr3',
);
$actual = wp_apply_colors_support( $block_type, $block_atts );
$expected = array( 'class' => 'has-text-color has-fg-1-color has-background has-bg-2-background-color has-background has-gr-3-gradient-background' );
$this->assertSame( $expected, $actual );
unregister_block_type( 'test/color-slug-with-numbers' );
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* @group block-supports
*/
class WP_Block_Supports_Elements_Test extends WP_UnitTestCase {
/**
* Given a string containing a class prefixed by "wp-elements-" followed by a unique id,
* this function returns a string where the id is one instead of being unique.
*
* @param string $string String containing unique id classes.
* @return string String where the unique id classes were replaced with "wp-elements-1".
*/
private static function make_unique_id_one( $string ) {
return preg_replace( '/wp-elements-.{13}/', 'wp-elements-1', $string );
}
/**
* Test wp_render_elements_support() with a simple paragraph and link color preset.
*/
public function test_simple_paragraph_link_color() {
$result = self::make_unique_id_one(
wp_render_elements_support(
'<p>Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>',
array(
'blockName' => 'core/paragraph',
'attrs' => array(
'style' => array(
'elements' => array(
'link' => array(
'color' => array(
'text' => 'var:preset|color|subtle-background',
),
),
),
),
),
)
)
);
$this->assertSame(
$result,
'<p class="wp-elements-1">Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>'
);
}
/**
* Test wp_render_elements_support() with a paragraph containing a class.
*/
public function test_class_paragraph_link_color() {
$result = self::make_unique_id_one(
wp_render_elements_support(
'<p class="has-dark-gray-background-color has-background">Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>',
array(
'blockName' => 'core/paragraph',
'attrs' => array(
'style' => array(
'elements' => array(
'link' => array(
'color' => array(
'text' => 'red',
),
),
),
),
'backgroundColor' => 'dark-gray',
),
)
)
);
$this->assertSame(
$result,
'<p class="wp-elements-1 has-dark-gray-background-color has-background">Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>'
);
}
/**
* Test wp_render_elements_support() with a paragraph containing a anchor.
*/
public function test_anchor_paragraph_link_color() {
$result = self::make_unique_id_one(
wp_render_elements_support(
'<p id="anchor">Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>',
array(
'blockName' => 'core/paragraph',
'attrs' => array(
'style' => array(
'elements' => array(
'link' => array(
'color' => array(
'text' => '#fff000',
),
),
),
),
),
)
)
);
$this->assertSame(
$result,
'<p id="anchor" class="wp-elements-1">Hello <a href="http://www.wordpress.org/">WordPress</a>!</p>'
);
}
}

View File

@@ -0,0 +1,138 @@
<?php
/**
* @group block-supports
*/
class WP_Block_Supports_Spacing_Test extends WP_UnitTestCase {
private $sample_block_content = '<div class="wp-block-test-block">Test</div>';
private $test_gap_block_value = array();
private $test_gap_block_args = array();
function set_up() {
parent::set_up();
$this->test_gap_block_value = array(
'blockName' => 'test/test-block',
'attrs' => array(
'style' => array(
'spacing' => array(
'blockGap' => '3em',
),
),
),
);
$this->test_gap_block_args = array(
'api_version' => 2,
'supports' => array(
'spacing' => array(
'blockGap' => true,
),
),
);
}
function tear_down() {
unregister_block_type( 'test/test-block' );
parent::tear_down();
}
function test_spacing_gap_block_support_renders_block_inline_style() {
register_block_type( 'test/test-block', $this->test_gap_block_args );
$render_output = wp_render_spacing_gap_support(
$this->sample_block_content,
$this->test_gap_block_value
);
$this->assertSame(
'<div style="--wp--style--block-gap: 3em" class="wp-block-test-block">Test</div>',
$render_output
);
}
function test_spacing_gap_block_support_renders_block_inline_style_with_inner_tag() {
register_block_type( 'test/test-block', $this->test_gap_block_args );
$render_output = wp_render_spacing_gap_support(
'<div class="wp-test-block"><p style="color: red;">Test</p></div>',
$this->test_gap_block_value
);
$this->assertSame(
'<div style="--wp--style--block-gap: 3em" class="wp-test-block"><p style="color: red;">Test</p></div>',
$render_output
);
}
function test_spacing_gap_block_support_renders_block_inline_style_with_no_other_attributes() {
register_block_type( 'test/test-block', $this->test_gap_block_args );
$render_output = wp_render_spacing_gap_support(
'<div><p>Test</p></div>',
$this->test_gap_block_value
);
$this->assertSame(
'<div style="--wp--style--block-gap: 3em"><p>Test</p></div>',
$render_output
);
}
function test_spacing_gap_block_support_renders_appended_block_inline_style() {
register_block_type( 'test/test-block', $this->test_gap_block_args );
$render_output = wp_render_spacing_gap_support(
'<div class="wp-test-block" style="background: green;"><p style="color: red;">Test</p></div>',
$this->test_gap_block_value
);
$this->assertSame(
'<div class="wp-test-block" style="--wp--style--block-gap: 3em; background: green;"><p style="color: red;">Test</p></div>',
$render_output
);
}
function test_spacing_gap_block_support_does_not_render_style_when_support_is_false() {
$this->test_gap_block_args['supports']['spacing']['blockGap'] = false;
register_block_type( 'test/test-block', $this->test_gap_block_args );
$render_output = wp_render_spacing_gap_support(
$this->sample_block_content,
$this->test_gap_block_value
);
$this->assertEquals(
$this->sample_block_content,
$render_output
);
}
function test_spacing_gap_block_support_does_not_render_style_when_gap_is_null() {
$this->test_gap_block_value['attrs']['style']['spacing']['blockGap'] = null;
$this->test_gap_block_args['supports']['spacing']['blockGap'] = true;
register_block_type( 'test/test-block', $this->test_gap_block_args );
$render_output = wp_render_spacing_gap_support(
$this->sample_block_content,
$this->test_gap_block_value
);
$this->assertEquals(
$this->sample_block_content,
$render_output
);
}
function test_spacing_gap_block_support_does_not_render_style_when_gap_is_illegal_value() {
$this->test_gap_block_value['attrs']['style']['spacing']['blockGap'] = '" javascript="alert("hello");';
$this->test_gap_block_args['supports']['spacing']['blockGap'] = true;
register_block_type( 'test/test-block', $this->test_gap_block_args );
$render_output = wp_render_spacing_gap_support(
$this->sample_block_content,
$this->test_gap_block_value
);
$this->assertEquals(
$this->sample_block_content,
$render_output
);
}
}

View File

@@ -0,0 +1,123 @@
<?php
/**
* @group block-supports
*/
class WP_Block_Supports_Typography_Test extends WP_UnitTestCase {
function test_font_size_slug_with_numbers_is_kebab_cased_properly() {
register_block_type(
'test/font-size-slug-with-numbers',
array(
'api_version' => 2,
'attributes' => array(
'fontSize' => array(
'type' => 'string',
),
),
'supports' => array(
'typography' => array(
'fontSize' => true,
),
),
)
);
$registry = WP_Block_Type_Registry::get_instance();
$block_type = $registry->get_registered( 'test/font-size-slug-with-numbers' );
$block_atts = array( 'fontSize' => 'h1' );
$actual = wp_apply_typography_support( $block_type, $block_atts );
$expected = array( 'class' => 'has-h-1-font-size' );
$this->assertSame( $expected, $actual );
unregister_block_type( 'test/font-size-slug-with-numbers' );
}
function test_font_family_with_legacy_inline_styles_using_a_value() {
$block_name = 'test/font-family-with-inline-styles-using-value';
register_block_type(
$block_name,
array(
'api_version' => 2,
'attributes' => array(
'style' => array(
'type' => 'object',
),
),
'supports' => array(
'typography' => array(
'__experimentalFontFamily' => true,
),
),
)
);
$registry = WP_Block_Type_Registry::get_instance();
$block_type = $registry->get_registered( $block_name );
$block_atts = array( 'style' => array( 'typography' => array( 'fontFamily' => 'serif' ) ) );
$actual = wp_apply_typography_support( $block_type, $block_atts );
$expected = array( 'style' => 'font-family: serif;' );
$this->assertSame( $expected, $actual );
unregister_block_type( $block_name );
}
function test_font_family_with_legacy_inline_styles_using_a_css_var() {
$block_name = 'test/font-family-with-inline-styles-using-css-var';
register_block_type(
$block_name,
array(
'api_version' => 2,
'attributes' => array(
'style' => array(
'type' => 'object',
),
),
'supports' => array(
'typography' => array(
'__experimentalFontFamily' => true,
),
),
)
);
$registry = WP_Block_Type_Registry::get_instance();
$block_type = $registry->get_registered( $block_name );
$block_atts = array( 'style' => array( 'typography' => array( 'fontFamily' => 'var:preset|font-family|h1' ) ) );
$actual = wp_apply_typography_support( $block_type, $block_atts );
$expected = array( 'style' => 'font-family: var(--wp--preset--font-family--h-1);' );
$this->assertSame( $expected, $actual );
unregister_block_type( $block_name );
}
function test_font_family_with_class() {
$block_name = 'test/font-family-with-class';
register_block_type(
$block_name,
array(
'api_version' => 2,
'attributes' => array(
'style' => array(
'type' => 'object',
),
),
'supports' => array(
'typography' => array(
'__experimentalFontFamily' => true,
),
),
)
);
$registry = WP_Block_Type_Registry::get_instance();
$block_type = $registry->get_registered( $block_name );
$block_atts = array( 'fontFamily' => 'h1' );
$actual = wp_apply_typography_support( $block_type, $block_atts );
$expected = array( 'class' => 'has-h-1-font-family' );
$this->assertSame( $expected, $actual );
unregister_block_type( $block_name );
}
}

View File

@@ -75,7 +75,7 @@ class Block_Template_Utils_Test extends WP_UnitTestCase {
wp_delete_post( self::$post->ID );
}
public function test_build_template_result_from_post() {
public function test_build_block_template_result_from_post() {
$template = _build_block_template_result_from_post(
self::$post,
'wp_template'

View File

@@ -154,8 +154,15 @@ class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase {
'/wp/v2/widget-types',
'/wp/v2/widget-types/(?P<id>[a-zA-Z0-9_-]+)',
'/wp/v2/widget-types/(?P<id>[a-zA-Z0-9_-]+)/encode',
'/wp/v2/widget-types/(?P<id>[a-zA-Z0-9_-]+)/render',
'/wp/v2/widgets',
'/wp/v2/widgets/(?P<id>[\w\-]+)',
'/wp/v2/navigation',
'/wp/v2/navigation/(?P<id>[\d]+)',
'/wp/v2/navigation/(?P<id>[\d]+)/autosaves',
'/wp/v2/navigation/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)',
'/wp/v2/navigation/(?P<parent>[\d]+)/revisions',
'/wp/v2/navigation/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)',
'/wp-site-health/v1',
'/wp-site-health/v1/tests/background-updates',
'/wp-site-health/v1/tests/loopback-requests',

View File

@@ -207,6 +207,7 @@ class WP_Test_REST_Users_Controller extends WP_Test_REST_Controller_Testcase {
'search',
'slug',
'who',
'has_published_posts',
),
$keys
);

View File

@@ -5115,6 +5115,814 @@ mockedApiResponse.Schema = {
}
]
},
"/wp/v2/navigation": {
"namespace": "wp/v2",
"methods": [
"GET",
"POST"
],
"endpoints": [
{
"methods": [
"GET"
],
"args": {
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
"type": "string",
"enum": [
"view",
"embed",
"edit"
],
"default": "view",
"required": false
},
"page": {
"description": "Current page of the collection.",
"type": "integer",
"default": 1,
"minimum": 1,
"required": false
},
"per_page": {
"description": "Maximum number of items to be returned in result set.",
"type": "integer",
"default": 10,
"minimum": 1,
"maximum": 100,
"required": false
},
"search": {
"description": "Limit results to those matching a string.",
"type": "string",
"required": false
},
"after": {
"description": "Limit response to posts published after a given ISO8601 compliant date.",
"type": "string",
"format": "date-time",
"required": false
},
"modified_after": {
"description": "Limit response to posts modified after a given ISO8601 compliant date.",
"type": "string",
"format": "date-time",
"required": false
},
"before": {
"description": "Limit response to posts published before a given ISO8601 compliant date.",
"type": "string",
"format": "date-time",
"required": false
},
"modified_before": {
"description": "Limit response to posts modified before a given ISO8601 compliant date.",
"type": "string",
"format": "date-time",
"required": false
},
"exclude": {
"description": "Ensure result set excludes specific IDs.",
"type": "array",
"items": {
"type": "integer"
},
"default": [],
"required": false
},
"include": {
"description": "Limit result set to specific IDs.",
"type": "array",
"items": {
"type": "integer"
},
"default": [],
"required": false
},
"offset": {
"description": "Offset the result set by a specific number of items.",
"type": "integer",
"required": false
},
"order": {
"description": "Order sort attribute ascending or descending.",
"type": "string",
"default": "desc",
"enum": [
"asc",
"desc"
],
"required": false
},
"orderby": {
"description": "Sort collection by post attribute.",
"type": "string",
"default": "date",
"enum": [
"author",
"date",
"id",
"include",
"modified",
"parent",
"relevance",
"slug",
"include_slugs",
"title"
],
"required": false
},
"slug": {
"description": "Limit result set to posts with one or more specific slugs.",
"type": "array",
"items": {
"type": "string"
},
"required": false
},
"status": {
"default": "publish",
"description": "Limit result set to posts assigned one or more statuses.",
"type": "array",
"items": {
"enum": [
"publish",
"future",
"draft",
"pending",
"private",
"trash",
"auto-draft",
"inherit",
"request-pending",
"request-confirmed",
"request-failed",
"request-completed",
"any"
],
"type": "string"
},
"required": false
}
}
},
{
"methods": [
"POST"
],
"args": {
"date": {
"description": "The date the post was published, in the site's timezone.",
"type": [
"string",
"null"
],
"format": "date-time",
"required": false
},
"date_gmt": {
"description": "The date the post was published, as GMT.",
"type": [
"string",
"null"
],
"format": "date-time",
"required": false
},
"slug": {
"description": "An alphanumeric identifier for the post unique to its type.",
"type": "string",
"required": false
},
"status": {
"description": "A named status for the post.",
"type": "string",
"enum": [
"publish",
"future",
"draft",
"pending",
"private"
],
"required": false
},
"password": {
"description": "A password to protect access to the content and excerpt.",
"type": "string",
"required": false
},
"title": {
"description": "The title for the post.",
"type": "object",
"properties": {
"raw": {
"description": "Title for the post, as it exists in the database.",
"type": "string",
"context": [
"edit"
]
},
"rendered": {
"description": "HTML title for the post, transformed for display.",
"type": "string",
"context": [
"view",
"edit",
"embed"
],
"readonly": true
}
},
"required": false
},
"content": {
"description": "The content for the post.",
"type": "object",
"properties": {
"raw": {
"description": "Content for the post, as it exists in the database.",
"type": "string",
"context": [
"edit"
]
},
"rendered": {
"description": "HTML content for the post, transformed for display.",
"type": "string",
"context": [
"view",
"edit"
],
"readonly": true
},
"block_version": {
"description": "Version of the content block format used by the post.",
"type": "integer",
"context": [
"edit"
],
"readonly": true
},
"protected": {
"description": "Whether the content is protected with a password.",
"type": "boolean",
"context": [
"view",
"edit",
"embed"
],
"readonly": true
}
},
"required": false
},
"template": {
"description": "The theme file to use to display the post.",
"type": "string",
"required": false
}
}
}
],
"_links": {
"self": [
{
"href": "http://example.org/index.php?rest_route=/wp/v2/navigation"
}
]
}
},
"/wp/v2/navigation/(?P<id>[\\d]+)": {
"namespace": "wp/v2",
"methods": [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE"
],
"endpoints": [
{
"methods": [
"GET"
],
"args": {
"id": {
"description": "Unique identifier for the post.",
"type": "integer",
"required": false
},
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
"type": "string",
"enum": [
"view",
"embed",
"edit"
],
"default": "view",
"required": false
},
"password": {
"description": "The password for the post if it is password protected.",
"type": "string",
"required": false
}
}
},
{
"methods": [
"POST",
"PUT",
"PATCH"
],
"args": {
"id": {
"description": "Unique identifier for the post.",
"type": "integer",
"required": false
},
"date": {
"description": "The date the post was published, in the site's timezone.",
"type": [
"string",
"null"
],
"format": "date-time",
"required": false
},
"date_gmt": {
"description": "The date the post was published, as GMT.",
"type": [
"string",
"null"
],
"format": "date-time",
"required": false
},
"slug": {
"description": "An alphanumeric identifier for the post unique to its type.",
"type": "string",
"required": false
},
"status": {
"description": "A named status for the post.",
"type": "string",
"enum": [
"publish",
"future",
"draft",
"pending",
"private"
],
"required": false
},
"password": {
"description": "A password to protect access to the content and excerpt.",
"type": "string",
"required": false
},
"title": {
"description": "The title for the post.",
"type": "object",
"properties": {
"raw": {
"description": "Title for the post, as it exists in the database.",
"type": "string",
"context": [
"edit"
]
},
"rendered": {
"description": "HTML title for the post, transformed for display.",
"type": "string",
"context": [
"view",
"edit",
"embed"
],
"readonly": true
}
},
"required": false
},
"content": {
"description": "The content for the post.",
"type": "object",
"properties": {
"raw": {
"description": "Content for the post, as it exists in the database.",
"type": "string",
"context": [
"edit"
]
},
"rendered": {
"description": "HTML content for the post, transformed for display.",
"type": "string",
"context": [
"view",
"edit"
],
"readonly": true
},
"block_version": {
"description": "Version of the content block format used by the post.",
"type": "integer",
"context": [
"edit"
],
"readonly": true
},
"protected": {
"description": "Whether the content is protected with a password.",
"type": "boolean",
"context": [
"view",
"edit",
"embed"
],
"readonly": true
}
},
"required": false
},
"template": {
"description": "The theme file to use to display the post.",
"type": "string",
"required": false
}
}
},
{
"methods": [
"DELETE"
],
"args": {
"id": {
"description": "Unique identifier for the post.",
"type": "integer",
"required": false
},
"force": {
"type": "boolean",
"default": false,
"description": "Whether to bypass Trash and force deletion.",
"required": false
}
}
}
]
},
"/wp/v2/navigation/(?P<parent>[\\d]+)/revisions": {
"namespace": "wp/v2",
"methods": [
"GET"
],
"endpoints": [
{
"methods": [
"GET"
],
"args": {
"parent": {
"description": "The ID for the parent of the revision.",
"type": "integer",
"required": false
},
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
"type": "string",
"enum": [
"view",
"embed",
"edit"
],
"default": "view",
"required": false
},
"page": {
"description": "Current page of the collection.",
"type": "integer",
"default": 1,
"minimum": 1,
"required": false
},
"per_page": {
"description": "Maximum number of items to be returned in result set.",
"type": "integer",
"minimum": 1,
"maximum": 100,
"required": false
},
"search": {
"description": "Limit results to those matching a string.",
"type": "string",
"required": false
},
"exclude": {
"description": "Ensure result set excludes specific IDs.",
"type": "array",
"items": {
"type": "integer"
},
"default": [],
"required": false
},
"include": {
"description": "Limit result set to specific IDs.",
"type": "array",
"items": {
"type": "integer"
},
"default": [],
"required": false
},
"offset": {
"description": "Offset the result set by a specific number of items.",
"type": "integer",
"required": false
},
"order": {
"description": "Order sort attribute ascending or descending.",
"type": "string",
"default": "desc",
"enum": [
"asc",
"desc"
],
"required": false
},
"orderby": {
"description": "Sort collection by object attribute.",
"type": "string",
"default": "date",
"enum": [
"date",
"id",
"include",
"relevance",
"slug",
"include_slugs",
"title"
],
"required": false
}
}
}
]
},
"/wp/v2/navigation/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)": {
"namespace": "wp/v2",
"methods": [
"GET",
"DELETE"
],
"endpoints": [
{
"methods": [
"GET"
],
"args": {
"parent": {
"description": "The ID for the parent of the revision.",
"type": "integer",
"required": false
},
"id": {
"description": "Unique identifier for the revision.",
"type": "integer",
"required": false
},
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
"type": "string",
"enum": [
"view",
"embed",
"edit"
],
"default": "view",
"required": false
}
}
},
{
"methods": [
"DELETE"
],
"args": {
"parent": {
"description": "The ID for the parent of the revision.",
"type": "integer",
"required": false
},
"id": {
"description": "Unique identifier for the revision.",
"type": "integer",
"required": false
},
"force": {
"type": "boolean",
"default": false,
"description": "Required to be true, as revisions do not support trashing.",
"required": false
}
}
}
]
},
"/wp/v2/navigation/(?P<id>[\\d]+)/autosaves": {
"namespace": "wp/v2",
"methods": [
"GET",
"POST"
],
"endpoints": [
{
"methods": [
"GET"
],
"args": {
"parent": {
"description": "The ID for the parent of the autosave.",
"type": "integer",
"required": false
},
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
"type": "string",
"enum": [
"view",
"embed",
"edit"
],
"default": "view",
"required": false
}
}
},
{
"methods": [
"POST"
],
"args": {
"parent": {
"description": "The ID for the parent of the autosave.",
"type": "integer",
"required": false
},
"date": {
"description": "The date the post was published, in the site's timezone.",
"type": [
"string",
"null"
],
"format": "date-time",
"required": false
},
"date_gmt": {
"description": "The date the post was published, as GMT.",
"type": [
"string",
"null"
],
"format": "date-time",
"required": false
},
"slug": {
"description": "An alphanumeric identifier for the post unique to its type.",
"type": "string",
"required": false
},
"status": {
"description": "A named status for the post.",
"type": "string",
"enum": [
"publish",
"future",
"draft",
"pending",
"private"
],
"required": false
},
"password": {
"description": "A password to protect access to the content and excerpt.",
"type": "string",
"required": false
},
"title": {
"description": "The title for the post.",
"type": "object",
"properties": {
"raw": {
"description": "Title for the post, as it exists in the database.",
"type": "string",
"context": [
"edit"
]
},
"rendered": {
"description": "HTML title for the post, transformed for display.",
"type": "string",
"context": [
"view",
"edit",
"embed"
],
"readonly": true
}
},
"required": false
},
"content": {
"description": "The content for the post.",
"type": "object",
"properties": {
"raw": {
"description": "Content for the post, as it exists in the database.",
"type": "string",
"context": [
"edit"
]
},
"rendered": {
"description": "HTML content for the post, transformed for display.",
"type": "string",
"context": [
"view",
"edit"
],
"readonly": true
},
"block_version": {
"description": "Version of the content block format used by the post.",
"type": "integer",
"context": [
"edit"
],
"readonly": true
},
"protected": {
"description": "Whether the content is protected with a password.",
"type": "boolean",
"context": [
"view",
"edit",
"embed"
],
"readonly": true
}
},
"required": false
},
"template": {
"description": "The theme file to use to display the post.",
"type": "string",
"required": false
}
}
}
]
},
"/wp/v2/navigation/(?P<parent>[\\d]+)/autosaves/(?P<id>[\\d]+)": {
"namespace": "wp/v2",
"methods": [
"GET"
],
"endpoints": [
{
"methods": [
"GET"
],
"args": {
"parent": {
"description": "The ID for the parent of the autosave.",
"type": "integer",
"required": false
},
"id": {
"description": "The ID for the autosave.",
"type": "integer",
"required": false
},
"context": {
"description": "Scope under which the request is made; determines fields present in response.",
"type": "string",
"enum": [
"view",
"embed",
"edit"
],
"default": "view",
"required": false
}
}
}
]
},
"/wp/v2/types": {
"namespace": "wp/v2",
"methods": [
@@ -5930,6 +6738,26 @@ mockedApiResponse.Schema = {
"authors"
],
"required": false
},
"has_published_posts": {
"description": "Limit result set to users who have published posts.",
"type": [
"boolean",
"array"
],
"items": {
"type": "string",
"enum": {
"post": "post",
"page": "page",
"attachment": "attachment",
"wp_block": "wp_block",
"wp_template": "wp_template",
"wp_template_part": "wp_template_part",
"wp_navigation": "wp_navigation"
}
},
"required": false
}
}
},
@@ -7728,6 +8556,31 @@ mockedApiResponse.Schema = {
}
]
},
"/wp/v2/widget-types/(?P<id>[a-zA-Z0-9_-]+)/render": {
"namespace": "wp/v2",
"methods": [
"POST"
],
"endpoints": [
{
"methods": [
"POST"
],
"args": {
"id": {
"description": "The widget type id.",
"type": "string",
"required": true
},
"instance": {
"description": "Current instance settings of the widget.",
"type": "object",
"required": false
}
}
}
]
},
"/wp/v2/widgets": {
"namespace": "wp/v2",
"methods": [
@@ -9210,6 +10063,34 @@ mockedApiResponse.TypesCollection = {
}
]
}
},
"wp_navigation": {
"description": "Navigation menus.",
"hierarchical": false,
"name": "Navigation Menus",
"slug": "wp_navigation",
"taxonomies": [],
"rest_base": "navigation",
"rest_namespace": "wp/v2",
"_links": {
"collection": [
{
"href": "http://example.org/index.php?rest_route=/wp/v2/types"
}
],
"wp:items": [
{
"href": "http://example.org/index.php?rest_route=/wp/v2/navigation"
}
],
"curies": [
{
"name": "wp",
"href": "https://api.w.org/{rel}",
"templated": true
}
]
}
}
};

View File

@@ -31,14 +31,22 @@ module.exports = function( env = { environment: 'production', watch: false, buil
'latest-comments',
'latest-posts',
'loginout',
'navigation',
'navigation-area',
'navigation-link',
'navigation-submenu',
'page-list',
'pattern',
'post-author',
'post-comments',
'post-content',
'post-date',
'post-excerpt',
'post-featured-image',
'post-navigation-link',
'post-template',
'post-terms',
'post-title',
'post-template',
'query',
'query-pagination',
'query-pagination-next',
@@ -54,6 +62,7 @@ module.exports = function( env = { environment: 'production', watch: false, buil
'social-link',
'tag-cloud',
'template-part',
'term-description',
];
const blockFolders = [
'audio',
@@ -90,6 +99,7 @@ module.exports = function( env = { environment: 'production', watch: false, buil
];
const blockPHPFiles = {
'widgets/src/blocks/legacy-widget/index.php': 'wp-includes/blocks/legacy-widget.php',
'widgets/src/blocks/widget-group/index.php': 'wp-includes/blocks/widget-group.php',
...dynamicBlockFolders.reduce( ( files, blockName ) => {
files[ `block-library/src/${ blockName }/index.php` ] = `wp-includes/blocks/${ blockName }.php`;
return files;
@@ -97,6 +107,7 @@ module.exports = function( env = { environment: 'production', watch: false, buil
};
const blockMetadataFiles = {
'widgets/src/blocks/legacy-widget/block.json': 'wp-includes/blocks/legacy-widget/block.json',
'widgets/src/blocks/widget-group/block.json': 'wp-includes/blocks/widget-group/block.json',
...blockFolders.reduce( ( files, blockName ) => {
files[ `block-library/src/${ blockName }/block.json` ] = `wp-includes/blocks/${ blockName }/block.json`;
return files;
@@ -143,6 +154,7 @@ module.exports = function( env = { environment: 'production', watch: false, buil
mode,
entry: {
'file/view': join( baseDir, `node_modules/@wordpress/block-library/build-module/file/view` ),
'navigation/view': join( baseDir, `node_modules/@wordpress/block-library/build-module/navigation/view` ),
},
output: {
devtoolNamespace: 'wp',