diff --git a/lib/experimental/fonts/font-library/font-library.php b/lib/experimental/fonts/font-library/font-library.php index e6c5f92da58a0a..ae3ad144f837f7 100644 --- a/lib/experimental/fonts/font-library/font-library.php +++ b/lib/experimental/fonts/font-library/font-library.php @@ -245,3 +245,84 @@ function _wp_before_delete_font_face( $post_id, $post ) { } add_action( 'before_delete_post', '_wp_before_delete_font_face', 10, 2 ); } + +// @core-merge: Do not merge this back compat function, it is for supporting a legacy font family format only in Gutenberg. +/** + * Convert legacy font family posts to the new format. + * + * @return void + */ +function gutenberg_convert_legacy_font_family_format() { + if ( get_option( 'gutenberg_font_family_format_converted' ) ) { + return; + } + + $font_families = new WP_Query( + array( + 'post_type' => 'wp_font_family', + // Set a maximum, but in reality there will be far less than this. + 'posts_per_page' => 999, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + ) + ); + + foreach ( $font_families->get_posts() as $font_family ) { + $already_converted = get_post_meta( $font_family->ID, '_gutenberg_legacy_font_family', true ); + if ( $already_converted ) { + continue; + } + + // Stash the old font family content in a meta field just in case we need it. + update_post_meta( $font_family->ID, '_gutenberg_legacy_font_family', $font_family->post_content ); + + $font_family_json = json_decode( $font_family->post_content, true ); + if ( ! $font_family_json ) { + continue; + } + + $font_faces = $font_family_json['fontFace'] ?? array(); + unset( $font_family_json['fontFace'] ); + + // Save wp_font_face posts within the family. + foreach ( $font_faces as $font_face ) { + $args = array(); + $args['post_type'] = 'wp_font_face'; + $args['post_title'] = WP_Font_Family_Utils::get_font_face_slug( $font_face ); + $args['post_name'] = sanitize_title( $args['post_title'] ); + $args['post_status'] = 'publish'; + $args['post_parent'] = $font_family->ID; + $args['post_content'] = wp_json_encode( $font_face ); + + $font_face_id = wp_insert_post( wp_slash( $args ) ); + + $file_urls = (array) $font_face['src'] ?? array(); + + foreach ( $file_urls as $file_url ) { + // continue if the file is not local. + if ( false === strpos( $file_url, site_url() ) ) { + continue; + } + + $relative_path = basename( $file_url ); + update_post_meta( $font_face_id, '_wp_font_face_file', $relative_path ); + } + } + + // Update the font family post to remove the font face data. + $args = array(); + $args['ID'] = $font_family->ID; + $args['post_title'] = $font_family_json['name'] ?? ''; + $args['post_name'] = sanitize_title( $font_family_json['slug'] ); + + unset( $font_family_json['name'] ); + unset( $font_family_json['slug'] ); + + $args['post_content'] = wp_json_encode( $font_family_json ); + + wp_update_post( wp_slash( $args ) ); + } + + update_option( 'gutenberg_font_family_format_converted', true ); +} +add_action( 'init', 'gutenberg_convert_legacy_font_family_format' ); diff --git a/phpunit/tests/fonts/font-library/fontFamilyBackwardsCompatibility.php b/phpunit/tests/fonts/font-library/fontFamilyBackwardsCompatibility.php new file mode 100644 index 00000000000000..a971bd51234305 --- /dev/null +++ b/phpunit/tests/fonts/font-library/fontFamilyBackwardsCompatibility.php @@ -0,0 +1,164 @@ +create_font_family( $legacy_content ); + + gutenberg_convert_legacy_font_family_format(); + + $font_family = get_post( $font_family_id ); + $font_faces = $this->get_font_faces( $font_family_id ); + + list( $font_face1, $font_face2, $font_face3 ) = $font_faces; + + // Updated font family post. + $this->assertSame( 'wp_font_family', $font_family->post_type ); + $this->assertSame( 'publish', $font_family->post_status ); + + $font_family_title = 'Open Sans'; + $this->assertSame( $font_family_title, $font_family->post_title ); + + $font_family_slug = 'open-sans'; + $this->assertSame( $font_family_slug, $font_family->post_name ); + + $font_family_content = wp_json_encode( json_decode( '{"fontFamily":"\'Open Sans\', sans-serif","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans.svg"}', true ) ); + $this->assertSame( $font_family_content, $font_family->post_content ); + + $meta = get_post_meta( $font_family_id, '_gutenberg_legacy_font_family', true ); + $this->assertSame( $legacy_content, $meta ); + + // First font face post. + $this->assertSame( 'wp_font_face', $font_face1->post_type ); + $this->assertSame( $font_family_id, $font_face1->post_parent ); + $this->assertSame( 'publish', $font_face1->post_status ); + + $font_face1_title = 'open sans;normal;400;100%;U+0-10FFFF'; + $this->assertSame( $font_face1_title, $font_face1->post_title ); + $this->assertSame( sanitize_title( $font_face1_title ), $font_face1->post_name ); + + $font_face1_content = wp_json_encode( json_decode( '{"fontFamily":"Open Sans","fontStyle":"normal","fontWeight":"400","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-400-normal.svg","src":"https://fonts.gstatic.com/s/opensans/v35/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsjZ0C4nY1M2xLER.ttf"}' ) ); + $this->assertSame( $font_face1_content, $font_face1->post_content ); + + // With a remote url, file post meta should not be set. + $meta = get_post_meta( $font_face1->ID, '_wp_font_face_file', true ); + $this->assertSame( '', $meta ); + + // Second font face post. + $this->assertSame( 'wp_font_face', $font_face2->post_type ); + $this->assertSame( $font_family_id, $font_face2->post_parent ); + $this->assertSame( 'publish', $font_face2->post_status ); + + $font_face2_title = 'open sans;italic;400;100%;U+0-10FFFF'; + $this->assertSame( $font_face2_title, $font_face2->post_title ); + $this->assertSame( sanitize_title( $font_face2_title ), $font_face2->post_name ); + + $font_face2_content = wp_json_encode( json_decode( '{"fontFamily":"Open Sans","fontStyle":"italic","fontWeight":"400","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-400-italic.svg","src":"https://fonts.gstatic.com/s/opensans/v35/memQYaGs126MiZpBA-UFUIcVXSCEkx2cmqvXlWq8tWZ0Pw86hd0Rk8ZkaVcUwaERZjA.ttf"}' ) ); + $this->assertSame( $font_face2_content, $font_face2->post_content ); + + // With a remote url, file post meta should not be set. + $meta = get_post_meta( $font_face2->ID, '_wp_font_face_file', true ); + $this->assertSame( '', $meta ); + + // Third font face post. + $this->assertSame( 'wp_font_face', $font_face3->post_type ); + $this->assertSame( $font_family_id, $font_face3->post_parent ); + $this->assertSame( 'publish', $font_face3->post_status ); + + $font_face3_title = 'open sans;normal;700;100%;U+0-10FFFF'; + $this->assertSame( $font_face3_title, $font_face3->post_title ); + $this->assertSame( sanitize_title( $font_face3_title ), $font_face3->post_name ); + + $font_face3_content = wp_json_encode( json_decode( '{"fontFamily":"Open Sans","fontStyle":"normal","fontWeight":"700","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-700-normal.svg","src":"https://fonts.gstatic.com/s/opensans/v35/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsg-1y4nY1M2xLER.ttf"}' ) ); + $this->assertSame( $font_face3_content, $font_face3->post_content ); + + // With a remote url, file post meta should not be set. + $meta = get_post_meta( $font_face3->ID, '_wp_font_face_file', true ); + $this->assertSame( '', $meta ); + + wp_delete_post( $font_family_id, true ); + wp_delete_post( $font_face1->ID, true ); + wp_delete_post( $font_face2->ID, true ); + wp_delete_post( $font_face3->ID, true ); + } + + public function test_font_faces_with_local_src() { + $legacy_content = '{"fontFace":[{"fontFamily":"Open Sans","fontStyle":"normal","fontWeight":"400","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans-400-normal.svg","src":"' . site_url() . '/wp-content/fonts/open-sans_normal_400.ttf"}],"fontFamily":"\'Open Sans\', sans-serif","name":"Open Sans","preview":"https://s.w.org/images/fonts/16.7/previews/open-sans/open-sans.svg","slug":"open-sans"}'; + + $font_family_id = $this->create_font_family( $legacy_content ); + + gutenberg_convert_legacy_font_family_format(); + + $font_faces = $this->get_font_faces( $font_family_id ); + $this->assertCount( 1, $font_faces ); + $font_face = reset( $font_faces ); + + // Check that file meta is present. + $file_path = 'open-sans_normal_400.ttf'; + $meta = get_post_meta( $font_face->ID, '_wp_font_face_file', true ); + $this->assertSame( $file_path, $meta ); + + wp_delete_post( $font_family_id, true ); + wp_delete_post( $font_face->ID, true ); + } + + public function test_migration_only_runs_once() { + $legacy_content = '{"fontFace":[],"fontFamily":"\'Open Sans\', sans-serif","name":"Open Sans","preview":"","slug":"open-sans"}'; + + // Simulate that the migration has already run. + update_option( 'gutenberg_font_family_format_converted', true ); + + $font_family_id = $this->create_font_family( $legacy_content ); + + gutenberg_convert_legacy_font_family_format(); + + // Meta with backup content will not be present if migration isn't triggered. + $meta = get_post_meta( $font_family_id, '_gutenberg_legacy_font_family', true ); + $this->assertSame( '', $meta ); + + wp_delete_post( $font_family_id, true ); + } + + protected function create_font_family( $content ) { + return wp_insert_post( + array( + 'post_type' => 'wp_font_family', + 'post_status' => 'publish', + 'post_title' => 'Open Sans', + 'post_name' => 'open-sans', + 'post_content' => $content, + ) + ); + } + + protected function get_font_faces( $font_family_id ) { + return get_posts( + array( + 'post_parent' => $font_family_id, + 'post_type' => 'wp_font_face', + 'order' => 'ASC', + 'orderby' => 'id', + ) + ); + } +}