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

Font Library: singularize install font families endpoint #57569

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ public function register_routes() {
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => function () {
return true;},
'permission_callback' => array( $this, 'update_font_library_permissions_check' ),
),
)
);
Expand All @@ -59,7 +58,7 @@ public function register_routes() {
'callback' => array( $this, 'install_fonts' ),
'permission_callback' => array( $this, 'update_font_library_permissions_check' ),
'args' => array(
'font_families' => array(
'font_family_settings' => array(
'required' => true,
'type' => 'string',
'validate_callback' => array( $this, 'validate_install_font_families' ),
Expand Down Expand Up @@ -92,85 +91,61 @@ public function register_routes() {
* @param array $files Files to install.
* @return array $error_messages Array of error messages.
*/
private function get_validation_errors( $font_families, $files ) {
private function get_validation_errors( $font_family_settings, $files ) {
$error_messages = array();

if ( ! is_array( $font_families ) ) {
$error_messages[] = __( 'font_families should be an array of font families.', 'gutenberg' );
if ( ! is_array( $font_family_settings ) ) {
$error_messages[] = __( 'font_family_settings should be a font family definition.', 'gutenberg' );
return $error_messages;
}

// Checks if there is at least one font family.
if ( count( $font_families ) < 1 ) {
$error_messages[] = __( 'font_families should have at least one font family definition.', 'gutenberg' );
if (
! isset( $font_family_settings['slug'] ) ||
! isset( $font_family_settings['name'] ) ||
! isset( $font_family_settings['fontFamily'] )
) {
$error_messages[] = __( 'Font family should have slug, name and fontFamily properties defined.', 'gutenberg' );

return $error_messages;
}

for ( $family_index = 0; $family_index < count( $font_families ); $family_index++ ) {
$font_family = $font_families[ $family_index ];

if (
! isset( $font_family['slug'] ) ||
! isset( $font_family['name'] ) ||
! isset( $font_family['fontFamily'] )
) {
$error_messages[] = sprintf(
// translators: 1: font family index.
__( 'Font family [%s] should have slug, name and fontFamily properties defined.', 'gutenberg' ),
$family_index
);
if ( isset( $font_family_settings['fontFace'] ) ) {
if ( ! is_array( $font_family_settings['fontFace'] ) ) {
$error_messages[] = __( 'Font family should have fontFace property defined as an array.', 'gutenberg' );
}

if ( isset( $font_family['fontFace'] ) ) {
if ( ! is_array( $font_family['fontFace'] ) ) {
$error_messages[] = sprintf(
// translators: 1: font family index.
__( 'Font family [%s] should have fontFace property defined as an array.', 'gutenberg' ),
$family_index
);
continue;
}
if ( count( $font_family_settings['fontFace'] ) < 1 ) {
$error_messages[] = __( 'Font family should have at least one font face definition.', 'gutenberg' );
}

if ( count( $font_family['fontFace'] ) < 1 ) {
$error_messages[] = sprintf(
// translators: 1: font family index.
__( 'Font family [%s] should have at least one font face definition.', 'gutenberg' ),
$family_index
);
}
if ( ! empty( $font_family_settings['fontFace'] ) ) {
for ( $face_index = 0; $face_index < count( $font_family_settings['fontFace'] ); $face_index++ ) {

if ( ! empty( $font_family['fontFace'] ) ) {
for ( $face_index = 0; $face_index < count( $font_family['fontFace'] ); $face_index++ ) {
$font_face = $font_family_settings['fontFace'][ $face_index ];
if ( ! isset( $font_face['fontWeight'] ) || ! isset( $font_face['fontStyle'] ) ) {
$error_messages[] = sprintf(
// translators: font face index.
__( 'Font family Font face [%1$s] should have fontWeight and fontStyle properties defined.', 'gutenberg' ),
$face_index
);
}

$font_face = $font_family['fontFace'][ $face_index ];
if ( ! isset( $font_face['fontWeight'] ) || ! isset( $font_face['fontStyle'] ) ) {
$error_messages[] = sprintf(
// translators: 1: font family index, 2: font face index.
__( 'Font family [%1$s] Font face [%2$s] should have fontWeight and fontStyle properties defined.', 'gutenberg' ),
$family_index,
$face_index
);
}
if ( isset( $font_face['downloadFromUrl'] ) && isset( $font_face['uploadedFile'] ) ) {
$error_messages[] = sprintf(
// translators: font face index.
__( 'Font family Font face [%1$s] should have only one of the downloadFromUrl or uploadedFile properties defined and not both.', 'gutenberg' ),
$face_index
);
}

if ( isset( $font_face['downloadFromUrl'] ) && isset( $font_face['uploadedFile'] ) ) {
if ( isset( $font_face['uploadedFile'] ) ) {
if ( ! isset( $files[ $font_face['uploadedFile'] ] ) ) {
$error_messages[] = sprintf(
// translators: 1: font family index, 2: font face index.
__( 'Font family [%1$s] Font face [%2$s] should have only one of the downloadFromUrl or uploadedFile properties defined and not both.', 'gutenberg' ),
$family_index,
// translators: font face index.
__( 'Font family Font face [%1$s] file is not defined in the request files.', 'gutenberg' ),
$face_index
);
}

if ( isset( $font_face['uploadedFile'] ) ) {
if ( ! isset( $files[ $font_face['uploadedFile'] ] ) ) {
$error_messages[] = sprintf(
// translators: 1: font family index, 2: font face index.
__( 'Font family [%1$s] Font face [%2$s] file is not defined in the request files.', 'gutenberg' ),
$family_index,
$face_index
);
}
}
}
}
}
Expand All @@ -189,9 +164,9 @@ private function get_validation_errors( $font_families, $files ) {
* @return true|WP_Error True if the parameter is valid, WP_Error otherwise.
*/
public function validate_install_font_families( $param, $request ) {
$font_families = json_decode( $param, true );
$files = $request->get_file_params();
$error_messages = $this->get_validation_errors( $font_families, $files );
$font_family_settings = json_decode( $param, true );
$files = $request->get_file_params();
$error_messages = $this->get_validation_errors( $font_family_settings, $files );

if ( empty( $error_messages ) ) {
return true;
Expand Down Expand Up @@ -327,17 +302,15 @@ private function has_write_permission() {
*
* @since 6.5.0
*
* @param array[] $font_families Font families to install.
* @param array[] $font_family_settings Font family definition.
* @return bool Whether the request needs write permissions.
*/
private function needs_write_permission( $font_families ) {
foreach ( $font_families as $font ) {
if ( isset( $font['fontFace'] ) ) {
foreach ( $font['fontFace'] as $face ) {
// If the font is being downloaded from a URL or uploaded, it needs write permissions.
if ( isset( $face['downloadFromUrl'] ) || isset( $face['uploadedFile'] ) ) {
return true;
}
private function needs_write_permission( $font_family_settings ) {
if ( isset( $font_family_settings['fontFace'] ) ) {
foreach ( $font_family_settings['fontFace'] as $face ) {
// If the font is being downloaded from a URL or uploaded, it needs write permissions.
if ( isset( $face['downloadFromUrl'] ) || isset( $face['uploadedFile'] ) ) {
return true;
}
}
}
Expand All @@ -358,28 +331,28 @@ private function needs_write_permission( $font_families ) {
*/
public function install_fonts( $request ) {
// Get new fonts to install.
$fonts_param = $request->get_param( 'font_families' );
$font_family_settings = $request->get_param( 'font_family_settings' );

/*
* As this is receiving form data, the font families are encoded as a string.
* The form data is used because local fonts need to use that format to
* attach the files in the request.
*/
$fonts_to_install = json_decode( $fonts_param, true );
$font_family_settings = json_decode( $font_family_settings, true );

$successes = array();
$errors = array();
$response_status = 200;

if ( empty( $fonts_to_install ) ) {
if ( empty( $font_family_settings ) ) {
$errors[] = new WP_Error(
'no_fonts_to_install',
__( 'No fonts to install', 'gutenberg' )
);
$response_status = 400;
}

if ( $this->needs_write_permission( $fonts_to_install ) ) {
if ( $this->needs_write_permission( $font_family_settings ) ) {
$upload_dir = WP_Font_Library::get_fonts_dir();
if ( ! $this->has_upload_directory() ) {
if ( ! wp_mkdir_p( $upload_dir ) ) {
Expand Down Expand Up @@ -415,15 +388,13 @@ public function install_fonts( $request ) {
}

// Get uploaded files (used when installing local fonts).
$files = $request->get_file_params();
foreach ( $fonts_to_install as $font_data ) {
$font = new WP_Font_Family( $font_data );
$result = $font->install( $files );
if ( is_wp_error( $result ) ) {
$errors[] = $result;
} else {
$successes[] = $result;
}
$files = $request->get_file_params();
$font = new WP_Font_Family( $font_family_settings );
$result = $font->install( $files );
if ( is_wp_error( $result ) ) {
$errors[] = $result;
} else {
$successes[] = $result;
}

$data = array(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
* Internal dependencies
*/
import {
fetchInstallFonts,
fetchInstallFont,
fetchUninstallFonts,
fetchFontCollections,
fetchFontCollection,
Expand All @@ -26,7 +26,7 @@ import {
mergeFontFamilies,
loadFontFaceInBrowser,
getDisplaySrcFromFontFace,
makeFormDataFromFontFamilies,
makeFormDataFromFontFamily,
} from './utils';
import { toggleFont } from './utils/toggleFont';
import getIntersectingFontFaces from './utils/get-intersecting-font-faces';
Expand Down Expand Up @@ -192,19 +192,19 @@ function FontLibraryProvider( { children } ) {
return getActivatedFontsOutline( source )[ slug ] || [];
};

async function installFonts( fonts ) {
async function installFont( font ) {
setIsInstalling( true );
try {
// Prepare formData to install.
const formData = makeFormDataFromFontFamilies( fonts );
const formData = makeFormDataFromFontFamily( font );
// Install the fonts (upload the font files to the server and create the post in the database).
const response = await fetchInstallFonts( formData );
const response = await fetchInstallFont( formData );
const fontsInstalled = response?.successes || [];
// Get intersecting font faces between the fonts we tried to installed and the fonts that were installed
// (to avoid activating a non installed font).
const fontToBeActivated = getIntersectingFontFaces(
fontsInstalled,
fonts
[ font ]
);
// Activate the font families (add the font families to the global styles).
activateCustomFontFamilies( fontToBeActivated );
Expand Down Expand Up @@ -358,7 +358,7 @@ function FontLibraryProvider( { children } ) {
isFontActivated,
getFontFacesActivated,
loadFontFaceAsset,
installFonts,
installFont,
uninstallFont,
toggleActivateFont,
getAvailableFontsOutline,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function FontCollection( { id } ) {
const [ renderConfirmDialog, setRenderConfirmDialog ] = useState(
requiresPermission && ! getGoogleFontsPermissionFromStorage()
);
const { collections, getFontCollection, installFonts } =
const { collections, getFontCollection, installFont } =
useContext( FontLibraryContext );
const selectedCollection = collections.find(
( collection ) => collection.id === id
Expand Down Expand Up @@ -92,6 +92,11 @@ function FontCollection( { id } ) {
setNotice( null );
}, [ id ] );

useEffect( () => {
// If the selected fonts change, reset the selected fonts to install
setFontsToInstall( [] );
}, [ selectedFont ] );

// Reset notice after 5 seconds
useEffect( () => {
if ( notice && notice?.duration !== 0 ) {
Expand Down Expand Up @@ -149,7 +154,7 @@ function FontCollection( { id } ) {
};

const handleInstall = async () => {
const response = await installFonts( fontsToInstall );
const response = await installFont( fontsToInstall[ 0 ] );
const installNotice = getNoticeFromInstallResponse( response );
setNotice( installNotice );
resetFontsToInstall();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { loadFontFaceInBrowser } from './utils';
import { getNoticeFromInstallResponse } from './utils/get-notice-from-response';

function LocalFonts() {
const { installFonts } = useContext( FontLibraryContext );
const { installFont } = useContext( FontLibraryContext );
const [ notice, setNotice ] = useState( null );
const supportedFormats =
ALLOWED_FILE_EXTENSIONS.slice( 0, -1 )
Expand Down Expand Up @@ -147,7 +147,18 @@ function LocalFonts() {
*/
const handleInstall = async ( fontFaces ) => {
const fontFamilies = makeFamiliesFromFaces( fontFaces );
const response = await installFonts( fontFamilies );

if ( fontFamilies.length > 1 ) {
setNotice( {
type: 'error',
message: __(
'Variants from only one font family can be uploaded at a time.'
),
} );
return;
}

const response = await installFont( fontFamilies[ 0 ] );
const installNotice = getNoticeFromInstallResponse( response );
setNotice( installNotice );
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
import apiFetch from '@wordpress/api-fetch';

export async function fetchInstallFonts( data ) {
export async function fetchInstallFont( data ) {
const config = {
path: '/wp/v2/font-families',
method: 'POST',
Expand Down
Loading
Loading