strategy = $strategy; } /** * Initializes the REST API endpoints. */ public function init() { add_action('rest_api_init', function () { register_rest_route('theme/v1', '/fonts', array( 'methods' => 'GET', 'callback' => [ $this, 'get_fonts' ], 'permission_callback' => function () { return current_user_can( 'edit_theme_options' ); }, )); register_rest_route('theme/v1', '/fonts', array( 'methods' => 'POST', 'callback' => [ $this, 'set_fonts' ], 'permission_callback' => function () { return current_user_can('edit_theme_options'); }, )); }); } /** * Retrieves the fonts using the current strategy. * * @return WP_REST_Response The response containing the fonts. */ public function get_fonts() { $fonts = $this->strategy->get_fonts(); // Filter fonts not installed on the server. $fonts = $this->filter_uninstalled_fonts( $fonts ); $fonts = $this->filter_empty_fonts( $fonts ); return new WP_REST_Response( array_values( $fonts ), 200 ); } /** * Filters out fonts that are not installed on the server. * This could happen when a font is deleted when the theme is not active. * * @param array $fonts The fonts to filter. * * @return array The filtered fonts. */ public function filter_uninstalled_fonts( $fonts ) { $installed_slugs = $this->get_installed_fonts(); $fonts = array_filter( $fonts, function( $font ) use ( $installed_slugs ) { return in_array( $font['slug'], $installed_slugs ); } ); return array_values( $fonts ); } /** * Retrieves the installed fonts. * * @return array The installed font slugs. */ public function get_installed_fonts() { $page = 1; $per_page = 100; $slugs = []; do { $installed_fonts = tve_send_wp_rest_request( '/wp/v2/font-families', [ 'per_page' => $per_page, 'page' => $page ] ); $slugs = array_merge( $slugs, array_map( function( $font ) { return $font['font_family_settings']['slug'] ?? ''; }, $installed_fonts ) ); $page++; // Just a safety measure to avoid infinite loops. This should never happen. if ( $page > 10 ) { break; } } while ( count( $installed_fonts ) === $per_page ); // If the count is less than the per_page, we've reached the end. return $slugs; } /** * Sets the fonts using the current strategy. * * @param WP_REST_Request $request The request containing the fonts to set. * @return WP_REST_Response The response after setting the fonts. */ public function set_fonts( $request ) { $fonts = $request['fonts']; if ( ! is_array( $fonts ) ) { return new WP_REST_Response( 'Invalid request', 400 ); } // Merge the existing fonts with the new fonts. Make sure they're unique by the slug key and the current fonts will override the existing ones. $existing_fonts = $this->strategy->get_fonts(); $fonts = array_merge( $existing_fonts, $fonts ); // Make the fonts unique by slug and remove empty fontFaces. $fonts = $this->filter_duplicate_fonts( $fonts ); $fonts = $this->filter_empty_fonts( $fonts ); $result = $this->strategy->set_fonts( $fonts ); return new WP_REST_Response( $result, 200 ); } /** * Filters out fonts with empty fontFaces. * * @param array $fonts The fonts to filter. * * @return array The filtered fonts. */ public function filter_empty_fonts( $fonts ) { $fonts = array_filter( $fonts, function( $font ) { return ! empty( $font[ 'fontFace' ] ); } ); return array_values( $fonts ); } /** * Filters out duplicate fonts. * * @param array $fonts The fonts to filter. * * @return array The filtered fonts. */ public function filter_duplicate_fonts( $fonts ) { $fonts = array_reduce( $fonts, function( $acc, $font ) { $acc[ $font['slug'] ] = $font; return $acc; }, [] ); return array_values( $fonts ); } }