Skip to content

Commit

Permalink
Merge pull request #51 from bigbite/feature/bbt-123-styles-switcher
Browse files Browse the repository at this point in the history
[BBT-123]: Add functionality to allow editors to manage multiple style variations
  • Loading branch information
Joe-Rouse committed Feb 2, 2024
2 parents 2ad7ec0 + 5a8cc84 commit 61554b5
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 36 deletions.
106 changes: 80 additions & 26 deletions inc/class-rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,32 @@ public function register_routes() {
'themer/v1',
'/style-variations',
array(
'methods' => 'GET',
'callback' => array( $this, 'get_all_theme_style_variation_posts' ),
'methods' => 'GET,POST',
'callback' => array( $this, 'handle_theme_style_variations' ),
'permission_callback' => function () {
return true;
return current_user_can( 'edit_theme_options' );
},
'args' => array(
'globalStylesId' => array(
'validate_callback' => function( $param, $request ) {
if ( $request->get_method() === 'GET' ) {
return true;
}

$post = get_post( $param );
if ( ! $post ) {
return false;
}

$stylesheet = get_stylesheet();
if ( 'wp_global_styles' !== $post->post_type || ! has_term( $stylesheet, 'wp_theme', $post ) ) {
return false;
}

return true;
},
),
),
)
);
}
Expand Down Expand Up @@ -145,37 +166,70 @@ public function get_theme_json(): WP_REST_Response|WP_Error {
}

/**
* Returns all 'wp_global_styles' posts linked to the current theme.
* Returns an array of all of the `wp_global_styles` posts linked to the active theme. If none exist, returns an error.
*
* @return WP_REST_Response|WP_Error
*/
public function get_all_theme_style_variation_posts(): WP_REST_Response|WP_Error {
$theme = get_stylesheet();
$posts = get_posts(
array(
'post_type' => 'wp_global_styles',
'post_status' => array( 'publish', 'draft' ),
'orderby' => 'date',
'order' => 'desc',
'numberposts' => -1,
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'tax_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- This could be a slow query, but it's necessary.
array(
'taxonomy' => 'wp_theme',
'field' => 'name',
'terms' => $theme,
),
),
)
);
private function get_theme_style_variations(): WP_REST_Response|WP_Error {
$posts = get_theme_style_variation_posts();

if ( empty( $posts ) ) {
return new WP_Error( 'no_theme_styles', __( 'Unable to locate existing styles for the theme', 'themer' ) );
}

return rest_ensure_response( $posts );
}

/**
* Sets a new 'active' style variation by ensuring it is the only one linked to the current theme that is published.
*
* @param int $global_styles_id - The ID of the style variation to be published.
* @return WP_REST_Response|WP_Error
*/
private function set_new_active_style_variation( int $global_styles_id ): WP_REST_Response|WP_Error {
$posts = get_theme_style_variation_posts();

// Sets the currently selected style variation to draft and publishes the newly selected one.
foreach ( $posts as $post ) {
if ( 'publish' !== $post->post_status && $post->ID !== $global_styles_id ) {
continue;
}

$post_status = 'draft';
if ( $post->ID === $global_styles_id ) {
$post_status = 'publish';
}
wp_update_post(
array(
'ID' => $post->ID,
'post_status' => $post_status,
)
);
}

return rest_ensure_response( new WP_REST_Response( array( 'message' => __( 'Active theme style variation updated.', 'themer' ) ), 200 ) );
}

/**
* GET request returns all of the `wp_global_styles` posts for the current theme.
* POST request publishes the post with the supplied ID and sets all other posts to draft.
*
* @param WP_REST_Request $request - The request object.
* @return WP_REST_Response|WP_Error
*/
public function handle_theme_style_variations( $request ): WP_REST_Response|WP_Error {
$method = $request->get_method();

if ( 'GET' === $method ) {
return $this->get_theme_style_variations();
}

if ( 'POST' === $method ) {
$json_body = $request->get_json_params();
$global_styles_id = $json_body['globalStylesId'];
return $this->set_new_active_style_variation( $global_styles_id );
}

return rest_ensure_response( new WP_REST_Response( array( 'error' => __( 'Unsupported request method.', 'themer' ) ), 405 ) );
}
}
32 changes: 32 additions & 0 deletions inc/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,35 @@
*
* @package themer
*/

namespace Big_Bite\themer;

/**
* Retrieves all of the `wp_global_styles` posts linked to the active theme.
*
* @return Array<WP_Post>
*/
function get_theme_style_variation_posts() {
$stylesheet = get_stylesheet();
return get_posts(
array(
'post_type' => 'wp_global_styles',
'post_status' => array( 'publish', 'draft' ),
'orderby' => 'date',
'order' => 'desc',
'numberposts' => -1,
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- This could be a slow query, but it's necessary.
'tax_query' => array(
array(
'taxonomy' => 'wp_theme',
'field' => 'name',
'terms' => $stylesheet,
),
),
)
);
}
104 changes: 94 additions & 10 deletions src/editor/components/ThemerComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Spinner,
MenuGroup,
MenuItem,
SelectControl,
__experimentalNavigatorProvider as NavigatorProvider,
} from '@wordpress/components';
import { useSelect, dispatch } from '@wordpress/data';
Expand Down Expand Up @@ -37,6 +38,9 @@ const ThemerComponent = () => {
const [ previewExampleIsActive, setPreviewExampleIsActive ] = useState();
const [ schema, setSchema ] = useState( {} );
const [ validThemeJson, setValidThemeJson ] = useState();
const [ globalStylesId, setGlobalStylesId ] = useState( 0 );
const [ styleVariations, setStyleVariations ] = useState( [] );
const [ publishedStylesId, setPublishedStylesId ] = useState( 0 );

const setUserConfig = ( config ) => {
dispatch( 'core' ).editEntityRecord(
Expand All @@ -47,33 +51,38 @@ const ThemerComponent = () => {
);
};

const { globalStylesId, baseConfig, userConfig, savedUserConfig } =
useSelect( ( select ) => {
const { baseConfig, userConfig, savedUserConfig } = useSelect(
( select ) => {
if ( ! globalStylesId ) {
return {
baseConfig: {},
userConfig: {},
savedUserConfig: {},
};
}

const {
__experimentalGetCurrentGlobalStylesId,
__experimentalGetCurrentThemeBaseGlobalStyles,
getEditedEntityRecord,
getEntityRecord,
} = select( 'core' );

const currentGlobalStylesId =
__experimentalGetCurrentGlobalStylesId();

return {
globalStylesId: currentGlobalStylesId, // eslint-disable no-underscore-dangle -- require underscore dangle for experimental functions
baseConfig: __experimentalGetCurrentThemeBaseGlobalStyles(), // eslint-disable no-underscore-dangle -- require underscore dangle for experimental functions
userConfig: getEditedEntityRecord(
'root',
'globalStyles',
currentGlobalStylesId
globalStylesId
),
savedUserConfig: getEntityRecord(
'root',
'globalStyles',
currentGlobalStylesId
globalStylesId
),
};
} );
},
[ globalStylesId ]
);

/**
* Returns merged base and user configs
Expand Down Expand Up @@ -118,6 +127,28 @@ const ThemerComponent = () => {
setValidThemeJson( res );
};

/**
* Retrieve all style variations for the theme and store the global style ID in state.
*
* @return {void}
*/
const getStyleVariations = async () => {
const styleVariationsRes = await apiFetch( {
path: '/themer/v1/style-variations',
method: 'GET',
} );
setStyleVariations( styleVariationsRes );

const activeVariation = styleVariationsRes?.find(
( variation ) => variation.post_status === 'publish'
);
if ( ! activeVariation ) {
return;
}
setPublishedStylesId( activeVariation.ID );
setGlobalStylesId( activeVariation.ID );
};

/**
* Resets preview blocks to default template
*/
Expand All @@ -137,6 +168,10 @@ const ThemerComponent = () => {
validateThemeJson();
}, [] );

useEffect( () => {
getStyleVariations();
}, [] );

/**
* Alert user if they try to leave Themer without saving.
*/
Expand Down Expand Up @@ -179,6 +214,25 @@ const ThemerComponent = () => {
);
};

/**
* Sets the active style variation for the theme.
*/
const activate = async () => {
try {
await apiFetch( {
path: '/themer/v1/style-variations',
method: 'POST',
data: {
globalStylesId,
},
} );
setPublishedStylesId( globalStylesId );
} catch ( err ) {
// eslint-disable-next-line no-console
console.log( err );
}
};

const clearAllCustomisations = () => {
dispatch( 'core' ).editEntityRecord(
'root',
Expand All @@ -196,6 +250,19 @@ const ThemerComponent = () => {
);
}

const selectOptions = [
{
disabled: true,
label: __( 'Select a style variation', 'themer' ),
value: '',
},
,
...styleVariations.map( ( variation ) => ( {
label: variation.post_name,
value: variation.ID,
} ) ),
];

return (
<>
<EditorContext.Provider
Expand Down Expand Up @@ -225,6 +292,15 @@ const ThemerComponent = () => {
{ validThemeJson === true && (
<>
<div className="themer-topbar">
<SelectControl
options={ selectOptions }
value={ globalStylesId }
onChange={ ( value ) =>
setGlobalStylesId(
parseInt( value, 10 )
)
}
/>
<Button
isSecondary
onClick={ () => reset() }
Expand All @@ -237,6 +313,14 @@ const ThemerComponent = () => {
text="Save"
disabled={ ! hasUnsavedChanges }
/>
<Button
isPrimary
onClick={ activate }
text={ __( 'Activate', 'themer' ) }
disabled={
publishedStylesId === globalStylesId
}
/>
<MoreMenuDropdown>
{ () => (
<MenuGroup
Expand Down

0 comments on commit 61554b5

Please sign in to comment.