Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigation Area block #36178

Merged
merged 36 commits into from
Nov 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6e365e0
try nav-area-block
adamziel Nov 3, 2021
9c3354f
Resolve conflicts with trunk
adamziel Nov 3, 2021
c9ee028
Rely on contexts also in PHP navigation block's render callback
adamziel Nov 3, 2021
228b376
Lint and typo fixes
talldan Nov 4, 2021
e87f27d
Add a description for the navigation area block
talldan Nov 4, 2021
b65423b
Remove navigation area and unused functions
talldan Nov 4, 2021
3ac3119
Update navigation area example
talldan Nov 4, 2021
dc82243
Remove unused attribute
talldan Nov 4, 2021
96f60cf
Minor copy update
talldan Nov 4, 2021
2ffe39a
Try passing the correct menu id into the template
talldan Nov 4, 2021
544169a
Only build template when menu id is known and add loading state
talldan Nov 4, 2021
d7bdda1
Show navigation block placeholder when in an unassigned navigation area
talldan Nov 4, 2021
0ab571d
Support deleting menus in navigation areas
talldan Nov 4, 2021
d23b0e3
Add location keyword
talldan Nov 4, 2021
1752fe5
Remove unused stylesheet
talldan Nov 4, 2021
32d48d0
Replace references to menu locations with navigation areas
adamziel Nov 4, 2021
1484347
Remove links from the first version
adamziel Nov 4, 2021
86af65c
Add gutenberg_register_navigation_areas that enables overriding the d…
adamziel Nov 4, 2021
c6dfa11
Rename "menu" property returned by the REST API to "navigation"
adamziel Nov 4, 2021
6b4fdd6
Replace a call to get_item() in update_item() with an encapsulation (…
adamziel Nov 4, 2021
7238ed2
Remove @Validate comment and follow the menu-items endpoint in not va…
adamziel Nov 4, 2021
665637b
Remove @TODOs related to changing permissions checks. The checks are …
adamziel Nov 4, 2021
dfe6abb
Restore the // Experimental blocks. comment
adamziel Nov 4, 2021
d1bb992
Preload navigation areas in the site editor
adamziel Nov 4, 2021
b24c048
Use templateLock: 'all' for navigation area inner blocks
adamziel Nov 4, 2021
4351726
Add missing fixtures
adamziel Nov 4, 2021
9586f41
Add file doc comment
adamziel Nov 4, 2021
03f96d0
Remove navigation menu bugfix so that it can be extracted into anothe…
adamziel Nov 4, 2021
7522956
Adjust fixtures
adamziel Nov 4, 2021
76501b7
Adjust fixtures
adamziel Nov 4, 2021
a467be3
Adjust fixturs
adamziel Nov 4, 2021
e31fdfb
Update lib/class-wp-rest-block-navigation-areas-controller.php
draganescu Nov 4, 2021
40349db
Remove div from save content
talldan Nov 5, 2021
e7bc4a1
Fix REST API registration variable nameing
talldan Nov 5, 2021
5956983
Revert "Update lib/class-wp-rest-block-navigation-areas-controller.php"
talldan Nov 5, 2021
b283ea3
Avoid trying to preload post with 0 id, which is default for a naviga…
talldan Nov 5, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function gutenberg_reregister_core_block_types() {
'media-text',
'missing',
'more',
'navigation-area',
'navigation-link',
'navigation-submenu',
'nextpage',
Expand Down Expand Up @@ -69,6 +70,7 @@ function gutenberg_reregister_core_block_types() {
'latest-posts.php' => 'core/latest-posts',
'loginout.php' => 'core/loginout',
'navigation.php' => 'core/navigation',
'navigation-area.php' => 'core/navigation-area',
'navigation-link.php' => 'core/navigation-link',
'navigation-submenu.php' => 'core/navigation-submenu',
'page-list.php' => 'core/page-list',
Expand Down
284 changes: 284 additions & 0 deletions lib/class-wp-rest-block-navigation-areas-controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
<?php
/**
* REST API: WP_REST_Block_Navigation_Areas_Controller class
*
* @subpackage REST_API
* @package WordPress
*/

/**
* Core class used to access block navigation areas via the REST API.
*
* @see WP_REST_Controller
*/
class WP_REST_Block_Navigation_Areas_Controller extends WP_REST_Controller {

/**
* Constructor.
*/
public function __construct() {
$this->namespace = '__experimental';
$this->rest_base = 'block-navigation-areas';
}

/**
* Registers the routes for the objects of the controller.
*
* @see register_rest_route()
*/
public function register_routes() {
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => $this->get_collection_params(),
),
'schema' => array( $this, 'get_public_item_schema' ),
)
);

register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<area>[\w-]+)',
array(
'args' => array(
'area' => array(
'description' => __( 'An alphanumeric identifier for the navigation area.', 'gutenberg' ),
'type' => 'string',
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_item' ),
'permission_callback' => array( $this, 'get_item_permissions_check' ),
'args' => array(
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'update_item' ),
'permission_callback' => array( $this, 'update_item_permissions_check' ),
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to add support for batching?

'schema' => array( $this, 'get_public_item_schema' ),
)
);
}

/**
* Checks whether a given request has permission to read navigation areas.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_Error|bool True if the request has read access, WP_Error object otherwise.
*/
public function get_items_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
if ( ! current_user_can( 'edit_theme_options' ) ) {
return new WP_Error(
'rest_cannot_view',
__( 'Sorry, you are not allowed to view navigation areas.', 'gutenberg' ),
array( 'status' => rest_authorization_required_code() )
);
}

return true;
}

/**
* Retrieves all navigation areas, depending on user context.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
*/
public function get_items( $request ) {
$data = array();
foreach ( gutenberg_get_navigation_areas() as $name => $description ) {
$area = $this->get_navigation_area_object( $name );
$area = $this->prepare_item_for_response( $area, $request );
$data[ $name ] = $this->prepare_response_for_collection( $area );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TimothyBJacobs what do you think if this? Other endpoints simple are a flat array. A key based array is different from other endpoints.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have a couple of object responses, but an array is preferable because the REST API does assume that collection responses are lists.

}
return rest_ensure_response( $data );
}

/**
* Checks if a given request has access to read a navigation area.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_Error|bool True if the request has read access for the item, WP_Error object otherwise.
*/
public function get_item_permissions_check( $request ) {
if ( ! current_user_can( 'edit_theme_options' ) ) {
return new WP_Error(
'rest_cannot_view',
__( 'Sorry, you are not allowed to view navigation areas.', 'gutenberg' ),
array( 'status' => rest_authorization_required_code() )
);
}
if ( ! array_key_exists( $request['area'], gutenberg_get_navigation_areas() ) ) {
return new WP_Error( 'rest_navigation_area_invalid', __( 'Invalid navigation area.', 'gutenberg' ), array( 'status' => 404 ) );
}

return true;
}

/**
* Checks if a request has access to update the specified term.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return bool|WP_Error True if the request has access to update the item, false or WP_Error object otherwise.
*/
public function update_item_permissions_check( $request ) {
return $this->get_item_permissions_check( $request );
}

/**
* Retrieves a specific navigation area.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
*/
public function get_item( $request ) {
$name = $request['area'];
$area = $this->get_navigation_area_object( $name );
$data = $this->prepare_item_for_response( $area, $request );

return rest_ensure_response( $data );
}

/**
* Updates a specific navigation area.
*
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
*/
public function update_item( $request ) {
$name = $request['area'];

$mapping = get_option( 'fse_navigation_areas', array() );
$mapping[ $name ] = $request['navigation'];
update_option( 'fse_navigation_areas', $mapping );

$area = $this->get_navigation_area_object( $name );
$data = $this->prepare_item_for_response( $area, $request );
return rest_ensure_response( $data );
}

/**
* Converts navigation area name to a convenient object that this endpoint can reason about.
*
* @param string $name Navigation area name.
* @return stdClass An object representation of the navigation area.
*/
private function get_navigation_area_object( $name ) {
$available_areas = gutenberg_get_navigation_areas();
$mapping = get_option( 'fse_navigation_areas', array() );
$area = new stdClass();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we create a custom object for this?

$area->name = $name;
$area->navigation = ! empty( $mapping[ $name ] ) ? $mapping[ $name ] : null;
$area->description = $available_areas[ $name ];
return $area;
}

/**
* Prepares a navigation area object for serialization.
*
* @param stdClass $area Post status data.
* @param WP_REST_Request $request Full details about the request.
*
* @return WP_REST_Response Post status data.
*/
public function prepare_item_for_response( $area, $request ) {
$areas = gutenberg_get_navigation_areas();
$navigation = ( isset( $areas[ $area->name ] ) ) ? $area->navigation : 0;

$fields = $this->get_fields_for_response( $request );
$data = array();

if ( rest_is_field_included( 'name', $fields ) ) {
$data['name'] = $area->name;
}

if ( rest_is_field_included( 'description', $fields ) ) {
$data['description'] = $area->description;
}

if ( rest_is_field_included( 'navigation', $fields ) ) {
$data['navigation'] = (int) $navigation;
}

$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
$data = $this->add_additional_fields_to_object( $data, $request );
$data = $this->filter_response_by_context( $data, $context );

$response = rest_ensure_response( $data );

/**
* Filters a navigation area returned from the REST API.
*
* Allows modification of the navigation area data right before it is
* returned.
*
* @param WP_REST_Response $response The response object.
* @param object $area The original status object.
* @param WP_REST_Request $request Request used to generate the response.
*/
return apply_filters( 'rest_prepare_navigation_area', $response, $area, $request );
}

/**
* Retrieves the navigation area's schema, conforming to JSON Schema.
*
* @return array Item schema data.
*/
public function get_item_schema() {
$schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a typo?

Suggested change
'$schema' => 'http://json-schema.org/draft-04/schema#',
'schema' => 'http://json-schema.org/draft-04/schema#',

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so. Suggestion commited.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every definition of get_item_schema in WordPress core has '$schema', so I'm going to revert this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looked weird when I saw it. I should have checked the usage of it. Sorry for the confusion.

'title' => 'navigation-area',
'type' => 'object',
'properties' => array(
'name' => array(
'description' => __( 'The name of the navigation area.', 'gutenberg' ),
'type' => 'string',
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'description' => array(
'description' => __( 'The description of the navigation area.', 'gutenberg' ),
'type' => 'string',
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'navigation' => array(
'description' => __( 'The ID of the assigned navigation.', 'gutenberg' ),
'type' => 'integer',
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
),
);

return $this->add_additional_fields_schema( $schema );
}

/**
* Retrieves the query params for collections.
*
* @return array Collection parameters.
*/
public function get_collection_params() {
return array(
'context' => $this->get_context_param( array( 'default' => 'view' ) ),
);
}

}
35 changes: 19 additions & 16 deletions lib/full-site-editing/edit-site-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,22 +106,25 @@ function gutenberg_edit_site_init( $hook ) {
'edit_site_editor',
'edit-site',
array(
'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',
'preload_paths' => array_merge(
gutenberg_get_navigation_areas_paths_to_preload(),
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',
)
),
'initializer_name' => 'initialize',
'editor_settings' => $settings,
Expand Down
3 changes: 3 additions & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ function gutenberg_is_experiment_enabled( $name ) {
if ( ! class_exists( 'WP_REST_Menu_Items_Controller' ) ) {
require_once __DIR__ . '/class-wp-rest-menu-items-controller.php';
}
if ( ! class_exists( 'WP_REST_Block_Navigation_Areas_Controller' ) ) {
require_once __DIR__ . '/class-wp-rest-block-navigation-areas-controller.php';
}
if ( ! class_exists( 'WP_REST_Menu_Locations_Controller' ) ) {
require_once __DIR__ . '/class-wp-rest-menu-locations-controller.php';
}
Expand Down
Loading