From 27c0fb7ba92827b9a19a5144f1d0efdd1ef58797 Mon Sep 17 00:00:00 2001 From: Matias Benedetto Date: Fri, 20 Sep 2024 15:11:44 -0300 Subject: [PATCH] Copy font assets to the local theme folder when creating a style variation (#713) * add option in frontend to opt-in saving font assets to local theme folder * shorter help text * copy font assets to the local theme folder when creating a style variation * skip font faces that has already a theme relative path --------- Co-authored-by: Sarah Norris <1645628+mikachan@users.noreply.github.com> --- includes/class-create-block-theme-api.php | 9 +- includes/create-theme/resolver_additions.php | 35 +++++-- includes/create-theme/theme-fonts.php | 36 +++++-- includes/create-theme/theme-json.php | 30 ++++-- src/editor-sidebar/create-variation-panel.js | 99 ++++++++++++++++---- src/resolvers.js | 4 +- 6 files changed, 164 insertions(+), 49 deletions(-) diff --git a/includes/class-create-block-theme-api.php b/includes/class-create-block-theme-api.php index 66fcfec0..01fc9646 100644 --- a/includes/class-create-block-theme-api.php +++ b/includes/class-create-block-theme-api.php @@ -239,8 +239,15 @@ function rest_create_child_theme( $request ) { } function rest_create_variation( $request ) { + $options = $request->get_params(); + + $save_fonts = isset( $options['saveFonts'] ) && true === $options['saveFonts']; - $response = CBT_Theme_JSON::add_theme_json_variation_to_local( 'variation', $this->sanitize_theme_data( $request->get_params() ) ); + $response = CBT_Theme_JSON::add_theme_json_variation_to_local( + 'variation', + $this->sanitize_theme_data( $options ), + $save_fonts + ); wp_cache_flush(); diff --git a/includes/create-theme/resolver_additions.php b/includes/create-theme/resolver_additions.php index 1a6ad48f..0c684739 100644 --- a/includes/create-theme/resolver_additions.php +++ b/includes/create-theme/resolver_additions.php @@ -65,11 +65,7 @@ public static function export_theme_data( $content, $extra_theme_data = null ) { } // Merge the User Data - if ( class_exists( 'WP_Theme_JSON_Resolver_Gutenberg' ) ) { - $theme->merge( WP_Theme_JSON_Resolver_Gutenberg::get_user_data() ); - } else { - $theme->merge( static::get_user_data() ); - } + $theme->merge( static::get_user_data() ); // Merge the extra theme data received as a parameter if ( ! empty( $extra_theme_data ) ) { @@ -93,8 +89,33 @@ public static function export_theme_data( $content, $extra_theme_data = null ) { $schema = 'https://schemas.wp.org/' . $theme_json_version . '/theme.json'; } $data['$schema'] = $schema; - $theme_json = wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); - return preg_replace( '~(?:^|\G)\h{4}~m', "\t", $theme_json ); + return static::stringify( $data ); + } + + /** + * Get the user data. + * + * This is a copy of the parent function with the addition of the Gutenberg resolver. + * + * @return array + */ + public static function get_user_data() { + // Determine the correct method to retrieve user data + return class_exists( 'WP_Theme_JSON_Resolver_Gutenberg' ) + ? WP_Theme_JSON_Resolver_Gutenberg::get_user_data() + : parent::get_user_data(); + } + + /** + * Stringify the array data. + * + * $data is an array of data to be converted to a JSON string. + * @return string JSON string. + */ + public static function stringify( $data ) { + $data = wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); + // Convert spaces to tabs + return preg_replace( '~(?:^|\G)\h{4}~m', "\t", $data ); } public static function get_theme_file_contents() { diff --git a/includes/create-theme/theme-fonts.php b/includes/create-theme/theme-fonts.php index 9e889fd3..8a05c8a0 100644 --- a/includes/create-theme/theme-fonts.php +++ b/includes/create-theme/theme-fonts.php @@ -112,19 +112,18 @@ public static function make_filename_from_fontface( $font_face, $src, $src_index return $font_filename; } - public static function copy_activated_fonts_to_theme() { - $font_families_to_copy = self::get_user_activated_fonts(); - - if ( is_null( $font_families_to_copy ) ) { - return; - } - - $theme_json = CBT_Theme_JSON_Resolver::get_theme_file_contents(); + /* + * Copy the font assets to the theme. + * + * @param array $font_families The font families to copy. + * @return array $font_families The font families with the font face src updated to the theme font asset location. + */ + public static function copy_font_assets_to_theme( $font_families ) { $theme_font_asset_location = path_join( get_stylesheet_directory(), 'assets/fonts/' ); // Create the font asset directory if it does not exist. wp_mkdir_p( $theme_font_asset_location ); - foreach ( $font_families_to_copy as &$font_family ) { + foreach ( $font_families as &$font_family ) { if ( ! isset( $font_family['fontFace'] ) ) { continue; } @@ -139,6 +138,10 @@ public static function copy_activated_fonts_to_theme() { // if it is a string, cast it to an array $font_face['src'] = (array) $font_face['src']; foreach ( $font_face['src'] as $font_src_index => &$font_src ) { + if ( str_starts_with( $font_src, 'file:' ) ) { + // If the font source starts with 'file:' then it's already a theme asset. + continue; + } $font_filename = basename( $font_src ); $font_pretty_filename = self::make_filename_from_fontface( $font_face, $font_src, $font_src_index ); $font_face_path = path_join( $font_family_dir_path, $font_pretty_filename ); @@ -158,9 +161,22 @@ public static function copy_activated_fonts_to_theme() { } } + return $font_families; + } + + public static function copy_activated_fonts_to_theme() { + $font_families_to_copy = self::get_user_activated_fonts(); + + if ( is_null( $font_families_to_copy ) ) { + return; + } + + $theme_json = CBT_Theme_JSON_Resolver::get_theme_file_contents(); + $copied_font_families = self::copy_font_assets_to_theme( $font_families_to_copy ); + $theme_json['settings']['typography']['fontFamilies'] = array_merge( $theme_json['settings']['typography']['fontFamilies'] ?? array(), - $font_families_to_copy + $copied_font_families ); $user_settings = CBT_Theme_JSON_Resolver::get_user_data()->get_settings(); diff --git a/includes/create-theme/theme-json.php b/includes/create-theme/theme-json.php index e8059de7..dfedb885 100644 --- a/includes/create-theme/theme-json.php +++ b/includes/create-theme/theme-json.php @@ -9,7 +9,7 @@ public static function add_theme_json_to_local( $export_type ) { ); } - public static function add_theme_json_variation_to_local( $export_type, $theme ) { + public static function add_theme_json_variation_to_local( $export_type, $theme, $save_fonts = false ) { $variation_path = get_stylesheet_directory() . DIRECTORY_SEPARATOR . 'styles' . DIRECTORY_SEPARATOR; if ( ! file_exists( $variation_path ) ) { @@ -20,18 +20,28 @@ public static function add_theme_json_variation_to_local( $export_type, $theme ) return new WP_Error( 'variation_already_exists', __( 'Variation already exists.', 'create-block-theme' ) ); } - $_POST['theme']['variation_slug'] = $theme['slug']; - - $extra_theme_data = array( - 'version' => WP_Theme_JSON::LATEST_SCHEMA, - 'title' => $theme['name'], - ); - - $variation_theme_json = CBT_Theme_JSON_Resolver::export_theme_data( $export_type, $extra_theme_data ); + $theme_json = class_exists( 'WP_Theme_JSON_Gutenberg' ) ? new WP_Theme_JSON_Gutenberg() : new WP_Theme_JSON(); + $user_data = CBT_Theme_JSON_Resolver::get_user_data(); + $theme_json->merge( $user_data ); + $variation = $theme_json->get_data(); + $variation['title'] = $theme['name']; + + if ( + $save_fonts && + isset( $variation['settings']['typography']['fontFamilies'] ) + ) { + $font_families = $variation['settings']['typography']['fontFamilies']; + // Copy the font assets to the theme assets folder. + $copied_font_families = CBT_Theme_Fonts::copy_font_assets_to_theme( $font_families ); + // Update the the variation theme json with the font families with the new paths. + $variation['settings']['typography']['fontFamilies'] = $copied_font_families; + } file_put_contents( $variation_path . $theme['slug'] . '.json', - $variation_theme_json + CBT_Theme_JSON_Resolver::stringify( $variation ) ); + + return $variation; } } diff --git a/src/editor-sidebar/create-variation-panel.js b/src/editor-sidebar/create-variation-panel.js index 6a26481a..daa00221 100644 --- a/src/editor-sidebar/create-variation-panel.js +++ b/src/editor-sidebar/create-variation-panel.js @@ -3,18 +3,24 @@ */ import { __ } from '@wordpress/i18n'; import { useState } from '@wordpress/element'; -import { useDispatch } from '@wordpress/data'; +import { useDispatch, useSelect } from '@wordpress/data'; import { store as noticesStore } from '@wordpress/notices'; import { // eslint-disable-next-line @wordpress/no-unsafe-wp-apis __experimentalVStack as VStack, // eslint-disable-next-line @wordpress/no-unsafe-wp-apis __experimentalText as Text, + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalSpacer as Spacer, + // eslint-disable-next-line @wordpress/no-unsafe-wp-apis + __experimentalView as View, PanelBody, Button, TextControl, + CheckboxControl, } from '@wordpress/components'; import { copy } from '@wordpress/icons'; +import { store as preferencesStore } from '@wordpress/preferences'; /** * Internal dependencies @@ -22,6 +28,9 @@ import { copy } from '@wordpress/icons'; import { postCreateThemeVariation } from '../resolvers'; import ScreenHeader from './screen-header'; +const PREFERENCE_SCOPE = 'create-block-theme'; +const PREFERENCE_KEY = 'create-variation'; + export const CreateVariationPanel = () => { const { createErrorNotice } = useDispatch( noticesStore ); @@ -29,8 +38,32 @@ export const CreateVariationPanel = () => { name: '', } ); + const preference = useSelect( ( select ) => { + const _preference = select( preferencesStore ).get( + PREFERENCE_SCOPE, + PREFERENCE_KEY + ); + return { + saveFonts: _preference?.saveFonts ?? true, + }; + }, [] ); + + const handleTogglePreference = ( key ) => { + setPreference( PREFERENCE_SCOPE, PREFERENCE_KEY, { + ...preference, + [ key ]: ! preference[ key ], + } ); + }; + + const { set: setPreference } = useDispatch( preferencesStore ); + const handleCreateVariationClick = () => { - postCreateThemeVariation( theme.name ) + const variationPreferences = { + name: theme.name, + ...preference, + }; + + postCreateThemeVariation( variationPreferences ) .then( () => { // eslint-disable-next-line no-alert window.alert( @@ -57,29 +90,57 @@ export const CreateVariationPanel = () => { + - + { __( 'Save the Global Styles changes as a theme variation.', 'create-block-theme' ) } -
- - setTheme( { ...theme, name: value } ) - } - /> -
- + + + + + + setTheme( { ...theme, name: value } ) + } + /> + + + handleTogglePreference( 'saveFonts' ) + } + /> + + + + +
); diff --git a/src/resolvers.js b/src/resolvers.js index 21d8f53d..1a9f75ac 100644 --- a/src/resolvers.js +++ b/src/resolvers.js @@ -101,11 +101,11 @@ export async function fetchReadmeData() { } ); } -export async function postCreateThemeVariation( name ) { +export async function postCreateThemeVariation( preferences ) { return apiFetch( { path: '/create-block-theme/v1/create-variation', method: 'POST', - data: { name }, + data: preferences, headers: { 'Content-Type': 'application/json', },