mirror of
https://github.com/WordPress/create-block-theme.git
synced 2025-10-03 16:11:13 +08:00
Refactor Theme_Readme (readme.txt) PHP class (#626)
* refactor ThemeReadme class * update phpunit tests config to make it more similar to core * Add tests for ThemeReadme class public methods * simplify copyright text creation * Fix cloned theme reference to original theme * updates comment * Theme_Reade::update receives a parameter with the readme content. * remove not so useful write method * Moved readme data fetching logic from utils to readme class and fleshed it out to get all sections. * Fetch and use the readme data in the metadata panel (for recomended plugins) --------- Co-authored-by: Jason Crist <jcrist@pbking.com>
This commit is contained in:
parent
6273f0228c
commit
7aed6ce339
17 changed files with 710 additions and 233 deletions
|
@ -149,7 +149,7 @@ class Create_Block_Theme_Admin {
|
||||||
// Add readme.txt.
|
// Add readme.txt.
|
||||||
$zip->addFromStringToTheme(
|
$zip->addFromStringToTheme(
|
||||||
'readme.txt',
|
'readme.txt',
|
||||||
Theme_Readme::build_readme_txt( $theme )
|
Theme_Readme::create( $theme )
|
||||||
);
|
);
|
||||||
|
|
||||||
// Augment style.css
|
// Augment style.css
|
||||||
|
@ -198,7 +198,6 @@ class Create_Block_Theme_Admin {
|
||||||
$theme['recommended_plugins'] = sanitize_textarea_field( $theme['recommended_plugins'] );
|
$theme['recommended_plugins'] = sanitize_textarea_field( $theme['recommended_plugins'] );
|
||||||
$theme['slug'] = $theme_slug;
|
$theme['slug'] = $theme_slug;
|
||||||
$theme['template'] = '';
|
$theme['template'] = '';
|
||||||
$theme['original_theme'] = wp_get_theme()->get( 'Name' );
|
|
||||||
$theme['text_domain'] = $theme_slug;
|
$theme['text_domain'] = $theme_slug;
|
||||||
|
|
||||||
// Use previous theme's tags if custom tags are empty.
|
// Use previous theme's tags if custom tags are empty.
|
||||||
|
@ -218,7 +217,7 @@ class Create_Block_Theme_Admin {
|
||||||
// Add readme.txt.
|
// Add readme.txt.
|
||||||
$zip->addFromStringToTheme(
|
$zip->addFromStringToTheme(
|
||||||
'readme.txt',
|
'readme.txt',
|
||||||
Theme_Readme::build_readme_txt( $theme )
|
Theme_Readme::create( $theme )
|
||||||
);
|
);
|
||||||
|
|
||||||
// Augment style.css
|
// Augment style.css
|
||||||
|
@ -267,7 +266,7 @@ class Create_Block_Theme_Admin {
|
||||||
$theme['tags_custom'] = sanitize_text_field( $theme['tags_custom'] );
|
$theme['tags_custom'] = sanitize_text_field( $theme['tags_custom'] );
|
||||||
$theme['image_credits'] = sanitize_textarea_field( $theme['image_credits'] );
|
$theme['image_credits'] = sanitize_textarea_field( $theme['image_credits'] );
|
||||||
$theme['recommended_plugins'] = sanitize_textarea_field( $theme['recommended_plugins'] );
|
$theme['recommended_plugins'] = sanitize_textarea_field( $theme['recommended_plugins'] );
|
||||||
$theme['is_parent_theme'] = true;
|
$theme['is_child_theme'] = true;
|
||||||
$theme['text_domain'] = $child_theme_slug;
|
$theme['text_domain'] = $child_theme_slug;
|
||||||
$theme['template'] = $parent_theme_slug;
|
$theme['template'] = $parent_theme_slug;
|
||||||
$theme['slug'] = $child_theme_slug;
|
$theme['slug'] = $child_theme_slug;
|
||||||
|
@ -282,7 +281,7 @@ class Create_Block_Theme_Admin {
|
||||||
// Add readme.txt.
|
// Add readme.txt.
|
||||||
$zip->addFromStringToTheme(
|
$zip->addFromStringToTheme(
|
||||||
'readme.txt',
|
'readme.txt',
|
||||||
Theme_Readme::build_readme_txt( $theme )
|
Theme_Readme::create( $theme )
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add style.css.
|
// Add style.css.
|
||||||
|
@ -357,7 +356,7 @@ class Create_Block_Theme_Admin {
|
||||||
// Add readme.txt.
|
// Add readme.txt.
|
||||||
file_put_contents(
|
file_put_contents(
|
||||||
$blank_theme_path . DIRECTORY_SEPARATOR . 'readme.txt',
|
$blank_theme_path . DIRECTORY_SEPARATOR . 'readme.txt',
|
||||||
Theme_Readme::build_readme_txt( $theme )
|
Theme_Readme::create( $theme )
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add new metadata.
|
// Add new metadata.
|
||||||
|
|
|
@ -7,7 +7,8 @@ class Theme_Create {
|
||||||
);
|
);
|
||||||
|
|
||||||
public static function clone_current_theme( $theme ) {
|
public static function clone_current_theme( $theme ) {
|
||||||
|
// Default values for cloned themes
|
||||||
|
$theme['is_cloned_theme'] = true;
|
||||||
$theme['version'] = '1.0';
|
$theme['version'] = '1.0';
|
||||||
$theme['tags_custom'] = implode( ', ', wp_get_theme()->get( 'Tags' ) );
|
$theme['tags_custom'] = implode( ', ', wp_get_theme()->get( 'Tags' ) );
|
||||||
|
|
||||||
|
@ -33,7 +34,11 @@ class Theme_Create {
|
||||||
Theme_Utils::clone_theme_to_folder( $new_theme_path, $theme['slug'], $theme['name'] );
|
Theme_Utils::clone_theme_to_folder( $new_theme_path, $theme['slug'], $theme['name'] );
|
||||||
Theme_Templates::add_templates_to_local( 'all', $new_theme_path, $theme['slug'], $template_options );
|
Theme_Templates::add_templates_to_local( 'all', $new_theme_path, $theme['slug'], $template_options );
|
||||||
file_put_contents( path_join( $new_theme_path, 'theme.json' ), MY_Theme_JSON_Resolver::export_theme_data( 'all' ) );
|
file_put_contents( path_join( $new_theme_path, 'theme.json' ), MY_Theme_JSON_Resolver::export_theme_data( 'all' ) );
|
||||||
file_put_contents( path_join( $new_theme_path, 'readme.txt' ), Theme_Readme::build_readme_txt( $theme ) );
|
|
||||||
|
// Create the text of readme.txt file and write it to the file.
|
||||||
|
$readme_content = Theme_Readme::create( $theme );
|
||||||
|
file_put_contents( path_join( $new_theme_path, 'readme.txt' ), $readme_content );
|
||||||
|
|
||||||
file_put_contents( path_join( $new_theme_path, 'style.css' ), Theme_Styles::update_style_css( file_get_contents( path_join( $new_theme_path, 'style.css' ) ), $theme ) );
|
file_put_contents( path_join( $new_theme_path, 'style.css' ), Theme_Styles::update_style_css( file_get_contents( path_join( $new_theme_path, 'style.css' ) ), $theme ) );
|
||||||
|
|
||||||
if ( $theme['subfolder'] ) {
|
if ( $theme['subfolder'] ) {
|
||||||
|
@ -61,7 +66,7 @@ class Theme_Create {
|
||||||
// Add readme.txt.
|
// Add readme.txt.
|
||||||
file_put_contents(
|
file_put_contents(
|
||||||
$new_theme_path . DIRECTORY_SEPARATOR . 'readme.txt',
|
$new_theme_path . DIRECTORY_SEPARATOR . 'readme.txt',
|
||||||
Theme_Readme::build_readme_txt( $theme )
|
Theme_Readme::create( $theme )
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add style.css.
|
// Add style.css.
|
||||||
|
@ -109,7 +114,7 @@ class Theme_Create {
|
||||||
// Add readme.txt.
|
// Add readme.txt.
|
||||||
file_put_contents(
|
file_put_contents(
|
||||||
$blank_theme_path . DIRECTORY_SEPARATOR . 'readme.txt',
|
$blank_theme_path . DIRECTORY_SEPARATOR . 'readme.txt',
|
||||||
Theme_Readme::build_readme_txt( $theme )
|
Theme_Readme::create( $theme )
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add new metadata.
|
// Add new metadata.
|
||||||
|
|
|
@ -1,132 +1,145 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Theme_Readme {
|
class Theme_Readme {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a readme.txt file for CHILD/GRANDCHILD themes.
|
* Get the path to the readme.txt file.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function build_readme_txt( $theme ) {
|
public static function file_path() {
|
||||||
$slug = $theme['slug'];
|
return path_join( get_stylesheet_directory(), 'readme.txt' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the content of the readme.txt file.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function get_content() {
|
||||||
|
$path = self::file_path();
|
||||||
|
if ( ! file_exists( $path ) ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return file_get_contents( $path );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates readme.txt text content from theme data.
|
||||||
|
*
|
||||||
|
* @param array $theme The theme data.
|
||||||
|
* {
|
||||||
|
* @type string $name The theme name.
|
||||||
|
* @type string $description The theme description.
|
||||||
|
* @type string $uri The theme URI.
|
||||||
|
* @type string $author The theme author.
|
||||||
|
* @type string $author_uri The theme author URI.
|
||||||
|
* @type string $copyright_year The copyright year.
|
||||||
|
* @type string $image_credits The image credits.
|
||||||
|
* @type string $recommended_plugins The recommended plugins.
|
||||||
|
* @type bool $is_child_theme Whether the theme is a child theme.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @return string The readme content.
|
||||||
|
*/
|
||||||
|
public static function create( $theme ) {
|
||||||
$name = $theme['name'];
|
$name = $theme['name'];
|
||||||
$description = $theme['description'];
|
$description = $theme['description'] ?? '';
|
||||||
$uri = $theme['uri'];
|
$uri = $theme['uri'] ?? '';
|
||||||
$author = $theme['author'];
|
$author = $theme['author'] ?? '';
|
||||||
$author_uri = $theme['author_uri'];
|
$author_uri = $theme['author_uri'] ?? '';
|
||||||
$copy_year = gmdate( 'Y' );
|
$copy_year = $theme['copyright_year'] ?? gmdate( 'Y' );
|
||||||
$wp_version = get_bloginfo( 'version' );
|
$wp_version = $theme['wp_version'] ?? get_bloginfo( 'version' );
|
||||||
|
$required_php_version = $theme['required_php_version'] ?? '5.7';
|
||||||
|
$license = $theme['license'] ?? 'GPLv2 or later';
|
||||||
|
$license_uri = $theme['license_uri'] ?? 'http://www.gnu.org/licenses/gpl-2.0.html';
|
||||||
$image_credits = $theme['image_credits'] ?? '';
|
$image_credits = $theme['image_credits'] ?? '';
|
||||||
$recommended_plugins = $theme['recommended_plugins'] ?? '';
|
$recommended_plugins = $theme['recommended_plugins'] ?? '';
|
||||||
$is_parent_theme = $theme['is_parent_theme'] ?? false;
|
$is_child_theme = $theme['is_child_theme'] ?? false;
|
||||||
$original_theme = $theme['original_theme'] ?? '';
|
|
||||||
|
|
||||||
// Handle copyright section.
|
// Generates the copyright section text.
|
||||||
$new_copyright_section = $is_parent_theme || $original_theme ? true : false;
|
$copyright_section_content = self::get_copyright_text( $theme );
|
||||||
$original_theme_credits = $new_copyright_section ? self::original_theme_credits( $name, $is_parent_theme ) : '';
|
|
||||||
$copyright_section = self::copyright_section( $new_copyright_section, $original_theme_credits, $name, $copy_year, $author, $image_credits );
|
|
||||||
|
|
||||||
// Handle recommended plugins section.
|
// Create empty readme content
|
||||||
$recommended_plugins_section = self::recommended_plugins_section( $recommended_plugins ) ?? '';
|
$readme_content = '';
|
||||||
|
|
||||||
return "=== {$name} ===
|
// Adds the Theme section.
|
||||||
|
$theme_section_content = "
|
||||||
Contributors: {$author}
|
Contributors: {$author}
|
||||||
Requires at least: 6.0
|
Requires at least: 6.0
|
||||||
Tested up to: {$wp_version}
|
Tested up to: {$wp_version}
|
||||||
Requires PHP: 5.7
|
Requires PHP: {$required_php_version}
|
||||||
License: GPLv2 or later
|
License: {$license}
|
||||||
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
License URI: {$license_uri}
|
||||||
|
|
||||||
== Description ==
|
|
||||||
|
|
||||||
{$description}
|
|
||||||
|
|
||||||
== Changelog ==
|
|
||||||
|
|
||||||
= 1.0 =
|
|
||||||
* Initial release
|
|
||||||
{$recommended_plugins_section}
|
|
||||||
{$copyright_section}
|
|
||||||
";
|
";
|
||||||
|
$readme_content = self::add_or_update_section( $name, $theme_section_content, $readme_content );
|
||||||
|
|
||||||
|
// Adds the Decription section
|
||||||
|
$readme_content = self::add_or_update_section( 'Description', $description, $readme_content );
|
||||||
|
|
||||||
|
// Adds the Changelog section
|
||||||
|
$initial_changelog = '
|
||||||
|
= 1.0.0 =
|
||||||
|
* Initial release
|
||||||
|
';
|
||||||
|
$readme_content = self::add_or_update_section( 'Changelog', $initial_changelog, $readme_content );
|
||||||
|
|
||||||
|
// Adds the recommended plugins section
|
||||||
|
$readme_content = self::add_or_update_section( 'Recommended Plugins', $recommended_plugins, $readme_content );
|
||||||
|
|
||||||
|
// Adds the Copyright section
|
||||||
|
$readme_content = self::add_or_update_section( 'Copyright', $copyright_section_content, $readme_content );
|
||||||
|
|
||||||
|
// Adds the Images section
|
||||||
|
$readme_content = self::add_or_update_section( 'Images', $image_credits, $readme_content );
|
||||||
|
|
||||||
|
return $readme_content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build string for original theme credits.
|
* Get the theme data from the installed theme.
|
||||||
* Used in readme.txt of cloned themes.
|
|
||||||
*
|
*
|
||||||
* @param string $new_name New theme name.
|
* @param string $new_name New theme name.
|
||||||
* @return string
|
* @return array The theme data.
|
||||||
|
* {
|
||||||
|
* @type string $name The theme name.
|
||||||
|
* @type string $uri The theme URI.
|
||||||
|
* @type string $author The theme author.
|
||||||
|
* @type string $license The theme license.
|
||||||
|
* @type string $license_uri The theme license URI.
|
||||||
|
* }
|
||||||
*/
|
*/
|
||||||
static function original_theme_credits( $new_name, $is_parent_theme = false ) {
|
private static function get_active_theme_data() {
|
||||||
if ( ! $new_name ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$original_name = wp_get_theme()->get( 'Name' ) ?? '';
|
$original_name = wp_get_theme()->get( 'Name' ) ?? '';
|
||||||
$original_uri = wp_get_theme()->get( 'ThemeURI' ) ?? '';
|
$original_uri = wp_get_theme()->get( 'ThemeURI' ) ?? '';
|
||||||
$original_author = wp_get_theme()->get( 'Author' ) ?? '';
|
$original_author = wp_get_theme()->get( 'Author' ) ?? '';
|
||||||
$original_readme = get_stylesheet_directory() . '/readme.txt' ?? '';
|
$original_license = self::get_prop( 'License' );
|
||||||
$original_license = '';
|
$original_license_uri = self::get_prop( 'License URI' );
|
||||||
$original_license_uri = '';
|
|
||||||
$readme_content = file_exists( $original_readme ) ? file_get_contents( $original_readme ) : '';
|
|
||||||
|
|
||||||
if ( ! $readme_content ) {
|
return array(
|
||||||
return;
|
'name' => $original_name,
|
||||||
}
|
'uri' => $original_uri,
|
||||||
|
'author' => $original_author,
|
||||||
// Get license from original theme readme.txt
|
'license' => $original_license,
|
||||||
if ( str_contains( $readme_content, 'License:' ) ) {
|
'license_uri' => $original_license_uri,
|
||||||
$starts = strpos( $readme_content, 'License:' ) + strlen( 'License:' );
|
|
||||||
$ends = strpos( $readme_content, 'License URI:', $starts );
|
|
||||||
$original_license = trim( substr( $readme_content, $starts, $ends - $starts ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get license URI from original theme readme.txt
|
|
||||||
if ( str_contains( $readme_content, 'License URI:' ) ) {
|
|
||||||
$starts = strpos( $readme_content, 'License URI:' ) + strlen( 'License URI:' );
|
|
||||||
$ends = strpos( $readme_content, '== Description ==', $starts );
|
|
||||||
$original_license_uri = trim( substr( $readme_content, $starts, $ends - $starts ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( empty( $original_license ) || empty( $original_license_uri ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$theme_credit_content = sprintf(
|
|
||||||
/* translators: 1: New Theme name, 2: Original Theme Name. 3. Original Theme URI. 4. Original Theme Author. 5. Original Theme License. 6. Original Theme License URI. */
|
|
||||||
__( '%1$s is based on %2$s (%3$s), (C) %4$s, [%5$s](%6$s)', 'create-block-theme' ),
|
|
||||||
$new_name,
|
|
||||||
$original_name,
|
|
||||||
$original_uri,
|
|
||||||
$original_author,
|
|
||||||
$original_license,
|
|
||||||
$original_license_uri
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( $is_parent_theme ) {
|
|
||||||
$theme_credit_content = sprintf(
|
|
||||||
/* translators: 1: New Theme name, 2: Parent Theme Name. 3. Parent Theme URI. 4. Parent Theme Author. 5. Parent Theme License. 6. Parent Theme License URI. */
|
|
||||||
__( '%1$s is a child theme of %2$s (%3$s), (C) %4$s, [%5$s](%6$s)', 'create-block-theme' ),
|
|
||||||
$new_name,
|
|
||||||
$original_name,
|
|
||||||
$original_uri,
|
|
||||||
$original_author,
|
|
||||||
$original_license,
|
|
||||||
$original_license_uri
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $theme_credit_content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build copyright section.
|
* Build default copyright text for a theme.
|
||||||
* Used in readme.txt of cloned themes or child themes.
|
|
||||||
*
|
*
|
||||||
* @return string
|
* @param string $name The theme name.
|
||||||
|
* @param string $copy_year The current year.
|
||||||
|
* @param string $author The theme author.
|
||||||
|
* @return string The default copyright text.
|
||||||
*/
|
*/
|
||||||
static function copyright_section( $new_copyright_section, $original_theme_credits, $name, $copy_year, $author, $image_credits ) {
|
private static function get_copyright_text( $theme ) {
|
||||||
// Default copyright section.
|
$name = $theme['name'];
|
||||||
$copyright_section = "== Copyright ==
|
$year = $theme['copy_year'] ?? gmdate( 'Y' );
|
||||||
|
$author = $theme['author'] ?? '';
|
||||||
|
|
||||||
{$name} WordPress Theme, (C) {$copy_year} {$author}
|
$text = "
|
||||||
|
{$name} WordPress Theme, (C) {$year} {$author}
|
||||||
{$name} is distributed under the terms of the GNU GPL.
|
{$name} is distributed under the terms of the GNU GPL.
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
@ -137,123 +150,184 @@ the Free Software Foundation, either version 2 of the License, or
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.";
|
GNU General Public License for more details.
|
||||||
|
";
|
||||||
|
|
||||||
// If a new copyright section is required, then build ones based on the current theme.
|
$is_child_theme = $theme['is_child_theme'] ?? false;
|
||||||
if ( $new_copyright_section ) {
|
$is_cloned_theme = $theme['is_cloned_theme'] ?? false;
|
||||||
$copyright_section_intro = '== Copyright ==';
|
|
||||||
|
|
||||||
// Get current theme readme.txt
|
/*
|
||||||
$current_readme = get_stylesheet_directory() . '/readme.txt' ?? '';
|
* If the theme is a child theme or a cloned theme, add a reference to the parent theme.
|
||||||
$current_readme_content = file_exists( $current_readme ) ? file_get_contents( $current_readme ) : '';
|
|
||||||
|
|
||||||
if ( ! $current_readme_content ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy copyright section from current theme readme.txt
|
|
||||||
if ( str_contains( $current_readme_content, $copyright_section_intro ) ) {
|
|
||||||
$copyright_section_start = strpos( $current_readme_content, $copyright_section_intro );
|
|
||||||
$copyright_section = substr( $current_readme_content, $copyright_section_start );
|
|
||||||
|
|
||||||
if ( $original_theme_credits ) {
|
|
||||||
$new_copyright_section = str_replace( $copyright_section_intro . "\n", '', $copyright_section );
|
|
||||||
$copyright_section = $copyright_section_intro . "\n\n" . $original_theme_credits . "\n" . $new_copyright_section;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $image_credits ) {
|
|
||||||
$copyright_section = $copyright_section . "\n" . $image_credits;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $copyright_section;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build Recommended Plugins section.
|
|
||||||
*
|
*
|
||||||
* @return string
|
* Example: "My Child Theme is a child theme of My Parent Theme (https://example.org/themes/my-parent-theme), (C) the WordPress team, [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html)"
|
||||||
*/
|
*/
|
||||||
static function recommended_plugins_section( $recommended_plugins, $updated_readme = '' ) {
|
if ( $is_child_theme || $is_cloned_theme ) {
|
||||||
$recommended_plugins_section = '';
|
$original_theme = self::get_active_theme_data();
|
||||||
|
|
||||||
if ( ! $recommended_plugins ) {
|
$reference_string = $is_child_theme
|
||||||
return '';
|
? '%1$s is a child theme of %2$s (%3$s), (C) %4$s, [%5$s](%6$s)'
|
||||||
|
: '%1$s is based on %2$s (%3$s), (C) %4$s, [%5$s](%6$s)';
|
||||||
|
|
||||||
|
$reference = sprintf(
|
||||||
|
$reference_string,
|
||||||
|
$name,
|
||||||
|
$original_theme['name'],
|
||||||
|
$original_theme['uri'],
|
||||||
|
$original_theme['author'],
|
||||||
|
$original_theme['license'],
|
||||||
|
$original_theme['license_uri']
|
||||||
|
);
|
||||||
|
|
||||||
|
$text .= "\n\n" . $reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
$section_start = "\n== Recommended Plugins ==\n";
|
return $text;
|
||||||
|
|
||||||
// Remove existing Recommended Plugins section.
|
|
||||||
if ( $updated_readme && str_contains( $updated_readme, $section_start ) ) {
|
|
||||||
$pattern = '/\s+== Recommended Plugins ==\s+(.*?)(?=(\n\=\=)|$)/s';
|
|
||||||
preg_match_all( $pattern, $updated_readme, $matches );
|
|
||||||
$current_section = $matches[0][0];
|
|
||||||
$updated_readme = str_replace( $current_section, '', $updated_readme );
|
|
||||||
}
|
|
||||||
|
|
||||||
$recommended_plugins_section = $section_start . "\n" . $recommended_plugins . "\n";
|
|
||||||
|
|
||||||
if ( $updated_readme ) {
|
|
||||||
return $updated_readme . $recommended_plugins_section;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $recommended_plugins_section;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update current readme.txt file, rather than building a new one.
|
* Update current readme.txt file, rather than building a new one.
|
||||||
*
|
*
|
||||||
|
* @param array $theme The theme data.
|
||||||
|
* {
|
||||||
|
* @type string $description The theme description.
|
||||||
|
* @type string $author The theme author.
|
||||||
|
* @type string $image_credits The image credits.
|
||||||
|
* @type string $recommended_plugins The recommended plugins.
|
||||||
|
* }
|
||||||
|
* @param string $readme_content readme.txt content.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function update_readme_txt( $theme ) {
|
public static function update( $theme, $readme_content = '' ) {
|
||||||
$description = $theme['description'];
|
// Theme data.
|
||||||
$author = $theme['author'];
|
$description = $theme['description'] ?? '';
|
||||||
$wp_version = get_bloginfo( 'version' );
|
$author = $theme['author'] ?? '';
|
||||||
|
$wp_version = $theme['wp_version'] ?? get_bloginfo( 'version' );
|
||||||
$image_credits = $theme['image_credits'] ?? '';
|
$image_credits = $theme['image_credits'] ?? '';
|
||||||
$recommended_plugins = $theme['recommended_plugins'] ?? '';
|
$recommended_plugins = $theme['recommended_plugins'] ?? '';
|
||||||
$updated_readme = '';
|
|
||||||
$current_readme = get_stylesheet_directory() . '/readme.txt' ?? '';
|
|
||||||
$readme_content = file_exists( $current_readme ) ? file_get_contents( $current_readme ) : '';
|
|
||||||
|
|
||||||
if ( ! $readme_content ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$updated_readme = $readme_content;
|
|
||||||
|
|
||||||
// Update description.
|
// Update description.
|
||||||
if ( $description ) {
|
$readme_content = self::add_or_update_section( 'Description', $description, $readme_content );
|
||||||
$pattern = '/(== Description ==)(.*?)(\n\n=|$)/s';
|
|
||||||
preg_match_all( $pattern, $updated_readme, $matches );
|
|
||||||
$current_description = $matches[0][0];
|
|
||||||
$updated_readme = str_replace( $current_description, "== Description ==\n\n{$description}\n\n=", $updated_readme );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update Author/Contributors.
|
// Update Author/Contributors.
|
||||||
if ( $author ) {
|
$readme_content = self::add_or_update_prop( 'Contributors', $author, $readme_content );
|
||||||
$pattern = '/(Contributors:)(.*?)(\n|$)/s';
|
|
||||||
preg_match_all( $pattern, $updated_readme, $matches );
|
|
||||||
$current_uri = $matches[0][0];
|
|
||||||
$updated_readme = str_replace( $current_uri, "Contributors: {$author}\n", $updated_readme );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update "Tested up to" version.
|
// Update "Tested up to" version.
|
||||||
if ( $wp_version ) {
|
$readme_content = self::add_or_update_prop( 'Tested up to', $wp_version, $readme_content );
|
||||||
$pattern = '/(Tested up to:)(.*?)(\n|$)/s';
|
|
||||||
preg_match_all( $pattern, $updated_readme, $matches );
|
// Update recommended plugins section.
|
||||||
|
$readme_content = self::add_or_update_section( 'Recommended Plugins', $recommended_plugins, $readme_content );
|
||||||
|
|
||||||
|
// Update image credits section.
|
||||||
|
$readme_content = self::add_or_update_section( 'Images', $image_credits, $readme_content );
|
||||||
|
|
||||||
|
return $readme_content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a section to the readme.txt file.
|
||||||
|
*
|
||||||
|
* @param string $section_title Section to write.
|
||||||
|
* @param string $section_content New content to write.
|
||||||
|
* @param string $current_content Current content to manipulate.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function add_or_update_section( $section_title, $section_content, $readme_content = '' ) {
|
||||||
|
// If the section content is empty, return the current content. This avoids adding empty sections.
|
||||||
|
if ( empty( $section_content ) ) {
|
||||||
|
return $readme_content;
|
||||||
|
}
|
||||||
|
|
||||||
|
$section_content = trim( $section_content, "\r" );
|
||||||
|
$section_content = trim( $section_content, "\n" );
|
||||||
|
|
||||||
|
// Regular expression to find the section, handling both '==' and '==='
|
||||||
|
$pattern = '/(={2,3}\s*' . preg_quote( $section_title, '/' ) . '\s*={2,3})(.*?)(?=(={2,3}|$))/s';
|
||||||
|
$replacement = "== $section_title ==\n\n$section_content\n\n";
|
||||||
|
|
||||||
|
// Check if the section exists
|
||||||
|
if ( preg_match( $pattern, $readme_content ) ) {
|
||||||
|
// Replace the existing section content
|
||||||
|
$updated_content = preg_replace( $pattern, $replacement, $readme_content );
|
||||||
|
} else {
|
||||||
|
// Remove any trailing whitespace, newlines or carriage returns from current content
|
||||||
|
$readme_content = rtrim( $readme_content );
|
||||||
|
|
||||||
|
// Ensure two newlines before appending new section
|
||||||
|
if ( ! empty( $readme_content ) ) {
|
||||||
|
$readme_content .= "\n\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append new section if not found
|
||||||
|
$updated_content = $readme_content . $replacement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $updated_content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds or updates a property in the readme content.
|
||||||
|
*
|
||||||
|
* @param string $prop_name The name of the property.
|
||||||
|
* @param string $prop_value The value of the property.
|
||||||
|
* @param string $readme_content The content of the readme file.
|
||||||
|
* @return string The updated readme content.
|
||||||
|
*/
|
||||||
|
private static function add_or_update_prop( $prop_name, $prop_value, $readme_content ) {
|
||||||
|
if ( empty( $prop_value ) ) {
|
||||||
|
return $readme_content;
|
||||||
|
}
|
||||||
|
$pattern = '/(' . preg_quote( $prop_name, '/' ) . ')(.*?)(\n|$)/s';
|
||||||
|
preg_match_all( $pattern, $readme_content, $matches );
|
||||||
$current_uri = $matches[0][0];
|
$current_uri = $matches[0][0];
|
||||||
$updated_readme = str_replace( $current_uri, "Tested up to: {$wp_version}\n", $updated_readme );
|
$updated_readme = str_replace( $current_uri, "{$prop_name}: {$prop_value}\n", $readme_content );
|
||||||
}
|
|
||||||
|
|
||||||
if ( $recommended_plugins ) {
|
|
||||||
$updated_readme = self::recommended_plugins_section( $recommended_plugins, $updated_readme );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $image_credits ) {
|
|
||||||
$updated_readme = $updated_readme . "\n\n" . $image_credits;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $updated_readme;
|
return $updated_readme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get property value from the readme content.
|
||||||
|
*
|
||||||
|
* @return string The property value
|
||||||
|
*/
|
||||||
|
private static function get_prop( $property, $readme_content = '' ) {
|
||||||
|
if ( empty( $readme_content ) ) {
|
||||||
|
$readme_content = self::get_content();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the regular expression pattern to match the line
|
||||||
|
$pattern = '/^' . preg_quote( $property, '/' ) . ': (.*)$/m';
|
||||||
|
|
||||||
|
// Use preg_match to find a matching line
|
||||||
|
if ( preg_match( $pattern, $readme_content, $matches ) ) {
|
||||||
|
// Return the capturing group which contains the value after the colon
|
||||||
|
return trim( $matches[1] );
|
||||||
|
} else {
|
||||||
|
// Return null if no match is found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_sections() {
|
||||||
|
|
||||||
|
$readme_content = self::get_content();
|
||||||
|
$sections = array();
|
||||||
|
|
||||||
|
// Regular expression to find the section, handling both '==' and '==='
|
||||||
|
$pattern = '/(={2,3}\s*(.*?)\s*={2,3})(.*?)(?=(={2,3}|$))/s';
|
||||||
|
|
||||||
|
// Find all sections
|
||||||
|
preg_match_all( $pattern, $readme_content, $matches, PREG_SET_ORDER );
|
||||||
|
|
||||||
|
// Loop through the matches
|
||||||
|
foreach ( $matches as $match ) {
|
||||||
|
$section_title = str_replace( '-', '_', sanitize_title( $match[2] ) );
|
||||||
|
$section_content = trim( $match[3] );
|
||||||
|
|
||||||
|
// Add the section to the sections array
|
||||||
|
$sections[ $section_title ] = $section_content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sections;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,26 +91,6 @@ class Theme_Utils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function get_readme_data() {
|
|
||||||
$readme_location = get_template_directory() . '/readme.txt';
|
|
||||||
|
|
||||||
if ( ! file_exists( $readme_location ) ) {
|
|
||||||
throw new Exception( 'No readme file found' );
|
|
||||||
}
|
|
||||||
|
|
||||||
$readme_file_contents = file_get_contents( $readme_location );
|
|
||||||
|
|
||||||
$readme_file_details = array();
|
|
||||||
|
|
||||||
// Handle Recommended Plugins.
|
|
||||||
$pattern = '/== Recommended Plugins ==\s+(.*?)(\s+==|$)/s';
|
|
||||||
preg_match_all( $pattern, $readme_file_contents, $matches );
|
|
||||||
$readme_file_details['recommendedPlugins'] = $matches[1][0] ?? '';
|
|
||||||
|
|
||||||
return $readme_file_details;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relocate the theme to a new folder and activate the newly relocated theme.
|
* Relocate the theme to a new folder and activate the newly relocated theme.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -165,7 +165,8 @@ class Create_Block_Theme_API {
|
||||||
|
|
||||||
function rest_get_readme_data( $request ) {
|
function rest_get_readme_data( $request ) {
|
||||||
try {
|
try {
|
||||||
$readme_data = Theme_Utils::get_readme_data();
|
$readme_data = Theme_Readme::get_sections();
|
||||||
|
|
||||||
return new WP_REST_Response(
|
return new WP_REST_Response(
|
||||||
array(
|
array(
|
||||||
'status' => 'SUCCESS',
|
'status' => 'SUCCESS',
|
||||||
|
@ -202,6 +203,7 @@ class Create_Block_Theme_API {
|
||||||
function rest_create_child_theme( $request ) {
|
function rest_create_child_theme( $request ) {
|
||||||
|
|
||||||
$theme = $this->sanitize_theme_data( $request->get_params() );
|
$theme = $this->sanitize_theme_data( $request->get_params() );
|
||||||
|
$theme['is_child_theme'] = true;
|
||||||
//TODO: Handle screenshots
|
//TODO: Handle screenshots
|
||||||
$screenshot = null;
|
$screenshot = null;
|
||||||
|
|
||||||
|
@ -281,7 +283,7 @@ class Create_Block_Theme_API {
|
||||||
// Add readme.txt.
|
// Add readme.txt.
|
||||||
$zip->addFromStringToTheme(
|
$zip->addFromStringToTheme(
|
||||||
'readme.txt',
|
'readme.txt',
|
||||||
Theme_Readme::build_readme_txt( $theme )
|
Theme_Readme::create( $theme )
|
||||||
);
|
);
|
||||||
|
|
||||||
// Build style.css with new theme metadata
|
// Build style.css with new theme metadata
|
||||||
|
@ -329,7 +331,7 @@ class Create_Block_Theme_API {
|
||||||
// Add readme.txt.
|
// Add readme.txt.
|
||||||
$zip->addFromStringToTheme(
|
$zip->addFromStringToTheme(
|
||||||
'readme.txt',
|
'readme.txt',
|
||||||
Theme_Readme::build_readme_txt( $theme )
|
Theme_Readme::create( $theme )
|
||||||
);
|
);
|
||||||
|
|
||||||
// Build style.css with new theme metadata
|
// Build style.css with new theme metadata
|
||||||
|
@ -407,16 +409,16 @@ class Create_Block_Theme_API {
|
||||||
* Update the theme metadata and relocate the theme.
|
* Update the theme metadata and relocate the theme.
|
||||||
*/
|
*/
|
||||||
function rest_update_theme( $request ) {
|
function rest_update_theme( $request ) {
|
||||||
$theme = $request->get_params();
|
$theme = $this->sanitize_theme_data( $request->get_params() );
|
||||||
|
|
||||||
// Update the metadata of the theme in the style.css file
|
// Update the metadata of the theme in the style.css file
|
||||||
$style_css = file_get_contents( get_stylesheet_directory() . '/style.css' );
|
$style_css = file_get_contents( get_stylesheet_directory() . '/style.css' );
|
||||||
$style_css = Theme_Styles::update_style_css( $style_css, $theme );
|
$style_css = Theme_Styles::update_style_css( $style_css, $theme );
|
||||||
file_put_contents( get_stylesheet_directory() . '/style.css', $style_css );
|
file_put_contents( get_stylesheet_directory() . '/style.css', $style_css );
|
||||||
file_put_contents(
|
|
||||||
get_stylesheet_directory() . '/readme.txt',
|
$readme_content = Theme_Readme::get_content();
|
||||||
Theme_Readme::update_readme_txt( $theme )
|
$readme_content = Theme_Readme::update( $theme, $readme_content );
|
||||||
);
|
file_put_contents( Theme_Readme::file_path(), $readme_content );
|
||||||
|
|
||||||
// Replace Screenshot
|
// Replace Screenshot
|
||||||
if ( wp_get_theme()->get_screenshot() !== $theme['screenshot'] ) {
|
if ( wp_get_theme()->get_screenshot() !== $theme['screenshot'] ) {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
>
|
>
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="testing">
|
<testsuite name="testing">
|
||||||
<directory prefix="test-" suffix=".php">./tests/</directory>
|
<directory suffix=".php">./tests/</directory>
|
||||||
<exclude>./tests/test-sample.php</exclude>
|
<exclude>./tests/test-sample.php</exclude>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { postUpdateThemeMetadata } from '../resolvers';
|
import { postUpdateThemeMetadata, fetchReadmeData } from '../resolvers';
|
||||||
|
|
||||||
const ALLOWED_SCREENSHOT_MEDIA_TYPES = [
|
const ALLOWED_SCREENSHOT_MEDIA_TYPES = [
|
||||||
'image/png',
|
'image/png',
|
||||||
|
@ -53,6 +53,7 @@ export const ThemeMetadataEditorModal = ( { onRequestClose } ) => {
|
||||||
|
|
||||||
useSelect( async ( select ) => {
|
useSelect( async ( select ) => {
|
||||||
const themeData = select( 'core' ).getCurrentTheme();
|
const themeData = select( 'core' ).getCurrentTheme();
|
||||||
|
const readmeData = await fetchReadmeData();
|
||||||
setTheme( {
|
setTheme( {
|
||||||
name: themeData.name.raw,
|
name: themeData.name.raw,
|
||||||
description: themeData.description.raw,
|
description: themeData.description.raw,
|
||||||
|
@ -62,6 +63,7 @@ export const ThemeMetadataEditorModal = ( { onRequestClose } ) => {
|
||||||
author_uri: themeData.author_uri.raw,
|
author_uri: themeData.author_uri.raw,
|
||||||
tags_custom: themeData.tags.rendered,
|
tags_custom: themeData.tags.rendered,
|
||||||
screenshot: themeData.screenshot,
|
screenshot: themeData.screenshot,
|
||||||
|
recommended_plugins: readmeData.recommended_plugins,
|
||||||
subfolder:
|
subfolder:
|
||||||
themeData.stylesheet.lastIndexOf( '/' ) > 1
|
themeData.stylesheet.lastIndexOf( '/' ) > 1
|
||||||
? themeData.stylesheet.substring(
|
? themeData.stylesheet.substring(
|
||||||
|
|
|
@ -29,6 +29,30 @@ export async function fetchThemeJson() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchReadmeData() {
|
||||||
|
const fetchOptions = {
|
||||||
|
path: '/create-block-theme/v1/get-readme-data',
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await apiFetch( fetchOptions );
|
||||||
|
if ( ! response?.data || 'SUCCESS' !== response?.status ) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to fetch readme data: ${
|
||||||
|
response?.message || response?.status
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return response?.data;
|
||||||
|
} catch ( e ) {
|
||||||
|
// @todo: handle error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function postCreateThemeVariation( name ) {
|
export async function postCreateThemeVariation( name ) {
|
||||||
return apiFetch( {
|
return apiFetch( {
|
||||||
path: '/create-block-theme/v1/create-variation',
|
path: '/create-block-theme/v1/create-variation',
|
||||||
|
|
50
tests/CbtThemeReadme/addOrUpdateSection.php
Normal file
50
tests/CbtThemeReadme/addOrUpdateSection.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/base.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the add_or_update_section method of the Theme_Readme class.
|
||||||
|
*
|
||||||
|
* @package Create_Block_Theme
|
||||||
|
* @covers Theme_Readme::add_or_update_section
|
||||||
|
* @group readme
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class CBT_ThemeReadme_AddOrUpdateSection extends CBT_Theme_Readme_UnitTestCase {
|
||||||
|
public function test_add_or_update_section() {
|
||||||
|
$section_title = 'Test Section';
|
||||||
|
$section_content = 'Test content abc123';
|
||||||
|
|
||||||
|
// Add a new section.
|
||||||
|
$readme = Theme_Readme::add_or_update_section( $section_title, $section_content );
|
||||||
|
|
||||||
|
// Check if the section was added.
|
||||||
|
$this->assertStringContainsString( $section_title, $readme, 'The section title is missing.' );
|
||||||
|
$this->assertStringContainsString( $section_content, $readme, 'The section content is missing' );
|
||||||
|
|
||||||
|
// Update the section.
|
||||||
|
$section_content_updated = 'Updated content xyz890';
|
||||||
|
|
||||||
|
$readme = Theme_Readme::add_or_update_section( $section_title, $section_content_updated );
|
||||||
|
|
||||||
|
// Check if the old content was updated.
|
||||||
|
$this->assertStringNotContainsString( $section_content, $readme, 'The old content is still present.' );
|
||||||
|
|
||||||
|
// Check if the new content was added.
|
||||||
|
$this->assertStringContainsString( $section_title, $readme, 'The section title is missing.' );
|
||||||
|
$this->assertStringContainsString( $section_content_updated, $readme, 'The updated content is missing.' );
|
||||||
|
|
||||||
|
// Check if that the section title was added only once.
|
||||||
|
$section_count = substr_count( $readme, $section_title );
|
||||||
|
$this->assertEquals( 1, $section_count, 'The section title was added more than once.' );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_add_or_update_section_with_no_content() {
|
||||||
|
$section_title = 'Test Section';
|
||||||
|
$section_content = '';
|
||||||
|
|
||||||
|
// Empty section should not be added.
|
||||||
|
$readme = Theme_Readme::add_or_update_section( $section_title, $section_content );
|
||||||
|
$this->assertStringNotContainsString( $section_title, $readme, 'The title of an empty section should not be added.' );
|
||||||
|
}
|
||||||
|
}
|
64
tests/CbtThemeReadme/base.php
Normal file
64
tests/CbtThemeReadme/base.php
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Base test case for Theme Readme tests.
|
||||||
|
*
|
||||||
|
* @package Create_Block_Theme
|
||||||
|
*/
|
||||||
|
abstract class CBT_Theme_Readme_UnitTestCase extends WP_UnitTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the original active theme slug in order to restore it in tear down.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $orig_active_theme_slug;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the custom test theme directory.
|
||||||
|
*
|
||||||
|
* @var string|null;
|
||||||
|
*/
|
||||||
|
private $test_theme_dir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the original readme.txt content.
|
||||||
|
*
|
||||||
|
* @var string|null;
|
||||||
|
*/
|
||||||
|
private $orig_readme_content;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up tests.
|
||||||
|
*/
|
||||||
|
public function set_up() {
|
||||||
|
parent::set_up();
|
||||||
|
|
||||||
|
// Store the original active theme.
|
||||||
|
$this->orig_active_theme_slug = get_option( 'stylesheet' );
|
||||||
|
|
||||||
|
// Create a test theme directory.
|
||||||
|
$this->test_theme_dir = DIR_TESTDATA . '/themes/';
|
||||||
|
|
||||||
|
// Register test theme directory.
|
||||||
|
register_theme_directory( $this->test_theme_dir );
|
||||||
|
|
||||||
|
// Switch to the test theme.
|
||||||
|
switch_theme( 'test-theme-readme' );
|
||||||
|
|
||||||
|
// Store the original readme.txt content.
|
||||||
|
$this->orig_readme_content = Theme_Readme::get_content();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tears down tests.
|
||||||
|
*/
|
||||||
|
public function tear_down() {
|
||||||
|
parent::tear_down();
|
||||||
|
|
||||||
|
// Restore the original readme.txt content.
|
||||||
|
file_put_contents( Theme_Readme::file_path(), $this->orig_readme_content );
|
||||||
|
|
||||||
|
// Restore the original active theme.
|
||||||
|
switch_theme( $this->orig_active_theme_slug );
|
||||||
|
}
|
||||||
|
}
|
115
tests/CbtThemeReadme/create.php
Normal file
115
tests/CbtThemeReadme/create.php
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/base.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the create method of the Theme_Readme class.
|
||||||
|
*
|
||||||
|
* @package Create_Block_Theme
|
||||||
|
* @covers Theme_Readme::create
|
||||||
|
* @group readme
|
||||||
|
*/
|
||||||
|
class CBT_ThemeReadme_Create extends CBT_Theme_Readme_UnitTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider data_test_create
|
||||||
|
*/
|
||||||
|
public function test_create( $data ) {
|
||||||
|
$readme = Theme_Readme::create( $data );
|
||||||
|
|
||||||
|
// Removes the newlines from the readme content to make it easier to search for strings.
|
||||||
|
$readme_without_newlines = str_replace( "\n", '', $readme );
|
||||||
|
|
||||||
|
$expected_name = '== ' . $data['name'] . ' ==';
|
||||||
|
$expected_description = '== Description ==' . $data['description'];
|
||||||
|
$expected_uri = 'Theme URI: ' . $data['uri'];
|
||||||
|
$expected_author = 'Contributors: ' . $data['author'];
|
||||||
|
$expected_author_uri = 'Author URI: ' . $data['author_uri'];
|
||||||
|
$expected_wp_version = 'Tested up to: ' . $data['wp_version'] ?? get_bloginfo( 'version' );
|
||||||
|
$expected_php_version = 'Requires PHP: ' . $data['required_php_version'];
|
||||||
|
$expected_license = 'License: ' . $data['license'];
|
||||||
|
$expected_license_uri = 'License URI: ' . $data['license_uri'];
|
||||||
|
$expected_image_credits = '== Images ==' . $data['image_credits'];
|
||||||
|
$expected_recommended_plugins = '== Recommended Plugins ==' . $data['recommended_plugins'];
|
||||||
|
|
||||||
|
$this->assertStringContainsString( $expected_name, $readme_without_newlines, 'The expected name is missing.' );
|
||||||
|
$this->assertStringContainsString( $expected_author, $readme_without_newlines, 'The expected author is missing.' );
|
||||||
|
$this->assertStringContainsString( $expected_wp_version, $readme_without_newlines, 'The expected WP version is missing.' );
|
||||||
|
$this->assertStringContainsString( $expected_image_credits, $readme_without_newlines, 'The expected image credits are missing.' );
|
||||||
|
$this->assertStringContainsString( $expected_recommended_plugins, $readme_without_newlines, 'The expected recommended plugins are missing.' );
|
||||||
|
|
||||||
|
// Assetion specific to child themes.
|
||||||
|
if ( isset( $data['is_child_theme'] ) && $data['is_child_theme'] ) {
|
||||||
|
$this->assertStringContainsString(
|
||||||
|
$data['name'] . ' is a child theme of Test Readme Theme (https://example.org/themes/test-readme-theme), (C) the WordPress team, [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html)',
|
||||||
|
$readme_without_newlines,
|
||||||
|
'The expected reference to the parent theme is missing.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assetion specific to child themes.
|
||||||
|
if ( isset( $data['is_cloned_theme'] ) && $data['is_cloned_theme'] ) {
|
||||||
|
$this->assertStringContainsString(
|
||||||
|
$data['name'] . ' is based on Test Readme Theme (https://example.org/themes/test-readme-theme), (C) the WordPress team, [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html)',
|
||||||
|
$readme_without_newlines,
|
||||||
|
'The expected reference to the parent theme is missing.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function data_test_create() {
|
||||||
|
return array(
|
||||||
|
'complete data for a nomal theme' => array(
|
||||||
|
'data' => array(
|
||||||
|
'name' => 'My Theme',
|
||||||
|
'description' => 'New theme description',
|
||||||
|
'uri' => 'https://example.com',
|
||||||
|
'author' => 'New theme author',
|
||||||
|
'author_uri' => 'https://example.com/author',
|
||||||
|
'copyright_year' => '2077',
|
||||||
|
'wp_version' => '12.12',
|
||||||
|
'required_php_version' => '10.0',
|
||||||
|
'license' => 'GPLv2 or later',
|
||||||
|
'license_uri' => 'https://www.gnu.org/licenses/gpl-2.0.html',
|
||||||
|
'image_credits' => 'The images were taken from https://example.org and have a CC0 license.',
|
||||||
|
'recommended_plugins' => 'The theme is best used with the following plugins: Plugin 1, Plugin 2, Plugin 3.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'complete data for a child theme' => array(
|
||||||
|
'data' => array(
|
||||||
|
'name' => 'My Child Theme',
|
||||||
|
'description' => 'New child theme description',
|
||||||
|
'uri' => 'https://example.com',
|
||||||
|
'author' => 'New theme author',
|
||||||
|
'author_uri' => 'https://example.com/author',
|
||||||
|
'copyright_year' => '2078',
|
||||||
|
'wp_version' => '13.13',
|
||||||
|
'required_php_version' => '11.0',
|
||||||
|
'license' => 'GPLv2 or later',
|
||||||
|
'license_uri' => 'https://www.gnu.org/licenses/gpl-2.0.html',
|
||||||
|
'image_credits' => 'The images were taken from https://example.org and have a CC0 license.',
|
||||||
|
'recommended_plugins' => 'The theme is best used with the following plugins: Plugin 1, Plugin 2, Plugin 3.',
|
||||||
|
'is_child_theme' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'complete data for a cloned theme' => array(
|
||||||
|
'data' => array(
|
||||||
|
'name' => 'My Cloned Theme',
|
||||||
|
'description' => 'New cloned theme description',
|
||||||
|
'uri' => 'https://example.com',
|
||||||
|
'author' => 'New theme author',
|
||||||
|
'author_uri' => 'https://example.com/author',
|
||||||
|
'copyright_year' => '2079',
|
||||||
|
'wp_version' => '14.14',
|
||||||
|
'required_php_version' => '12.0',
|
||||||
|
'license' => 'GPLv2 or later',
|
||||||
|
'license_uri' => 'https://www.gnu.org/licenses/gpl-2.0.html',
|
||||||
|
'image_credits' => 'The images were taken from https://example.org and have a CC0 license.',
|
||||||
|
'recommended_plugins' => 'The theme is best used with the following plugins: Plugin 1, Plugin 2, Plugin 3.',
|
||||||
|
'is_cloned_theme' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// TODO: Add more test cases.
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
20
tests/CbtThemeReadme/filePath.php
Normal file
20
tests/CbtThemeReadme/filePath.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/base.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the file_path method of the Theme_Readme class.
|
||||||
|
*
|
||||||
|
* @package Create_Block_Theme
|
||||||
|
* @covers Theme_Readme::file_path
|
||||||
|
* @group readme
|
||||||
|
*/
|
||||||
|
class CBT_ThemeReadme_FilePath extends CBT_Theme_Readme_UnitTestCase {
|
||||||
|
public function test_file_path() {
|
||||||
|
$result = Theme_Readme::file_path();
|
||||||
|
$expected = get_stylesheet_directory() . '/readme.txt';
|
||||||
|
$this->assertEquals( $expected, $result );
|
||||||
|
|
||||||
|
$this->assertEquals( 'test-theme-readme', get_option( 'stylesheet' ) );
|
||||||
|
}
|
||||||
|
}
|
18
tests/CbtThemeReadme/getContent.php
Normal file
18
tests/CbtThemeReadme/getContent.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/base.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the get_content method of the Theme_Readme class.
|
||||||
|
*
|
||||||
|
* @package Create_Block_Theme
|
||||||
|
* @covers Theme_Readme::get_content
|
||||||
|
* @group readme
|
||||||
|
*/
|
||||||
|
class CBT_ThemeReadme_GetContent extends CBT_Theme_Readme_UnitTestCase {
|
||||||
|
public function test_get_content() {
|
||||||
|
$result = Theme_Readme::get_content();
|
||||||
|
$expected = file_get_contents( Theme_Readme::file_path() );
|
||||||
|
$this->assertEquals( $expected, $result );
|
||||||
|
}
|
||||||
|
}
|
49
tests/CbtThemeReadme/update.php
Normal file
49
tests/CbtThemeReadme/update.php
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/base.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the update method of the Theme_Readme class.
|
||||||
|
*
|
||||||
|
* @package Create_Block_Theme
|
||||||
|
* @covers Theme_Readme::update
|
||||||
|
* @group readme
|
||||||
|
*/
|
||||||
|
class CBT_ThemeReadme_Update extends CBT_Theme_Readme_UnitTestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider data_test_update
|
||||||
|
*/
|
||||||
|
public function test_update( $data ) {
|
||||||
|
$readme_content = Theme_Readme::get_content();
|
||||||
|
$readme = Theme_Readme::update( $data, $readme_content );
|
||||||
|
|
||||||
|
// Removes the newlines from the readme content to make it easier to search for strings.
|
||||||
|
$readme_without_newlines = str_replace( "\n", '', $readme );
|
||||||
|
|
||||||
|
$expected_author = 'Contributors: ' . $data['author'];
|
||||||
|
$expected_wp_version = 'Tested up to: ' . $data['wp_version'] ?? get_bloginfo( 'version' );
|
||||||
|
$expected_image_credits = '== Images ==' . $data['image_credits'];
|
||||||
|
$expected_recommended_plugins = '== Recommended Plugins ==' . $data['recommended_plugins'];
|
||||||
|
|
||||||
|
$this->assertStringContainsString( $expected_author, $readme_without_newlines, 'The expected author is missing.' );
|
||||||
|
$this->assertStringContainsString( $expected_wp_version, $readme_without_newlines, 'The expected WP version is missing.' );
|
||||||
|
$this->assertStringContainsString( $expected_image_credits, $readme_without_newlines, 'The expected image credits are missing.' );
|
||||||
|
$this->assertStringContainsString( $expected_recommended_plugins, $readme_without_newlines, 'The expected recommended plugins are missing.' );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function data_test_update() {
|
||||||
|
return array(
|
||||||
|
'complete data' => array(
|
||||||
|
'data' => array(
|
||||||
|
'description' => 'New theme description',
|
||||||
|
'author' => 'New theme author',
|
||||||
|
'wp_version' => '12.12',
|
||||||
|
'image_credits' => 'New image credits',
|
||||||
|
'recommended_plugins' => 'New recommended plugins',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// TODO: Add more test cases.
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
54
tests/data/themes/test-theme-readme/readme.txt
Normal file
54
tests/data/themes/test-theme-readme/readme.txt
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
=== Test Readme Theme ===
|
||||||
|
Contributors: wordpressdotorg
|
||||||
|
Requires at least: 6.5
|
||||||
|
Tested up to: 6.5
|
||||||
|
Requires PHP: 7.0
|
||||||
|
Stable tag: 2.1
|
||||||
|
License: GPLv2 or later
|
||||||
|
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
||||||
|
|
||||||
|
|
||||||
|
== Description ==
|
||||||
|
|
||||||
|
Test Readme Theme is a test theme created with the sole purpose of testing the Create_Block_Theme_Readme class.
|
||||||
|
|
||||||
|
|
||||||
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.0 =
|
||||||
|
* Initial release
|
||||||
|
|
||||||
|
= 1.1 =
|
||||||
|
* Added new feature
|
||||||
|
|
||||||
|
= 2.0 =
|
||||||
|
* Added new major feature
|
||||||
|
|
||||||
|
= 2.1 =
|
||||||
|
* Added new feature
|
||||||
|
|
||||||
|
|
||||||
|
== Copyright ==
|
||||||
|
|
||||||
|
Test Readme Theme WordPress Theme, (C) 2023 WordPress.org
|
||||||
|
Test Readme Theme is distributed under the terms of the GNU GPL.
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
|
||||||
|
=== Images ===
|
||||||
|
|
||||||
|
License: CC0 https://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
museum.webp - https://www.rawpixel.com/image/3297419/free-photo-image-interior-hallway-architecture
|
||||||
|
tourist-and-building.webp - https://www.rawpixel.com/image/5928004/photo-image-public-domain-hand-person
|
||||||
|
|
||||||
|
|
15
tests/data/themes/test-theme-readme/style.css
Normal file
15
tests/data/themes/test-theme-readme/style.css
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
Theme Name: Test Readme Theme
|
||||||
|
Theme URI: https://example.org/themes/test-readme-theme
|
||||||
|
Author: the WordPress team
|
||||||
|
Author URI: https://wordpress.org
|
||||||
|
Description: Test Readme Theme is a theme for testing the readme.txt file reading/writing capabilities of the Create Block Theme plugin.
|
||||||
|
Requires at least: 6.4
|
||||||
|
Tested up to: 6.4
|
||||||
|
Requires PHP: 7.0
|
||||||
|
Version: 1.0
|
||||||
|
License: GNU General Public License v2 or later
|
||||||
|
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
||||||
|
Text Domain: testreadmetheme
|
||||||
|
Tags: one-column, custom-colors, custom-menu, custom-logo, editor-style, featured-images, full-site-editing, block-patterns, rtl-language-support, sticky-post, threaded-comments, translation-ready, wide-blocks, block-styles, style-variations, accessibility-ready, blog, portfolio, news
|
||||||
|
*/
|
6
tests/data/themes/test-theme-readme/theme.json
Normal file
6
tests/data/themes/test-theme-readme/theme.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://schemas.wp.org/trunk/theme.json",
|
||||||
|
"version": 2,
|
||||||
|
"styles": {},
|
||||||
|
"settings": {}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue