Skip to content

Commit

Permalink
Behaviors: Extend Global Styles API to read/write behaviors config. (#…
Browse files Browse the repository at this point in the history
…52370)

* Add behaviors to global styles API

* Add behaviors to global styles API endpoint

* Fix coding standards warnings

* Add behavior selector to the site editor, should read for theme.json

* Now the database saves global behaviors

* Fix selector value according to behavior selected

* Add animation to the selector

* Remove not needed themebehaviors

* Use settings for the display

* Show theme json default animation in selector

* If all behaviors are disabled, prevent selector showing on site editor

* Define set function

* Fix if settings behaviors are undefined

* Add apply globally, not yet autosaving

* Add apply globally, still need to work with styles too

* Update to work with new setImmutably function

* Make it work with global styles too

* Make default option work

* Prevent behaviors ui showing if experimental setting is disabled

* Make compatible with supports

* Move `__experimentalUseHasBehaviorsPanel` to hooks.js

* Rename `__experimentalUseGlobalBehaviors` to `useGlobalBehaviors` when destructuring in screen-block.js

* Remove `shouldDecodeEncode` because it's  always true.

* Add global revisions by extending 6.3 controller

* Update php unit test

* Add a default behaviors object to StyleVariationsContainer

* Move all endpoints to 6.4 folder

* Remove `shouldEncodeDecode` parameter from __experimentalUseGlobalBehaviors

* Remove `shouldReturnBehaviors` parameter from __experimentalUseGlobalBehaviors

* Revert test change that sneaked in previous commit

* Skip already existing in Core test that is interfering

* Remove unrelated line that sneaked in thu rebase

---------

Co-authored-by: Michal Czaplinski <mmczaplinski@gmail.com>
  • Loading branch information
cbravobernal and michalczaplinski committed Jul 20, 2023
1 parent 6d22c92 commit d5ab723
Show file tree
Hide file tree
Showing 22 changed files with 789 additions and 156 deletions.
23 changes: 0 additions & 23 deletions docs/reference-guides/data/data-core-block-editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,29 +168,6 @@ _Returns_

- `Array?`: The list of allowed block types.

### getBehaviors

Returns the behaviors registered with the editor.

Behaviors are named, reusable pieces of functionality that can be attached to blocks. They are registered with the editor using the `theme.json` file.

_Usage_

```js
const behaviors = select( blockEditorStore ).getBehaviors();
if ( behaviors?.lightbox ) {
// Do something with the lightbox.
}
```
_Parameters_
- _state_ `Object`: Editor state.
_Returns_
- `Object`: The editor behaviors object.
### getBlock

Returns a block given its client ID. This is a parsed copy of the block, containing its `blockName`, `clientId`, and current `attributes` state. This is not the block's registration settings, which must be retrieved from the blocks module registration store.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Gutenberg_REST_Global_Styles_Revisions_Controller_6_3 extends WP_REST_Cont
* @since 6.3.0
* @var string
*/
private $parent_post_type;
protected $parent_post_type;

/**
* The base of the parent controller's route.
Expand Down Expand Up @@ -102,7 +102,7 @@ public function get_collection_params() {
* @param string $raw_json Encoded JSON from global styles custom post content.
* @return Array|WP_Error
*/
private function get_decoded_global_styles_json( $raw_json ) {
protected function get_decoded_global_styles_json( $raw_json ) {
$decoded_json = json_decode( $raw_json, true );

if ( is_array( $decoded_json ) && isset( $decoded_json['isGlobalStylesUserThemeJSON'] ) && true === $decoded_json['isGlobalStylesUserThemeJSON'] ) {
Expand Down
18 changes: 0 additions & 18 deletions lib/compat/wordpress-6.3/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,6 @@ function gutenberg_update_templates_template_parts_rest_controller( $args, $post
}
add_filter( 'register_post_type_args', 'gutenberg_update_templates_template_parts_rest_controller', 10, 2 );

/**
* Registers the Global Styles Revisions REST API routes.
*/
function gutenberg_register_global_styles_revisions_endpoints() {
$global_styles_revisions_controller = new Gutenberg_REST_Global_Styles_Revisions_Controller_6_3();
$global_styles_revisions_controller->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_global_styles_revisions_endpoints' );

/**
* Registers the Global Styles REST API routes.
*/
function gutenberg_register_global_styles_endpoints() {
$global_styles_controller = new Gutenberg_REST_Global_Styles_Controller_6_3();
$global_styles_controller->register_routes();
}
add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' );

/**
* Add the `modified` value to the `wp_template` schema.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
<?php
/**
* REST API: Gutenberg_REST_Global_Styles_Controller class
*
* @package Gutenberg
* @subpackage REST_API
*/

/**
* Base Global Styles REST API Controller.
*/
class Gutenberg_REST_Global_Styles_Controller_6_4 extends Gutenberg_REST_Global_Styles_Controller_6_3 {

/**
* Retrieves the block pattern category schema, conforming to JSON Schema.
*
* @since 6.0.0
*
* @return array Item schema data.
*/
public function get_item_schema() {
if ( $this->schema ) {
return $this->add_additional_fields_schema( $this->schema );
}

$schema = array(
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => $this->post_type,
'type' => 'object',
'properties' => array(
'id' => array(
'description' => __( 'ID of global styles config.', 'default' ),
'type' => 'string',
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'styles' => array(
'description' => __( 'Global styles.', 'default' ),
'type' => array( 'object' ),
'context' => array( 'view', 'edit' ),
),
'settings' => array(
'description' => __( 'Global settings.', 'default' ),
'type' => array( 'object' ),
'context' => array( 'view', 'edit' ),
),
'behaviors' => array(
'description' => __( 'Global behaviors.', 'default' ),
'type' => array( 'object' ),
'context' => array( 'view', 'edit' ),
),
'title' => array(
'description' => __( 'Title of the global styles variation.', 'default' ),
'type' => array( 'object', 'string' ),
'default' => '',
'context' => array( 'embed', 'view', 'edit' ),
'properties' => array(
'raw' => array(
'description' => __( 'Title for the global styles variation, as it exists in the database.', 'default' ),
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
),
'rendered' => array(
'description' => __( 'HTML title for the post, transformed for display.', 'default' ),
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => true,
),
),
),
),
);

$this->schema = $schema;

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

/**
* Prepare a global styles config output for response.
*
* @since 5.9.0
* @since 6.2 Handling of style.css was added to WP_Theme_JSON.
*
* @param WP_Post $post Global Styles post object.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response Response object.
*/
public function prepare_item_for_response( $post, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
$raw_config = json_decode( $post->post_content, true );
$is_global_styles_user_theme_json = isset( $raw_config['isGlobalStylesUserThemeJSON'] ) && true === $raw_config['isGlobalStylesUserThemeJSON'];
$config = array();
if ( $is_global_styles_user_theme_json ) {
$config = ( new WP_Theme_JSON_Gutenberg( $raw_config, 'custom' ) )->get_raw_data();
}

// Base fields for every post.
$data = array();
$fields = $this->get_fields_for_response( $request );

if ( rest_is_field_included( 'id', $fields ) ) {
$data['id'] = $post->ID;
}

if ( rest_is_field_included( 'title', $fields ) ) {
$data['title'] = array();
}
if ( rest_is_field_included( 'title.raw', $fields ) ) {
$data['title']['raw'] = $post->post_title;
}
if ( rest_is_field_included( 'title.rendered', $fields ) ) {
add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );

$data['title']['rendered'] = get_the_title( $post->ID );

remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) );
}

if ( rest_is_field_included( 'settings', $fields ) ) {
$data['settings'] = ! empty( $config['settings'] ) && $is_global_styles_user_theme_json ? $config['settings'] : new stdClass();
}

if ( rest_is_field_included( 'styles', $fields ) ) {
$data['styles'] = ! empty( $config['styles'] ) && $is_global_styles_user_theme_json ? $config['styles'] : new stdClass();
}

if ( rest_is_field_included( 'behaviors', $fields ) ) {
$data['behaviors'] = ! empty( $config['behaviors'] ) && $is_global_styles_user_theme_json ? $config['behaviors'] : new stdClass();
}

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

// Wrap the data in a response object.
$response = rest_ensure_response( $data );

if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
$links = $this->prepare_links( $post->ID );
$response->add_links( $links );
if ( ! empty( $links['self']['href'] ) ) {
$actions = $this->get_available_actions();
$self = $links['self']['href'];
foreach ( $actions as $rel ) {
$response->add_link( $rel, $self );
}
}
}

return $response;
}

/**
* Returns the given theme global styles config.
* Duplicated from core.
* The only change is that we call WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( 'theme' ) instead of WP_Theme_JSON_Resolver::get_merged_data( 'theme' ).
*
* @since 6.2.0
*
* @param WP_REST_Request $request The request instance.
* @return WP_REST_Response|WP_Error
*/
public function get_theme_item( $request ) {
if ( get_stylesheet() !== $request['stylesheet'] ) {
// This endpoint only supports the active theme for now.
return new WP_Error(
'rest_theme_not_found',
__( 'Theme not found.', 'default' ),
array( 'status' => 404 )
);
}

$theme = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( 'theme' );
$data = array();
$fields = $this->get_fields_for_response( $request );

if ( rest_is_field_included( 'settings', $fields ) ) {
$data['settings'] = $theme->get_settings();
}

if ( rest_is_field_included( 'styles', $fields ) ) {
$raw_data = $theme->get_raw_data();
$data['styles'] = isset( $raw_data['styles'] ) ? $raw_data['styles'] : array();
}

if ( rest_is_field_included( 'behaviors', $fields ) ) {
$raw_data = $theme->get_raw_data();
$data['behaviors'] = isset( $raw_data['behaviors'] ) ? $raw_data['behaviors'] : array();
}

$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 );

if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) {
$links = array(
'self' => array(
'href' => rest_url( sprintf( '%s/%s/themes/%s', $this->namespace, $this->rest_base, $request['stylesheet'] ) ),
),
);
$response->add_links( $links );
}

return $response;
}

/**
* Prepares a single global styles config for update.
*
* @since 5.9.0
* @since 6.2.0 Added validation of styles.css property.
*
* @param WP_REST_Request $request Request object.
* @return stdClass Changes to pass to wp_update_post.
*/
protected function prepare_item_for_database( $request ) {
$changes = new stdClass();
$changes->ID = $request['id'];
$post = get_post( $request['id'] );
$existing_config = array();
if ( $post ) {
$existing_config = json_decode( $post->post_content, true );
$json_decoding_error = json_last_error();
if ( JSON_ERROR_NONE !== $json_decoding_error || ! isset( $existing_config['isGlobalStylesUserThemeJSON'] ) ||
! $existing_config['isGlobalStylesUserThemeJSON'] ) {
$existing_config = array();
}
}
if ( isset( $request['styles'] ) || isset( $request['settings'] ) || isset( $request['behaviors'] ) ) {
$config = array();
if ( isset( $request['styles'] ) ) {
$config['styles'] = $request['styles'];
if ( isset( $request['styles']['css'] ) ) {
$validate_custom_css = $this->validate_custom_css( $request['styles']['css'] );
if ( is_wp_error( $validate_custom_css ) ) {
return $validate_custom_css;
}
}
} elseif ( isset( $existing_config['styles'] ) ) {
$config['styles'] = $existing_config['styles'];
}
if ( isset( $request['settings'] ) ) {
$config['settings'] = $request['settings'];
} elseif ( isset( $existing_config['settings'] ) ) {
$config['settings'] = $existing_config['settings'];
}
if ( isset( $request['behaviors'] ) ) {
$config['behaviors'] = $request['behaviors'];
} elseif ( isset( $existing_config['behaviors'] ) ) {
$config['behaviors'] = $existing_config['behaviors'];
}
$config['isGlobalStylesUserThemeJSON'] = true;
$config['version'] = WP_Theme_JSON_Gutenberg::LATEST_SCHEMA;
$changes->post_content = wp_json_encode( $config );
}
// Post title.
if ( isset( $request['title'] ) ) {
if ( is_string( $request['title'] ) ) {
$changes->post_title = $request['title'];
} elseif ( ! empty( $request['title']['raw'] ) ) {
$changes->post_title = $request['title']['raw'];
}
}
return $changes;
}

}
Loading

0 comments on commit d5ab723

Please sign in to comment.