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

[BBT-53] Add a new ButtonExport component and corresponding rest endpoint #13

Merged
merged 3 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 40 additions & 7 deletions inc/class-rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

namespace Big_Bite\themer;

use WP_Error;
use WP_Theme_JSON;
use WP_REST_Request;
use WP_REST_Response;
use WP_Theme_JSON_Resolver;

/**
* Custom REST routes.
*/
Expand All @@ -33,26 +39,53 @@ public function register_routes() {
},
)
);

register_rest_route(
'themer/v1',
'/export',
array(
'methods' => 'GET',
'callback' => array( $this, 'get_theme_json' ),
'permission_callback' => fn() => is_user_logged_in() && current_user_can( 'edit_theme_options' ),
)
);
}

/**
* Get custom CSS rules by merging styles from request with existing theme.json data
*
* @param \WP_REST_Request $request Full data about the request.
* @return \WP_REST_Response|\WP_Error theme.json generated stylesheet response data or WP_Error on failure.
* @param WP_REST_Request $request Full data about the request.
* @return WP_REST_Response|WP_Error theme.json generated stylesheet response data or WP_Error on failure.
*/
public function get_styles( \WP_REST_Request $request ) {
$existing_theme_json = \WP_Theme_JSON_Resolver::get_merged_data();
public function get_styles( WP_REST_Request $request ) {
$existing_theme_json = WP_Theme_JSON_Resolver::get_merged_data();

if ( ! $existing_theme_json instanceof \WP_Theme_JSON ) {
return new \WP_Error( 'no_theme_json', __( 'Unable to locate existing theme.json data', 'themer' ) );
if ( ! $existing_theme_json instanceof WP_Theme_JSON ) {
return new WP_Error( 'no_theme_json', __( 'Unable to locate existing theme.json data', 'themer' ) );
}

$custom_styles = $request->get_json_params();
$custom_theme_json_data = array_merge( $existing_theme_json->get_raw_data(), $custom_styles );
$custom_theme_json = new \WP_Theme_JSON( $custom_theme_json_data );
$custom_theme_json = new WP_Theme_JSON( $custom_theme_json_data );

return rest_ensure_response( $custom_theme_json->get_stylesheet() );
}

/**
* Returns an updated theme.json with merged and flattened layers
*
* @return WP_REST_Response|WP_Error
*/
public function get_theme_json(): WP_REST_Response | WP_Error {
$all_theme_json_layers = WP_Theme_JSON_Resolver::get_merged_data();

if ( ! $all_theme_json_layers instanceof WP_Theme_JSON ) {
return new WP_Error( 'no_theme_json', __( 'Unable to locate existing theme.json data', 'themer' ) );
}

$theme_json_raw_data = new WP_Theme_JSON( $all_theme_json_layers->get_raw_data() );
$theme_json_flattened = $theme_json_raw_data->get_data();

return rest_ensure_response( $theme_json_flattened );
}
}
62 changes: 62 additions & 0 deletions src/editor/components/ButtonExport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import { useState } from '@wordpress/element';
import { Button } from '@wordpress/components';

/**
* Renders the button to export theme.json
*/
const ButtonExport = () => {
const [ isFetching, setIsFetching ] = useState( false );
const isExportSupported =
window.isSecureContext && 'showSaveFilePicker' in window;

/**
* Fetch theme JSON object
*/
const fetchThemeJSON = async () => {
setIsFetching( true );
try {
const response = await apiFetch( { path: '/themer/v1/export' } );
saveThemeJSON( JSON.stringify( response, null, '\t' ) );
} catch ( error ) {
console.error( error ); // eslint-disable-line no-console -- Output of caught error
}
setIsFetching( false );
};

/**
* Save JSON blob to a file
*
* @param {Object} data theme.json data
*/
const saveThemeJSON = async ( data ) => {
const blob = new Blob( [ data ], { type: 'application/json' } ); // eslint-disable-line no-undef -- Blob available in browser environment

const handle = await window.showSaveFilePicker( {
suggestedName: 'theme.json',
} );
const stream = await handle.createWritable();

await stream.write( blob );
await stream.close();
};

if ( ! isExportSupported ) {
return;
}

return (
<Button
isPrimary
isBusy={ isFetching }
disabled={ isFetching }
onClick={ fetchThemeJSON }
aria-label={ __( 'Export theme.json', 'themer' ) }
>
{ __( 'Export theme.json', 'themer' ) }
</Button>
);
};

export default ButtonExport;
2 changes: 2 additions & 0 deletions src/editor/components/fields/ThemerComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import apiFetch from '@wordpress/api-fetch';

import Preview from './Preview';
import Fields from './Fields';
import ButtonExport from '../ButtonExport';

/**
* main component
Expand Down Expand Up @@ -123,6 +124,7 @@ const ThemerComponent = () => {
onClick={ () => reset() }
text="reset to theme.json"
/>
<ButtonExport />
</div>
<div className="themer-fields">
<Fields sourceObject={ themeConfig } />
Expand Down