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 Families REST API endpoint: ensure unique font family slugs #57861

Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -381,20 +381,7 @@ public function prepare_item_for_response( $item, $request ) { // phpcs:ignore V
$data['id'] = $item->ID;
$data['theme_json_version'] = 2;
$data['parent'] = $item->post_parent;

$settings = json_decode( $item->post_content, true );
$properties = $this->get_item_schema()['properties']['font_face_settings']['properties'];

// Provide required, empty settings if the post_content is not valid JSON.
if ( null === $settings ) {
$settings = array(
'fontFamily' => '',
'src' => array(),
);
}

// Only return the properties defined in the schema.
$data['font_face_settings'] = array_intersect_key( $settings, $properties );
$data['font_face_settings'] = $this->get_settings_from_post( $item );

$response = rest_ensure_response( $data );
$links = $this->prepare_links( $item );
Expand Down Expand Up @@ -748,4 +735,28 @@ protected function relative_fonts_path( $path ) {

return $new_path;
}

/**
* Gets the font face's settings from the post.
*
* @since 6.5.0
*
* @param WP_Post $post Font face post object.
* @return array Font face settings array.
*/
protected function get_settings_from_post( $post ) {
$settings = json_decode( $post->post_content, true );
$properties = $this->get_item_schema()['properties']['font_face_settings']['properties'];

// Provide required, empty settings if needed.
if ( null === $settings ) {
$settings = array(
'src' => array(),
);
}
$settings['fontFamily'] = $post->post_title ?? '';

// Only return the properties defined in the schema.
return array_intersect_key( $settings, $properties );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,18 @@ public function validate_font_family_settings( $value, $request ) {
$schema = $this->get_item_schema()['properties']['font_family_settings'];
$required = $schema['required'];

// Allow setting individual properties if we are updating an existing font family.
if ( isset( $request['id'] ) ) {
// Allow sending individual properties if we are updating an existing font family.
unset( $schema['required'] );

// But don't allow updating the slug, since it is used as a unique identifier.
if ( isset( $settings['slug'] ) ) {
return new WP_Error(
'rest_invalid_param',
__( 'font_family_settings[slug] cannot be updated.', 'gutenberg' ),
array( 'status' => 400 )
);
}
}

// Check that the font face settings match the theme.json schema.
Expand Down Expand Up @@ -192,6 +201,37 @@ public function sanitize_font_family_settings( $value ) {
return $settings;
}

/**
* Creates a single font family.
*
* @since 6.5.0
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function create_item( $request ) {
$settings = $request->get_param( 'font_family_settings' );

// Check that the font family slug is unique.
$existing_font_family = get_posts(
array(
'post_type' => $this->post_type,
'posts_per_page' => 1,
'name' => $settings['slug'],
)
);
if ( ! empty( $existing_font_family ) ) {
return new WP_Error(
'rest_duplicate_font_family',
/* translators: %s: Font family slug. */
sprintf( __( 'A font family with slug "%s" already exists.', 'gutenberg' ), $settings['slug'] ),
array( 'status' => 400 )
);
}

return parent::create_item( $request );
}

/**
* Deletes a single font family.
*
Expand Down Expand Up @@ -237,25 +277,10 @@ public function delete_item( $request ) {
public function prepare_item_for_response( $item, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- required by parent class
$data = array();

$data['id'] = $item->ID;
$data['theme_json_version'] = 2;
$data['font_faces'] = $this->get_font_face_ids( $item->ID );

$settings = json_decode( $item->post_content, true );
$properties = $this->get_item_schema()['properties']['font_family_settings']['properties'];

// Provide empty settings if the post_content is not valid JSON.
if ( null === $settings ) {
$settings = array(
'name' => '',
'slug' => '',
'fontFamily' => '',
'preview' => '',
);
}

// Only return the properties defined in the schema.
$data['font_family_settings'] = array_intersect_key( $settings, $properties );
$data['id'] = $item->ID;
$data['theme_json_version'] = 2;
$data['font_faces'] = $this->get_font_face_ids( $item->ID );
$data['font_family_settings'] = $this->get_settings_from_post( $item );

$response = rest_ensure_response( $data );
$links = $this->prepare_links( $item );
Expand Down Expand Up @@ -349,6 +374,7 @@ public function get_collection_params() {
'page' => $params['page'],
'per_page' => $params['per_page'],
'search' => $params['search'],
'slug' => $params['slug'],
);
}

Expand Down Expand Up @@ -458,26 +484,52 @@ protected function prepare_font_face_links( $font_family_id ) {
*/
protected function prepare_item_for_database( $request ) {
$prepared_post = new stdClass();
// Settings have already been decoded by sanitize_font_family_settings().
// Settings have already been decoded by ::sanitize_font_family_settings().
$settings = $request->get_param( 'font_family_settings' );

// This is an update and we merge with the existing font family.
if ( isset( $request['id'] ) ) {
$existing_post = $this->get_post( $request['id'] );
if ( is_wp_error( $existing_post ) ) {
return $existing_post;
}

$prepared_post->ID = $existing_post->ID;
$existing_settings = json_decode( $existing_post->post_content, true );
$existing_settings = $this->get_settings_from_post( $existing_post );
$settings = array_merge( $existing_settings, $settings );
}

$prepared_post->post_type = $this->post_type;
$prepared_post->post_status = 'publish';
$prepared_post->post_title = $settings['name'];
$prepared_post->post_name = sanitize_title( $settings['slug'] );
$prepared_post->post_type = $this->post_type;
$prepared_post->post_status = 'publish';
$prepared_post->post_title = $settings['name'];
$prepared_post->post_name = sanitize_title( $settings['slug'] );

// Remove duplicate information from settings.
unset( $settings['name'] );
unset( $settings['slug'] );

$prepared_post->post_content = wp_json_encode( $settings );

return $prepared_post;
}

/**
* Gets the font family's settings from the post.
*
* @since 6.5.0
*
* @param WP_Post $post Font family post object.
* @return array Font family settings array.
*/
protected function get_settings_from_post( $post ) {
$settings_json = json_decode( $post->post_content, true );

// Default to empty strings if the settings are missing.
return array(
'name' => $post->post_title ?? '',
'slug' => $post->post_name ?? '',
'fontFamily' => $settings_json['fontFamily'] ?? '',
'preview' => $settings_json['preview'] ?? '',
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ public function test_get_item_malformed_post_content_returns_empty_settings() {
);

$empty_settings = array(
'fontFamily' => '',
'src' => array(),
'fontFamily' => '',
);

wp_set_current_user( self::$admin_id );
Expand Down
Loading
Loading