Skip to content

Commit

Permalink
Font Library: singularize install font families endpoint (#57569)
Browse files Browse the repository at this point in the history
* singularize install font families endpoint to accept only one font family instead of many in the same request

* lint php

* frontend changes to send only one font family per install request

* restrict the upload of local font files to variants of only one font family

* rename function to make it singular

* fixing strings for translations

* fix permission_callback value updated by mistake
  • Loading branch information
matiasbenedetto authored Jan 8, 2024
1 parent 302c148 commit 6d4e6dd
Show file tree
Hide file tree
Showing 9 changed files with 258 additions and 368 deletions.
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 @@ -29,7 +29,7 @@ import { unlock } from '../../../lock-unlock';
const { ProgressBar } = unlock( componentsPrivateApis );

function LocalFonts() {
const { installFonts } = useContext( FontLibraryContext );
const { installFont } = useContext( FontLibraryContext );
const [ notice, setNotice ] = useState( null );
const [ isUploading, setIsUploading ] = useState( false );
const supportedFormats =
Expand Down Expand Up @@ -153,7 +153,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 );
setIsUploading( false );
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

0 comments on commit 6d4e6dd

Please sign in to comment.