Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
Showing revisions for the wp_global_styles custom post type in the styles sidebar.
We're only showing max 10 to limit the payload.
  • Loading branch information
ramonjd committed Dec 20, 2022
1 parent 0d5caa2 commit e32c7d4
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,72 @@ public function register_routes() {
parent::register_routes();
}

/**
* Retrieves the global styles type' schema, conforming to JSON Schema.
*
* @since 5.9.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.' ),
'type' => 'string',
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'styles' => array(
'description' => __( 'Global styles.' ),
'type' => array( 'object' ),
'context' => array( 'view', 'edit' ),
),
'settings' => array(
'description' => __( 'Global settings.' ),
'type' => array( 'object' ),
'context' => array( 'view', 'edit' ),
),
'title' => array(
'description' => __( 'Title of the global styles variation.' ),
'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.' ),
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
),
'rendered' => array(
'description' => __( 'HTML title for the post, transformed for display.' ),
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => true,
),
),
),
'revisions' => array(
'description' => __( 'Global styles revisions.' ),
'type' => array( 'object' ),
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
),
);

$this->schema = $schema;

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

/**
* Prepare a global styles config output for response.
*
Expand Down Expand Up @@ -67,6 +133,37 @@ public function prepare_item_for_response( $post, $request ) { // phpcs:ignore V
$data['styles'] = ! empty( $config['styles'] ) && $is_global_styles_user_theme_json ? $config['styles'] : new stdClass();
}

if ( $is_global_styles_user_theme_json && rest_is_field_included( 'revisions', $fields ) ) {
$user_theme_revisions = wp_get_post_revisions(
$post->ID,
array(
'author' => $post->post_author,
'posts_per_page' => 10,
)
);
if ( empty( $user_theme_revisions ) ) {
$data['revisions'] = array();
} else {
$user_revisions = array();
// Mostly taken from wp_prepare_revisions_for_js().
foreach ( $user_theme_revisions as $revision ) {
$raw_revision_config = json_decode( $revision->post_content, true );
$config = ( new WP_Theme_JSON_Gutenberg( $raw_revision_config, 'custom' ) )->get_raw_data();
$now_gmt = time();
$modified = strtotime( $revision->post_modified );
$modified_gmt = strtotime( $revision->post_modified_gmt . ' +0000' );
$user_revisions[] = array(
'styles' => ! empty( $config['styles'] ) ? $config['styles'] : new stdClass(),
'dateShort' => date_i18n( _x( 'j M @ H:i', 'revision date short format' ), $modified ),
/* translators: %s: Human-readable time difference. */
'timeAgo' => sprintf( __( '%s ago' ), human_time_diff( $modified_gmt, $now_gmt ) ),
'id' => $revision->ID,
);
}
$data['revisions'] = $user_revisions;
}
}

$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
$data = $this->add_additional_fields_to_object( $data, $request );
$data = $this->filter_response_by_context( $data, $context );
Expand Down
111 changes: 111 additions & 0 deletions packages/edit-site/src/components/global-styles/screen-revisions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* External dependencies
*/
import { set } from 'lodash';
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { __experimentalVStack as VStack, Button } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { useContext, useCallback, useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import ScreenHeader from './header';
import Subtitle from './subtitle';
import { GlobalStylesContext } from './context';

function cloneDeep( object ) {
return ! object ? {} : JSON.parse( JSON.stringify( object ) );
}

function ScreenRevisions() {
const { user: userConfig, setUserConfig } =
useContext( GlobalStylesContext );
const { userRevisions } = useSelect( ( select ) => {
const { getEditedEntityRecord } = select( coreStore );
const _globalStylesId =
select( coreStore ).__experimentalGetCurrentGlobalStylesId();

// Maybe we can return the whole object from __experimentalGetCurrentGlobalStylesId
// and rename it to __experimentalGetCurrentGlobalStyles,
// otherwise we're grabbing this twice.
const record = _globalStylesId
? getEditedEntityRecord( 'root', 'globalStyles', _globalStylesId )
: undefined;

return {
userRevisions: record?.revisions || [],
};
}, [] );
const [ currentRevision, setCurrentRevision ] = useState(
userRevisions?.[ 0 ].id
);
const restoreRevision = useCallback(
( revision ) => {
const newUserConfig = cloneDeep( userConfig );
set( newUserConfig, [ 'styles' ], revision?.styles );
setUserConfig( () => newUserConfig );
setCurrentRevision( revision?.id );
},
[ userConfig ]
);

const hasRevisions = userRevisions.length > 0;

return (
<>
<ScreenHeader
title={ __( 'Revisions' ) }
description={ __(
"Select a revision to preview it in the editor. Changes won't take effect until you've saved the template."
) }
/>
<div className="edit-site-global-styles-screen-revisions">
<VStack spacing={ 3 }>
<Subtitle>{ __( 'REVISIONS' ) }</Subtitle>
{ hasRevisions
? userRevisions.map( ( revision ) => (
<Button
className={ classnames(
'edit-site-global-styles-screen-revisions__revision-item',
{
'is-current':
currentRevision === revision.id,
}
) }
variant={
currentRevision === revision.id
? 'tertiary'
: 'secondary'
}
isPressed={
currentRevision === revision.id
}
disabled={ currentRevision === revision.id }
key={ `user-styles-revision-${ revision.id }` }
onClick={ () => {
restoreRevision( revision );
} }
>
<span className="edit-site-global-styles-screen-revisions__time-ago">
{ revision.timeAgo }
</span>
<span className="edit-site-global-styles-screen-revisions__date">
({ revision.dateShort })
</span>
</Button>
) )
: __( 'There are currently no revisions.' ) }
</VStack>
</div>
</>
);
}

export default ScreenRevisions;
28 changes: 28 additions & 0 deletions packages/edit-site/src/components/global-styles/screen-root.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,34 @@ function ScreenRoot() {
</CardBody>
</>
) }

<CardDivider />

<CardBody>
<Spacer
as="p"
paddingTop={ 2 }
paddingX="13px"
marginBottom={ 4 }
>
{ __(
"View the last ten revisions to your site's styles."
) }
</Spacer>
<ItemGroup>
<NavigationButtonAsItem
path="/revisions"
aria-label={ __( 'Styles revisions' ) }
>
<HStack justify="space-between">
<FlexItem>{ __( 'Revisions' ) }</FlexItem>
<IconWithCurrentColor
icon={ isRTL() ? chevronLeft : chevronRight }
/>
</HStack>
</NavigationButtonAsItem>
</ItemGroup>
</CardBody>
</Card>
);
}
Expand Down
18 changes: 18 additions & 0 deletions packages/edit-site/src/components/global-styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,21 @@ $block-preview-height: 150px;
max-height: 200px;
overflow-y: scroll;
}

.edit-site-global-styles-screen-revisions {
margin: $grid-unit-20;
}

.edit-site-global-styles-screen-revisions__revision-item {
justify-content: center;
&.is-current:disabled {
color: $white;
background: $gray-900;
}
}

.edit-site-global-styles-screen-revisions__time-ago {
display: inline-block;
margin-right: $grid-unit-10;
font-style: italic;
}
5 changes: 5 additions & 0 deletions packages/edit-site/src/components/global-styles/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import ScreenStyleVariations from './screen-style-variations';
import ScreenBorder from './screen-border';
import StyleBook from '../style-book';
import ScreenCSS from './screen-css';
import ScreenRevisions from './screen-revisions';

function GlobalStylesNavigationScreen( { className, ...props } ) {
return (
Expand Down Expand Up @@ -195,6 +196,10 @@ function GlobalStylesUI( { isStyleBookOpened, onCloseStyleBook } ) {
<GlobalStylesNavigationScreen path="/css">
<ScreenCSS />
</GlobalStylesNavigationScreen>

<GlobalStylesNavigationScreen path="/revisions">
<ScreenRevisions />
</GlobalStylesNavigationScreen>
</NavigatorProvider>
);
}
Expand Down

1 comment on commit e32c7d4

@github-actions
Copy link

Choose a reason for hiding this comment

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

Flaky tests detected.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/3737767136
📝 Reported issues:

Please sign in to comment.