Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c8072d4
Font Library: add wp_font_face post type and scaffold font face REST …
creativecoder Jan 9, 2024
61da35c
Font Library: create font faces through the REST API (#57702)
creativecoder Jan 11, 2024
42565a3
Refactor Font Family Controller (#57785)
creativecoder Jan 12, 2024
afacdf2
Font Family and Font Face REST API endpoints: better data handling an…
creativecoder Jan 15, 2024
7c82cb9
Font Families REST API endpoint: ensure unique font family slugs (#57…
creativecoder Jan 16, 2024
f84cc6d
Font Library: delete child font faces and font assets when deleting p…
creativecoder Jan 16, 2024
492a3ee
Font Library: refactor client side install functions to work with rev…
jffng Jan 17, 2024
9ebc25f
Cleanup/font library view error handling (#57926)
pbking Jan 17, 2024
92ff955
Fix unique key prop warning when opening modal
mikachan Jan 17, 2024
5b57f3e
Add key props to FontsGrid children
mikachan Jan 17, 2024
8a5efd4
Font Faces endpoint: prevent creating font faces with duplicate setti…
creativecoder Jan 17, 2024
3a739bc
Font Library: Update uninstall/delete on client side (#57932)
mikachan Jan 18, 2024
22253e5
Add slug/id back to FontCollection
mikachan Jan 18, 2024
c646e3d
Change tabsFromCollections inline with Font Collections PR
mikachan Jan 18, 2024
2d3abce
Use child.key for key prop in FontsGrid
mikachan Jan 18, 2024
c6e0fbb
Update packages/edit-site/src/components/global-styles/font-library-m…
mikachan Jan 18, 2024
74be330
Merge branch 'trunk' into try/font-library-refactor
mikachan Jan 18, 2024
a666bb5
Font Library: address JS feedback in #57688 (#57961)
mikachan Jan 18, 2024
32689e1
Font Library REST API endpoints: address initial feedback from featur…
creativecoder Jan 19, 2024
2aa9c64
Font Library: font collection refactor to use the new schema (#57884)
matiasbenedetto Jan 19, 2024
fe2c0ae
Merge branch 'try/font-library-refactor' into font-library/fix-react-…
mikachan Jan 19, 2024
ab889a7
Merge branch 'try/font-library-refactor' into font-library/fix-react-…
mikachan Jan 23, 2024
75c27e5
Remove old WP_REST_Autosave_Fonts_Controller class
mikachan Jan 23, 2024
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

Large diffs are not rendered by default.

Large diffs are not rendered by default.

114 changes: 113 additions & 1 deletion lib/experimental/fonts/font-library/font-library.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,74 @@
function gutenberg_init_font_library_routes() {
// @core-merge: This code will go into Core's `create_initial_post_types()`.
$args = array(
'labels' => array(
'name' => __( 'Font Families', 'gutenberg' ),
'singular_name' => __( 'Font Family', 'gutenberg' ),
),
'public' => false,
'_builtin' => true, /* internal use only. don't use this when registering your own post type. */
'label' => 'Font Family',
'show_in_rest' => true,
'capabilities' => array(
'read' => 'edit_theme_options',
'read_post' => 'edit_theme_options',
'read_private_posts' => 'edit_theme_options',
'create_posts' => 'edit_theme_options',
'publish_posts' => 'edit_theme_options',
'edit_post' => 'edit_theme_options',
'edit_posts' => 'edit_theme_options',
'edit_others_posts' => 'edit_theme_options',
'edit_published_posts' => 'edit_theme_options',
'delete_post' => 'edit_theme_options',
'delete_posts' => 'edit_theme_options',
'delete_others_posts' => 'edit_theme_options',
'delete_published_posts' => 'edit_theme_options',
),
'map_meta_cap' => false,
'rest_base' => 'font-families',
'rest_controller_class' => 'WP_REST_Font_Families_Controller',
'autosave_rest_controller_class' => 'WP_REST_Autosave_Font_Families_Controller',
'query_var' => false,
);
register_post_type( 'wp_font_family', $args );

register_post_type(
'wp_font_face',
array(
'labels' => array(
'name' => __( 'Font Faces', 'gutenberg' ),
'singular_name' => __( 'Font Face', 'gutenberg' ),
),
'public' => false,
'_builtin' => true, /* internal use only. don't use this when registering your own post type. */
'hierarchical' => false,
'show_in_rest' => false,
'rest_base' => 'font-faces',
'capabilities' => array(
'read' => 'edit_theme_options',
'read_post' => 'edit_theme_options',
'read_private_posts' => 'edit_theme_options',
'create_posts' => 'edit_theme_options',
'publish_posts' => 'edit_theme_options',
'edit_post' => 'edit_theme_options',
'edit_posts' => 'edit_theme_options',
'edit_others_posts' => 'edit_theme_options',
'edit_published_posts' => 'edit_theme_options',
'delete_post' => 'edit_theme_options',
'delete_posts' => 'edit_theme_options',
'delete_others_posts' => 'edit_theme_options',
'delete_published_posts' => 'edit_theme_options',
),
'map_meta_cap' => false,
'query_var' => false,
)
);

// @core-merge: This code will go into Core's `create_initial_rest_routes()`.
$font_collections_controller = new WP_REST_Font_Collections_Controller();
$font_collections_controller->register_routes();

$font_faces_controller = new WP_REST_Font_Faces_Controller();
$font_faces_controller->register_routes();
}

add_action( 'rest_api_init', 'gutenberg_init_font_library_routes' );
Expand Down Expand Up @@ -132,3 +187,60 @@ function wp_get_font_dir( $defaults = array() ) {
return apply_filters( 'font_dir', $defaults );
}
}

// @core-merge: Filters should go in `src/wp-includes/default-filters.php`,
// functions in a general file for font library.
if ( ! function_exists( '_wp_delete_font_family' ) ) {
/**
* Deletes child font faces when a font family is deleted.
*
* @access private
* @since 6.5.0
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @return void
*/
function _wp_delete_font_family( $post_id, $post ) {
if ( 'wp_font_family' !== $post->post_type ) {
return;
}

$font_faces = get_children(
array(
'post_parent' => $post_id,
'post_type' => 'wp_font_face',
)
);

foreach ( $font_faces as $font_face ) {
wp_delete_post( $font_face->ID, true );
}
}
add_action( 'deleted_post', '_wp_delete_font_family', 10, 2 );
}

if ( ! function_exists( '_wp_delete_font_face' ) ) {
/**
* Deletes associated font files when a font face is deleted.
*
* @access private
* @since 6.5.0
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @return void
*/
function _wp_delete_font_face( $post_id, $post ) {
if ( 'wp_font_face' !== $post->post_type ) {
return;
}

$font_files = get_post_meta( $post_id, '_wp_font_face_files', false );

foreach ( $font_files as $font_file ) {
wp_delete_file( wp_get_font_dir()['path'] . '/' . $font_file );
}
}
add_action( 'before_delete_post', '_wp_delete_font_face', 10, 2 );
}
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family-utils.php';
require __DIR__ . '/experimental/fonts/font-library/class-wp-font-family.php';
require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-families-controller.php';
require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-faces-controller.php';
require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-font-collections-controller.php';
require __DIR__ . '/experimental/fonts/font-library/class-wp-rest-autosave-font-families-controller.php';
require __DIR__ . '/experimental/fonts/font-library/font-library.php';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import {
useEntityRecords,
store as coreStore,
} from '@wordpress/core-data';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import {
fetchInstallFont,
fetchGetFontFamilyBySlug,
fetchInstallFontFamily,
fetchUninstallFonts,
fetchFontCollections,
fetchFontCollection,
Expand All @@ -26,10 +28,12 @@ import {
mergeFontFamilies,
loadFontFaceInBrowser,
getDisplaySrcFromFontFace,
makeFormDataFromFontFamily,
makeFontFacesFormData,
makeFontFamilyFormData,
batchInstallFontFaces,
checkFontFaceInstalled,
} from './utils';
import { toggleFont } from './utils/toggleFont';
import getIntersectingFontFaces from './utils/get-intersecting-font-faces';

export const FontLibraryContext = createContext( {} );

Expand Down Expand Up @@ -60,12 +64,19 @@ function FontLibraryProvider( { children } ) {
records: libraryPosts = [],
isResolving: isResolvingLibrary,
hasResolved: hasResolvedLibrary,
} = useEntityRecords( 'postType', 'wp_font_family', { refreshKey } );
} = useEntityRecords( 'postType', 'wp_font_family', {
refreshKey,
_embed: true,
} );

const libraryFonts =
( libraryPosts || [] ).map( ( post ) =>
JSON.parse( post.content.raw )
) || [];
( libraryPosts || [] ).map( ( post ) => {
post.font_family_settings.fontFace =
post?._embedded?.font_faces.map(
( face ) => face.font_face_settings
) || [];
return post.font_family_settings;
} ) || [];

// Global Styles (settings) font families
const [ fontFamilies, setFontFamilies ] = useGlobalSetting(
Expand Down Expand Up @@ -192,35 +203,96 @@ function FontLibraryProvider( { children } ) {
return getActivatedFontsOutline( source )[ slug ] || [];
};

async function installFont( font ) {
async function installFont( fontFamilyToInstall ) {
setIsInstalling( true );
try {
// Prepare formData to install.
const formData = makeFormDataFromFontFamily( font );
// Get the font family if it already exists.
let installedFontFamily = await fetchGetFontFamilyBySlug(
fontFamilyToInstall.slug
);

// Otherwise create it.
if ( ! installedFontFamily ) {
// Prepare font family form data to install.
installedFontFamily = await fetchInstallFontFamily(
makeFontFamilyFormData( fontFamilyToInstall )
);
}

// Collect font faces that have already been installed (to be activated later)
const alreadyInstalledFontFaces =
installedFontFamily.fontFace.filter( ( fontFaceToInstall ) =>
checkFontFaceInstalled(
fontFaceToInstall,
fontFamilyToInstall.fontFace
)
);

// Filter out Font Faces that have already been installed (so that they are not re-installed)
fontFamilyToInstall.fontFace = fontFamilyToInstall.fontFace.filter(
( fontFaceToInstall ) =>
! checkFontFaceInstalled(
fontFaceToInstall,
installedFontFamily.fontFace
)
);

// Install the fonts (upload the font files to the server and create the post in the database).
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,
[ font ]
let sucessfullyInstalledFontFaces = [];
let unsucessfullyInstalledFontFaces = [];
if ( fontFamilyToInstall.fontFace.length > 0 ) {
const response = await batchInstallFontFaces(
installedFontFamily.id,
makeFontFacesFormData( fontFamilyToInstall )
);
sucessfullyInstalledFontFaces = response?.successes;
unsucessfullyInstalledFontFaces = response?.errors;
}

const detailedErrorMessage = unsucessfullyInstalledFontFaces.reduce(
( errorMessageCollection, error ) => {
return `${ errorMessageCollection } ${ error.message }`;
},
''
);
// Activate the font families (add the font families to the global styles).
activateCustomFontFamilies( fontToBeActivated );

// If there were no successes and nothing already installed then we don't need to activate anything and can bounce now.
if (
sucessfullyInstalledFontFaces.length === 0 &&
alreadyInstalledFontFaces.length === 0
) {
throw new Error(
__( 'No font faces were installed. ' ) +
detailedErrorMessage
);
}

// Use the sucessfully installed font faces
// As well as any font faces that were already installed (those will be activated)
fontFamilyToInstall.fontFace = [
...sucessfullyInstalledFontFaces,
...alreadyInstalledFontFaces,
];

// Activate the font family (add the font family to the global styles).
activateCustomFontFamilies( [ fontFamilyToInstall ] );

// Save the global styles to the database.
saveSpecifiedEntityEdits( 'root', 'globalStyles', globalStylesId, [
'settings.typography.fontFamilies',
] );

refreshLibrary();
setIsInstalling( false );

return response;
} catch ( error ) {
if ( unsucessfullyInstalledFontFaces.length > 0 ) {
throw new Error(
__(
'Some font faces were installed. There were some errors. '
) + detailedErrorMessage
);
}
} finally {
setIsInstalling( false );
return {
errors: [ error ],
};
}
}

Expand All @@ -239,7 +311,7 @@ function FontLibraryProvider( { children } ) {
[ 'settings.typography.fontFamilies' ]
);
}
// Refresh the library (the the library font families from database).
// Refresh the library (the library font families from database).
refreshLibrary();
return response;
} catch ( error ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import CollectionFontDetails from './collection-font-details';
import { toggleFont } from './utils/toggleFont';
import { getFontsOutline } from './utils/fonts-outline';
import GoogleFontsConfirmDialog from './google-fonts-confirm-dialog';
import { getNoticeFromInstallResponse } from './utils/get-notice-from-response';
import { downloadFontFaceAsset } from './utils';

const DEFAULT_CATEGORY = {
Expand Down Expand Up @@ -182,9 +181,18 @@ function FontCollection( { id } ) {
return;
}

const response = await installFont( fontFamily );
const installNotice = getNoticeFromInstallResponse( response );
setNotice( installNotice );
try {
await installFont( fontFamily );
setNotice( {
type: 'success',
message: __( 'Fonts were installed successfully.' ),
} );
} catch ( error ) {
setNotice( {
type: 'error',
message: error.message,
} );
}
resetFontsToInstall();
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ function FontsGrid( { title, children, pageSize = 32 } ) {
<div className="font-library-modal__fonts-grid__main">
{ items.map( ( child, i ) => {
if ( i === itemsLimit - 1 ) {
return <div ref={ setLastItem }>{ child }</div>;
return (
<div key={ i } ref={ setLastItem }>
{ child }
</div>
);
}
return child;
return <div key={ i }>{ child }</div>;
} ) }
</div>
</VStack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ const DEFAULT_TABS = [
];

const tabsFromCollections = ( collections ) =>
collections.map( ( { id, name } ) => ( {
id,
collections.map( ( { slug, name } ) => ( {
slug,
title:
collections.length === 1 && id === 'default-font-collection'
collections.length === 1 && slug === 'default-font-collection'
? __( 'Install Fonts' )
: name,
id: slug,
} ) );

function FontLibraryModal( {
Expand Down Expand Up @@ -76,7 +77,7 @@ function FontLibraryModal( {
contents = <InstalledFonts />;
break;
default:
contents = <FontCollection id={ id } />;
contents = <FontCollection />;
}
return (
<Tabs.TabPanel
Expand Down
Loading