Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions includes/class-create-block-theme-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public function register_rest_routes() {
}

/**
* Add README data and theme.json data to the current theme REST API response.
* Add README data, theme.json data, and normalized theme tags to the current theme REST API response.
*
* @param WP_REST_Response $response The response object.
* @param WP_Theme $theme The theme object.
Expand All @@ -171,10 +171,12 @@ public function add_additional_data_to_theme_response( $response, $theme ) {

$data = $response->get_data();

// Add README data and theme.json data.
// Add README data, theme.json data, and normalized theme tags.
try {
$data['readme'] = CBT_Theme_Readme::get_sections();
$data['theme_json'] = CBT_Theme_JSON_Resolver::get_theme_file_contents();
$active_tags = $current_theme->get( 'Tags' );
$data['theme_tags'] = CBT_Theme_Tags::normalize_tags( is_array( $active_tags ) ? $active_tags : array() );
} catch ( Exception $error ) {
return new WP_Error(
'theme_data_retrieval_failed',
Expand Down
73 changes: 71 additions & 2 deletions includes/create-theme/theme-tags.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,80 @@ public static function theme_tags_list( $theme ) {
$theme['tags-layout'] ?? array(),
$theme['tags-features'] ?? array(),
);
$custom_tags = array_map( 'trim', explode( ',', $theme['tags_custom'] ?? '' ) );
$tags = array_unique( array_merge( $checkbox_tags_merged, $custom_tags ) );
$custom_tags = explode( ',', $theme['tags_custom'] ?? '' );
$tags = self::normalize_tags( array_merge( $checkbox_tags_merged, $custom_tags ) );

return implode( ', ', $tags );
}

/**
* Get mapping of lowercase feature labels and slugs to canonical slugs.
*
* @return array<string, string> Map of lowercase labels and slugs to canonical slugs.
*/
protected static function get_label_to_slug_map() {
static $map = null;
if ( null !== $map ) {
return $map;
}

$map = array();
if ( ! function_exists( 'get_theme_feature_list' ) ) {
return $map;
}

$feature_list = get_theme_feature_list( false );
if ( ! is_array( $feature_list ) ) {
return $map;
}

foreach ( $feature_list as $group ) {
if ( ! is_array( $group ) ) {
continue;
}
foreach ( $group as $slug => $label ) {
$map[ strtolower( $label ) ] = $slug;
}
}

return $map;
}

/**
* Normalize an array or comma-separated list of tags to valid, lowercase WordPress theme slugs.
*
* @param array|string $tags Tags to normalize.
* @return array Normalized list of tag slugs.
*/
public static function normalize_tags( $tags ) {
if ( is_string( $tags ) ) {
$tags = explode( ',', $tags );
}

if ( ! is_array( $tags ) ) {
return array();
}

$label_to_slug = self::get_label_to_slug_map();
$normalized = array();

foreach ( $tags as $tag ) {
if ( ! is_string( $tag ) ) {
continue;
}

$tag = trim( $tag );
if ( '' === $tag ) {
continue;
}

$lower_tag = strtolower( $tag );
$normalized[] = $label_to_slug[ $lower_tag ] ?? sanitize_title( $lower_tag );
}

return array_values( array_unique( array_filter( $normalized ) ) );
}

/**
* Render theme tags form section.
*
Expand Down Expand Up @@ -136,6 +204,7 @@ protected static function is_active_theme_tag( $tag ) {

$tag = strtolower( $tag );
$active_theme_tags = wp_get_theme()->get( 'Tags' );
$active_theme_tags = is_array( $active_theme_tags ) ? self::normalize_tags( $active_theme_tags ) : array();
$default_tags = self::list_default_tags();
$merged_tags = array_unique( array_merge( $default_tags, $active_theme_tags ) );

Expand Down
15 changes: 14 additions & 1 deletion src/editor-sidebar/metadata-editor-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ const ALLOWED_SCREENSHOT_MEDIA_TYPES = [

const WP_MINIMUM_VERSIONS = generateWpVersions( WP_VERSION ); // eslint-disable-line no-undef

const getInitialThemeTags = ( themeData ) => {
if ( Array.isArray( themeData?.theme_tags ) ) {
return themeData.theme_tags.join( ', ' );
}
if ( Array.isArray( themeData?.tags?.raw ) ) {
return themeData.tags.raw.join( ', ' );
}
if ( typeof themeData?.tags?.rendered === 'string' ) {
return themeData.tags.rendered;
}
return '';
};

export const ThemeMetadataEditorModal = ( { onRequestClose } ) => {
const [ theme, setTheme ] = useState( {
name: '',
Expand Down Expand Up @@ -78,7 +91,7 @@ export const ThemeMetadataEditorModal = ( { onRequestClose } ) => {
requires_wp: themeData.requires_wp,
author: themeData.author.raw,
author_uri: themeData.author_uri.raw,
tags_custom: themeData.tags.rendered,
tags_custom: getInitialThemeTags( themeData ),
screenshot: themeData.screenshot,
recommended_plugins: themeData.readme?.recommended_plugins || '',
font_credits: themeData.readme?.fonts || '',
Expand Down
68 changes: 68 additions & 0 deletions tests/test-theme-tags.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/**
* @package Create_Block_Theme
*/
class Test_Create_Block_Theme_Tags extends WP_UnitTestCase {

/**
* Tests that normalize_tags converts display labels to slugs, lowercases custom tags,
* handles comma-separated strings as well as arrays, trims whitespace, and deduplicates.
*/
public function test_normalize_tags() {
$tags = array(
'Blog',
'blog',
' Block Editor Styles ',
'Site Editor',
'RTL-Language-Support',
'translation-ready',
'Food & Drink',
'',
' ',
);

$normalized = CBT_Theme_Tags::normalize_tags( $tags );

$this->assertSame(
array(
'blog',
'block-styles',
'full-site-editing',
'rtl-language-support',
'translation-ready',
'food-and-drink',
),
$normalized
);

// Also verify comma-separated string input.
$string_input = 'Blog, Block Editor Styles, custom-tag';
$this->assertSame(
array(
'blog',
'block-styles',
'custom-tag',
),
CBT_Theme_Tags::normalize_tags( $string_input )
);
}

/**
* Tests that theme_tags_list merges checkbox tags and custom tags, normalizes them,
* removes duplicates, and formats the output as a comma-separated string.
*/
public function test_theme_tags_list() {
$theme = array(
'tags-subject' => array( 'Blog' ),
'tags-features' => array( 'Block Editor Styles' ),
'tags_custom' => 'blog, RTL-Language-Support, translation-ready, Site Editor',
);

$tags_list = CBT_Theme_Tags::theme_tags_list( $theme );

$this->assertSame(
'blog, block-styles, rtl-language-support, translation-ready, full-site-editing',
$tags_list
);
}
}
Loading