initial
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Child Theme Wizard
|
||||
* Plugin URI: https://wpguru.tv
|
||||
* Description: Creates a child theme from any installed parent theme.
|
||||
* Version: 1.5
|
||||
* Author: Jay Versluis
|
||||
* Author URI: https://wpguru.tv
|
||||
* License: GPL2
|
||||
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
* Text Domain: child-theme-wizard
|
||||
*/
|
||||
|
||||
/* Copyright 2013-2026 Jay Versluis (email support@wpguru.tv)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2, as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
add_action( 'admin_menu', 'ctw_menu' );
|
||||
|
||||
function ctw_menu(): void {
|
||||
add_submenu_page(
|
||||
'tools.php',
|
||||
__( 'Child Theme Wizard', 'child-theme-wizard' ),
|
||||
__( 'Child Theme Wizard', 'child-theme-wizard' ),
|
||||
'manage_options',
|
||||
'ChildThemeWizard',
|
||||
'ctw_main_function'
|
||||
);
|
||||
}
|
||||
|
||||
function ctw_main_function(): void {
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( esc_html__( 'You do not have sufficient privileges to access this page.', 'child-theme-wizard' ) );
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e( 'Child Theme Wizard', 'child-theme-wizard' ); ?></h1>
|
||||
|
||||
<?php
|
||||
|
||||
if ( isset( $_POST['hiddenfield'] ) && $_POST['hiddenfield'] === 'Y' ) {
|
||||
|
||||
// Verify nonce — blocks CSRF attacks.
|
||||
check_admin_referer( 'ctw_create_child_theme', 'ctw_nonce' );
|
||||
|
||||
// Validate parent against actually-installed parent themes.
|
||||
$parent_themes = ctw_giveme_parents();
|
||||
$valid_parents = array_map( fn( $t ) => $t->get_stylesheet(), $parent_themes );
|
||||
$submitted_parent = sanitize_text_field( wp_unslash( $_POST['parent'] ?? '' ) );
|
||||
|
||||
if ( ! in_array( $submitted_parent, $valid_parents, true ) ) {
|
||||
wp_die( esc_html__( 'Invalid parent theme selected.', 'child-theme-wizard' ) );
|
||||
}
|
||||
|
||||
$newchildtheme = array(
|
||||
'parent' => $submitted_parent,
|
||||
'title' => sanitize_text_field( wp_unslash( $_POST['title'] ?? '' ) ),
|
||||
'description' => sanitize_text_field( wp_unslash( $_POST['description'] ?? '' ) ),
|
||||
'url' => esc_url_raw( wp_unslash( $_POST['url'] ?? '' ) ),
|
||||
'author' => sanitize_text_field( wp_unslash( $_POST['author'] ?? '' ) ),
|
||||
'author-url' => esc_url_raw( wp_unslash( $_POST['author-url'] ?? '' ) ),
|
||||
'version' => sanitize_text_field( wp_unslash( $_POST['version'] ?? '' ) ),
|
||||
'include-gpl' => ( isset( $_POST['include-gpl'] ) && $_POST['include-gpl'] === 'on' ) ? 'on' : 'off',
|
||||
);
|
||||
|
||||
$status = ctw_create_theme( $newchildtheme );
|
||||
|
||||
if ( $status['alert'] === -1 ) {
|
||||
?>
|
||||
<div class="notice notice-error">
|
||||
<p><strong><?php esc_html_e( 'Yikes — something went wrong:', 'child-theme-wizard' ); ?></strong></p>
|
||||
<?php echo wp_kses_post( $status['message'] ); ?>
|
||||
</div>
|
||||
<p><a href="<?php echo esc_url( admin_url( 'tools.php?page=ChildThemeWizard' ) ); ?>"><?php esc_html_e( 'Try again?', 'child-theme-wizard' ); ?></a></p>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<div class="notice notice-success">
|
||||
<?php echo wp_kses_post( $status['message'] ); ?>
|
||||
<p><strong><?php esc_html_e( 'Your Child Theme was created successfully!', 'child-theme-wizard' ); ?></strong></p>
|
||||
</div>
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: 1: link to Appearance > Themes, 2: link to Appearance > Editor */
|
||||
esc_html__( 'Head over to %1$s to activate it. Add your custom styles in %2$s.', 'child-theme-wizard' ),
|
||||
'<a href="' . esc_url( admin_url( 'themes.php' ) ) . '">' . esc_html__( 'Appearance › Themes', 'child-theme-wizard' ) . '</a>',
|
||||
'<a href="' . esc_url( admin_url( 'theme-editor.php' ) ) . '">' . esc_html__( 'Appearance › Editor', 'child-theme-wizard' ) . '</a>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
} else {
|
||||
|
||||
$current_user = wp_get_current_user();
|
||||
$parent_themes = ctw_giveme_parents();
|
||||
$current_theme = wp_get_theme();
|
||||
?>
|
||||
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %s: link to WordPress child theme documentation */
|
||||
esc_html__( 'This simple wizard will help you generate a new %s with just one click.', 'child-theme-wizard' ),
|
||||
'<a href="https://developer.wordpress.org/themes/advanced-topics/child-themes/" target="_blank">' . esc_html__( 'Child Theme', 'child-theme-wizard' ) . '</a>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<p><?php esc_html_e( 'Select which theme you want to use as a base, fill in the details and click "Create Child Theme".', 'child-theme-wizard' ); ?></p>
|
||||
<hr>
|
||||
|
||||
<form name="ctwform" method="post" action="">
|
||||
<?php wp_nonce_field( 'ctw_create_child_theme', 'ctw_nonce' ); ?>
|
||||
<input type="hidden" name="hiddenfield" value="Y">
|
||||
|
||||
<table class="form-table" role="presentation">
|
||||
<tr>
|
||||
<th scope="row"><label for="ctw-parent"><?php esc_html_e( 'Parent Theme', 'child-theme-wizard' ); ?></label></th>
|
||||
<td>
|
||||
<select name="parent" id="ctw-parent">
|
||||
<?php foreach ( $parent_themes as $theme ) :
|
||||
$stylesheet = $theme->get_stylesheet();
|
||||
$name = $theme->get( 'Name' );
|
||||
$is_active = $current_theme->get( 'Name' ) === $name;
|
||||
?>
|
||||
<option value="<?php echo esc_attr( $stylesheet ); ?>"<?php selected( $is_active ); ?>>
|
||||
<?php
|
||||
echo esc_html( $name );
|
||||
if ( $is_active ) {
|
||||
echo ' (' . esc_html__( 'currently active', 'child-theme-wizard' ) . ')';
|
||||
}
|
||||
?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="ctw-title"><?php esc_html_e( 'Title', 'child-theme-wizard' ); ?></label></th>
|
||||
<td>
|
||||
<input type="text" name="title" id="ctw-title" value="" class="regular-text">
|
||||
<p class="description"><?php esc_html_e( "What's your new Child Theme called?", 'child-theme-wizard' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="ctw-description"><?php esc_html_e( 'Description', 'child-theme-wizard' ); ?></label></th>
|
||||
<td>
|
||||
<input type="text" name="description" id="ctw-description" value="" class="regular-text">
|
||||
<p class="description"><?php esc_html_e( 'A few notes about your Child Theme.', 'child-theme-wizard' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="ctw-url"><?php esc_html_e( 'Child Theme URL', 'child-theme-wizard' ); ?></label></th>
|
||||
<td>
|
||||
<input type="url" name="url" id="ctw-url" value="" class="regular-text">
|
||||
<p class="description"><?php esc_html_e( 'Does it have a website or release post?', 'child-theme-wizard' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="ctw-author"><?php esc_html_e( 'Author', 'child-theme-wizard' ); ?></label></th>
|
||||
<td>
|
||||
<input type="text" name="author" id="ctw-author" value="<?php echo esc_attr( $current_user->display_name ); ?>" class="regular-text">
|
||||
<p class="description"><?php esc_html_e( "That's you.", 'child-theme-wizard' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="ctw-author-url"><?php esc_html_e( 'Author URL', 'child-theme-wizard' ); ?></label></th>
|
||||
<td>
|
||||
<input type="url" name="author-url" id="ctw-author-url" value="<?php echo esc_url( $current_user->user_url ); ?>" class="regular-text">
|
||||
<p class="description"><?php esc_html_e( "That's your website.", 'child-theme-wizard' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="ctw-version"><?php esc_html_e( 'Version', 'child-theme-wizard' ); ?></label></th>
|
||||
<td>
|
||||
<input type="text" name="version" id="ctw-version" value="1.0" class="small-text">
|
||||
<p class="description"><?php esc_html_e( 'Start with 1.0 and work your way up.', 'child-theme-wizard' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><label for="ctw-include-gpl"><?php esc_html_e( 'Include GPL License', 'child-theme-wizard' ); ?></label></th>
|
||||
<td>
|
||||
<select name="include-gpl" id="ctw-include-gpl">
|
||||
<option value="on"><?php esc_html_e( 'Yes Please!', 'child-theme-wizard' ); ?></option>
|
||||
<option value="off"><?php esc_html_e( 'No Thanks', 'child-theme-wizard' ); ?></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="submit">
|
||||
<input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Create Child Theme', 'child-theme-wizard' ); ?>">
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
<br><hr><br>
|
||||
</div><!-- .wrap -->
|
||||
|
||||
<p><a href="https://wpguru.co.uk" target="_blank"><img src="<?php echo esc_url( plugins_url( 'images/guru-header-2013.png', __FILE__ ) ); ?>" width="300" alt="WP Guru"></a></p>
|
||||
|
||||
<p>
|
||||
<a href="https://wpguru.co.uk/2014/03/introducing-child-theme-wizard-for-wordpress/" target="_blank">Plugin by Jay Versluis</a>
|
||||
| <a href="https://github.com/versluis/Child-Theme-Wizard" target="_blank">Fork me or Contribute on GitHub</a>
|
||||
| <a href="https://patreon.com/versluis" target="_blank">Support me on Patreon</a>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
function ctw_create_theme( array $childtheme ): array {
|
||||
|
||||
$status = array(
|
||||
'alert' => 0,
|
||||
'message' => '',
|
||||
);
|
||||
|
||||
global $wp_filesystem;
|
||||
if ( empty( $wp_filesystem ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
WP_Filesystem();
|
||||
}
|
||||
|
||||
$directory = get_theme_root() . '/' . sanitize_file_name( $childtheme['title'] );
|
||||
|
||||
if ( ! $wp_filesystem->mkdir( $directory ) ) {
|
||||
$status['message'] = '<p>' . esc_html__( 'Could not create directory. Does it exist already?', 'child-theme-wizard' ) . '</p>';
|
||||
$status['alert'] = -1;
|
||||
return $status;
|
||||
}
|
||||
|
||||
$status['message'] .= '<p>' . esc_html__( 'Directory created successfully.', 'child-theme-wizard' ) . '</p>';
|
||||
|
||||
// Strip */ so user-supplied values cannot break out of the CSS comment block.
|
||||
$safe = array_map( fn( $v ) => str_replace( '*/', '', (string) $v ), $childtheme );
|
||||
|
||||
$style = "/* \n Theme Name: " . $safe['title'];
|
||||
$style .= "\n Theme URI: " . $safe['url'];
|
||||
$style .= "\n Description: " . $safe['description'];
|
||||
$style .= "\n Author: " . $safe['author'];
|
||||
$style .= "\n Author URI: " . $safe['author-url'];
|
||||
$style .= "\n Template: " . $safe['parent'];
|
||||
$style .= "\n Version: " . $safe['version'];
|
||||
|
||||
if ( $safe['include-gpl'] === 'on' ) {
|
||||
$style .= "\n License: GNU General Public License v2 or later";
|
||||
$style .= "\n License URI: http://www.gnu.org/licenses/gpl-2.0.html";
|
||||
}
|
||||
|
||||
$style .= "\n\n /* == Add your own styles below this line ==";
|
||||
$style .= "\n--------------------------------------------*/\n\n";
|
||||
|
||||
if ( ! $wp_filesystem->put_contents( $directory . '/style.css', $style, FS_CHMOD_FILE ) ) {
|
||||
$status['message'] .= '<p>' . esc_html__( 'There was a problem writing data to style.css.', 'child-theme-wizard' ) . '</p>';
|
||||
$status['alert'] = -1;
|
||||
return $status;
|
||||
}
|
||||
|
||||
$status['message'] .= '<p>' . esc_html__( 'Writing data to style.css…', 'child-theme-wizard' ) . '</p>';
|
||||
|
||||
$functions = "<?php\n/*\n\n This file is part of a child theme called " . $safe['title'] . ".";
|
||||
$functions .= "\n Functions in this file will be loaded before the parent theme's functions.";
|
||||
$functions .= "\n For more information, please read";
|
||||
$functions .= "\n https://developer.wordpress.org/themes/advanced-topics/child-themes/";
|
||||
$functions .= "\n\n*/\n\n";
|
||||
$functions .= "// Loads the parent stylesheet — leave this in place unless you know what you're doing.\n";
|
||||
$functions .= "function your_theme_enqueue_styles() {\n\n";
|
||||
$functions .= " \$parent_style = 'parent-style';\n\n";
|
||||
$functions .= " wp_enqueue_style( \$parent_style,\n";
|
||||
$functions .= " get_template_directory_uri() . '/style.css'\n";
|
||||
$functions .= " );\n\n";
|
||||
$functions .= " wp_enqueue_style( 'child-style',\n";
|
||||
$functions .= " get_stylesheet_directory_uri() . '/style.css',\n";
|
||||
$functions .= " array( \$parent_style ),\n";
|
||||
$functions .= " wp_get_theme()->get( 'Version' )\n";
|
||||
$functions .= " );\n";
|
||||
$functions .= "}\n";
|
||||
$functions .= "add_action( 'wp_enqueue_scripts', 'your_theme_enqueue_styles' );\n\n";
|
||||
$functions .= "/* Add your own functions below this line.\n";
|
||||
$functions .= " ======================================== */\n\n";
|
||||
|
||||
if ( ! $wp_filesystem->put_contents( $directory . '/functions.php', $functions, FS_CHMOD_FILE ) ) {
|
||||
$status['message'] .= '<p>' . esc_html__( 'There was a problem writing data to functions.php.', 'child-theme-wizard' ) . '</p>';
|
||||
$status['alert'] = -1;
|
||||
return $status;
|
||||
}
|
||||
|
||||
$status['message'] .= '<p>' . esc_html__( 'Writing data to functions.php…', 'child-theme-wizard' ) . '</p>';
|
||||
|
||||
$status['message'] .= ctw_make_thumbnail( $directory );
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
function ctw_giveme_parents(): array {
|
||||
|
||||
$parent_themes = array();
|
||||
|
||||
foreach ( wp_get_themes() as $theme ) {
|
||||
if ( $theme->parent() === false ) {
|
||||
$parent_themes[] = $theme;
|
||||
}
|
||||
}
|
||||
|
||||
return $parent_themes;
|
||||
}
|
||||
|
||||
function ctw_make_thumbnail( string $childpath ): string {
|
||||
|
||||
global $wp_filesystem;
|
||||
if ( empty( $wp_filesystem ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
WP_Filesystem();
|
||||
}
|
||||
|
||||
$source = plugin_dir_path( __FILE__ ) . 'images/screenshot.png';
|
||||
$destination = $childpath . '/screenshot.png';
|
||||
|
||||
if ( ! $wp_filesystem->copy( $source, $destination ) ) {
|
||||
return '<p>' . esc_html__( 'Could not copy thumbnail image.', 'child-theme-wizard' ) . '</p>';
|
||||
}
|
||||
|
||||
return '<p>' . esc_html__( 'Copied thumbnail file — looking good!', 'child-theme-wizard' ) . '</p>';
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
Binary file not shown.
@@ -0,0 +1,70 @@
|
||||
=== Child Theme Wizard ===
|
||||
Contributors: versluis
|
||||
Donate link: https://patreon.com/versluis
|
||||
Tags: child theme, generator, creator, one click, starter
|
||||
Requires at least: 5.0
|
||||
Tested up to: 7.0
|
||||
Requires PHP: 7.4
|
||||
Stable tag: 1.5
|
||||
License: GPLv2 or later
|
||||
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
|
||||
Creates a child theme from any installed parent theme with just one click.
|
||||
|
||||
== Description ==
|
||||
|
||||
The Child Theme Wizard lets you create a new child theme without the need for additional tools, right from within the WordPress admin interface. Once activated you can find it under Tools - Child Theme Wizard.
|
||||
|
||||
Specify a parent theme, customise options such as title and description and click Create Child Theme. Upon success you will find your new theme under Appearance - Themes.
|
||||
|
||||
You have the option to include GPL License Terms if you wish. The Wizard will automatically create a thumbnail too.
|
||||
|
||||
To find out more about child themes and why they are useful, please read the [WordPress Developer Docs on Child Themes](https://developer.wordpress.org/themes/advanced-topics/child-themes/).
|
||||
|
||||
== Installation ==
|
||||
|
||||
1. Either: Upload the entire folder `child-theme-wizard` to the `/wp-content/plugins/` directory.
|
||||
1. Or: download the ZIP file, then head over to Plugins - Add New - Upload Plugin, then browse to your file.
|
||||
1. Or: from Plugins - Add New, search for "child theme wizard" to find this plugin and hit Install.
|
||||
1. Then: Activate the plugin through the Plugins menu in WordPress.
|
||||
1. You can find it under Tools - Child Theme Wizard.
|
||||
|
||||
== Screenshots ==
|
||||
|
||||
1. Create a Child Theme with just one click.
|
||||
2. The wizard completed successfully.
|
||||
3. Your new child theme appears under Appearance - Themes with a thumbnail.
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 1.5 =
|
||||
* Security: added CSRF nonce protection to the creation form
|
||||
* Security: parent theme selection now validated server-side against installed themes
|
||||
* Security: all HTML output is now properly escaped
|
||||
* Fixed: incorrect admin URL argument that broke the "Try again" link
|
||||
* Fixed: missing closing </form> tag
|
||||
* Fixed: thumbnail copy now uses the filesystem path instead of an HTTP URL, fixing failures on hosts with allow_url_fopen disabled
|
||||
* Fixed: uninstall.php guard order corrected per WordPress standards
|
||||
* Replaced direct PHP file functions (fopen, fwrite, fclose, mkdir, copy) with WP_Filesystem API
|
||||
* Added PHP 8.2 return type declarations
|
||||
* Removed unused ctw_testing() function
|
||||
* Form layout updated to use WordPress standard form-table markup
|
||||
* Tested with WordPress 7.0
|
||||
|
||||
= 1.4 =
|
||||
* Defined the previously undefined variable $parent_style (thanks, Marcin!)
|
||||
* Updated links to WordPress Dev Docs
|
||||
|
||||
= 1.3 =
|
||||
* Tested compatibility with WordPress 5.1
|
||||
* Updated link to Codex
|
||||
* Added version query as suggested in Codex as of 2019
|
||||
|
||||
= 1.2 =
|
||||
* Corrected a spelling mistake (thanks, Cory!)
|
||||
|
||||
= 1.1 =
|
||||
* The parent theme stylesheet is now enqueued via functions.php
|
||||
|
||||
= 1.0 =
|
||||
* Initial release
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
// Direct file access guard must come first.
|
||||
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// This plugin stores no options in the database, so there is nothing to clean up.
|
||||
Reference in New Issue
Block a user