PluginProbe
Child Theme Wizard / trunk
Child Theme Wizard vtrunk
trunk 1.0 1.1 1.2 1.3 1.4 1.5
child-theme-wizard / child-theme-wizard.php

child-theme-wizard.php in Child Theme Wizard trunk, at child-theme-wizard.php

347 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Child Theme Wizard
4 * Plugin URI: https://wpguru.tv
5 * Description: Creates a child theme from any installed parent theme.
6 * Version: 1.5
7 * Author: Jay Versluis
8 * Author URI: https://wpguru.tv
9 * License: GPL2
10 * License URI: http://www.gnu.org/licenses/gpl-2.0.html
11 * Text Domain: child-theme-wizard
12 */
13
14 /* Copyright 2013-2026 Jay Versluis (email support@wpguru.tv)
15
16 This program is free software; you can redistribute it and/or modify
17 it under the terms of the GNU General Public License, version 2, as
18 published by the Free Software Foundation.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 */
29
30 if ( ! defined( 'ABSPATH' ) ) {
31 exit;
32 }
33
34 add_action( 'admin_menu', 'ctw_menu' );
35
36 function ctw_menu(): void {
37 add_submenu_page(
38 'tools.php',
39 __( 'Child Theme Wizard', 'child-theme-wizard' ),
40 __( 'Child Theme Wizard', 'child-theme-wizard' ),
41 'manage_options',
42 'ChildThemeWizard',
43 'ctw_main_function'
44 );
45 }
46
47 function ctw_main_function(): void {
48
49 if ( ! current_user_can( 'manage_options' ) ) {
50 wp_die( esc_html__( 'You do not have sufficient privileges to access this page.', 'child-theme-wizard' ) );
51 }
52
53 ?>
54 <div class="wrap">
55 <h1><?php esc_html_e( 'Child Theme Wizard', 'child-theme-wizard' ); ?></h1>
56
57 <?php
58
59 if ( isset( $_POST['hiddenfield'] ) && $_POST['hiddenfield'] === 'Y' ) {
60
61 // Verify nonce — blocks CSRF attacks.
62 check_admin_referer( 'ctw_create_child_theme', 'ctw_nonce' );
63
64 // Validate parent against actually-installed parent themes.
65 $parent_themes = ctw_giveme_parents();
66 $valid_parents = array_map( fn( $t ) => $t->get_stylesheet(), $parent_themes );
67 $submitted_parent = sanitize_text_field( wp_unslash( $_POST['parent'] ?? '' ) );
68
69 if ( ! in_array( $submitted_parent, $valid_parents, true ) ) {
70 wp_die( esc_html__( 'Invalid parent theme selected.', 'child-theme-wizard' ) );
71 }
72
73 $newchildtheme = array(
74 'parent' => $submitted_parent,
75 'title' => sanitize_text_field( wp_unslash( $_POST['title'] ?? '' ) ),
76 'description' => sanitize_text_field( wp_unslash( $_POST['description'] ?? '' ) ),
77 'url' => esc_url_raw( wp_unslash( $_POST['url'] ?? '' ) ),
78 'author' => sanitize_text_field( wp_unslash( $_POST['author'] ?? '' ) ),
79 'author-url' => esc_url_raw( wp_unslash( $_POST['author-url'] ?? '' ) ),
80 'version' => sanitize_text_field( wp_unslash( $_POST['version'] ?? '' ) ),
81 'include-gpl' => ( isset( $_POST['include-gpl'] ) && $_POST['include-gpl'] === 'on' ) ? 'on' : 'off',
82 );
83
84 $status = ctw_create_theme( $newchildtheme );
85
86 if ( $status['alert'] === -1 ) {
87 ?>
88 <div class="notice notice-error">
89 <p><strong><?php esc_html_e( 'Yikes — something went wrong:', 'child-theme-wizard' ); ?></strong></p>
90 <?php echo wp_kses_post( $status['message'] ); ?>
91 </div>
92 <p><a href="<?php echo esc_url( admin_url( 'tools.php?page=ChildThemeWizard' ) ); ?>"><?php esc_html_e( 'Try again?', 'child-theme-wizard' ); ?></a></p>
93 <?php
94 } else {
95 ?>
96 <div class="notice notice-success">
97 <?php echo wp_kses_post( $status['message'] ); ?>
98 <p><strong><?php esc_html_e( 'Your Child Theme was created successfully!', 'child-theme-wizard' ); ?></strong></p>
99 </div>
100 <p>
101 <?php
102 printf(
103 /* translators: 1: link to Appearance > Themes, 2: link to Appearance > Editor */
104 esc_html__( 'Head over to %1$s to activate it. Add your custom styles in %2$s.', 'child-theme-wizard' ),
105 '<a href="' . esc_url( admin_url( 'themes.php' ) ) . '">' . esc_html__( 'Appearance › Themes', 'child-theme-wizard' ) . '</a>',
106 '<a href="' . esc_url( admin_url( 'theme-editor.php' ) ) . '">' . esc_html__( 'Appearance › Editor', 'child-theme-wizard' ) . '</a>'
107 );
108 ?>
109 </p>
110 <?php
111 }
112 } else {
113
114 $current_user = wp_get_current_user();
115 $parent_themes = ctw_giveme_parents();
116 $current_theme = wp_get_theme();
117 ?>
118
119 <p>
120 <?php
121 printf(
122 /* translators: %s: link to WordPress child theme documentation */
123 esc_html__( 'This simple wizard will help you generate a new %s with just one click.', 'child-theme-wizard' ),
124 '<a href="https://developer.wordpress.org/themes/advanced-topics/child-themes/" target="_blank">' . esc_html__( 'Child Theme', 'child-theme-wizard' ) . '</a>'
125 );
126 ?>
127 </p>
128 <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>
129 <hr>
130
131 <form name="ctwform" method="post" action="">
132 <?php wp_nonce_field( 'ctw_create_child_theme', 'ctw_nonce' ); ?>
133 <input type="hidden" name="hiddenfield" value="Y">
134
135 <table class="form-table" role="presentation">
136 <tr>
137 <th scope="row"><label for="ctw-parent"><?php esc_html_e( 'Parent Theme', 'child-theme-wizard' ); ?></label></th>
138 <td>
139 <select name="parent" id="ctw-parent">
140 <?php foreach ( $parent_themes as $theme ) :
141 $stylesheet = $theme->get_stylesheet();
142 $name = $theme->get( 'Name' );
143 $is_active = $current_theme->get( 'Name' ) === $name;
144 ?>
145 <option value="<?php echo esc_attr( $stylesheet ); ?>"<?php selected( $is_active ); ?>>
146 <?php
147 echo esc_html( $name );
148 if ( $is_active ) {
149 echo ' (' . esc_html__( 'currently active', 'child-theme-wizard' ) . ')';
150 }
151 ?>
152 </option>
153 <?php endforeach; ?>
154 </select>
155 </td>
156 </tr>
157 <tr>
158 <th scope="row"><label for="ctw-title"><?php esc_html_e( 'Title', 'child-theme-wizard' ); ?></label></th>
159 <td>
160 <input type="text" name="title" id="ctw-title" value="" class="regular-text">
161 <p class="description"><?php esc_html_e( "What's your new Child Theme called?", 'child-theme-wizard' ); ?></p>
162 </td>
163 </tr>
164 <tr>
165 <th scope="row"><label for="ctw-description"><?php esc_html_e( 'Description', 'child-theme-wizard' ); ?></label></th>
166 <td>
167 <input type="text" name="description" id="ctw-description" value="" class="regular-text">
168 <p class="description"><?php esc_html_e( 'A few notes about your Child Theme.', 'child-theme-wizard' ); ?></p>
169 </td>
170 </tr>
171 <tr>
172 <th scope="row"><label for="ctw-url"><?php esc_html_e( 'Child Theme URL', 'child-theme-wizard' ); ?></label></th>
173 <td>
174 <input type="url" name="url" id="ctw-url" value="" class="regular-text">
175 <p class="description"><?php esc_html_e( 'Does it have a website or release post?', 'child-theme-wizard' ); ?></p>
176 </td>
177 </tr>
178 <tr>
179 <th scope="row"><label for="ctw-author"><?php esc_html_e( 'Author', 'child-theme-wizard' ); ?></label></th>
180 <td>
181 <input type="text" name="author" id="ctw-author" value="<?php echo esc_attr( $current_user->display_name ); ?>" class="regular-text">
182 <p class="description"><?php esc_html_e( "That's you.", 'child-theme-wizard' ); ?></p>
183 </td>
184 </tr>
185 <tr>
186 <th scope="row"><label for="ctw-author-url"><?php esc_html_e( 'Author URL', 'child-theme-wizard' ); ?></label></th>
187 <td>
188 <input type="url" name="author-url" id="ctw-author-url" value="<?php echo esc_url( $current_user->user_url ); ?>" class="regular-text">
189 <p class="description"><?php esc_html_e( "That's your website.", 'child-theme-wizard' ); ?></p>
190 </td>
191 </tr>
192 <tr>
193 <th scope="row"><label for="ctw-version"><?php esc_html_e( 'Version', 'child-theme-wizard' ); ?></label></th>
194 <td>
195 <input type="text" name="version" id="ctw-version" value="1.0" class="small-text">
196 <p class="description"><?php esc_html_e( 'Start with 1.0 and work your way up.', 'child-theme-wizard' ); ?></p>
197 </td>
198 </tr>
199 <tr>
200 <th scope="row"><label for="ctw-include-gpl"><?php esc_html_e( 'Include GPL License', 'child-theme-wizard' ); ?></label></th>
201 <td>
202 <select name="include-gpl" id="ctw-include-gpl">
203 <option value="on"><?php esc_html_e( 'Yes Please!', 'child-theme-wizard' ); ?></option>
204 <option value="off"><?php esc_html_e( 'No Thanks', 'child-theme-wizard' ); ?></option>
205 </select>
206 </td>
207 </tr>
208 </table>
209
210 <p class="submit">
211 <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Create Child Theme', 'child-theme-wizard' ); ?>">
212 </p>
213 </form>
214
215 <?php
216 }
217 ?>
218
219 <br><hr><br>
220 </div><!-- .wrap -->
221
222 <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>
223
224 <p>
225 <a href="https://wpguru.co.uk/2014/03/introducing-child-theme-wizard-for-wordpress/" target="_blank">Plugin by Jay Versluis</a>
226 | <a href="https://github.com/versluis/Child-Theme-Wizard" target="_blank">Fork me or Contribute on GitHub</a>
227 | <a href="https://patreon.com/versluis" target="_blank">Support me on Patreon</a>
228 </p>
229
230 <?php
231 }
232
233 function ctw_create_theme( array $childtheme ): array {
234
235 $status = array(
236 'alert' => 0,
237 'message' => '',
238 );
239
240 global $wp_filesystem;
241 if ( empty( $wp_filesystem ) ) {
242 require_once ABSPATH . 'wp-admin/includes/file.php';
243 WP_Filesystem();
244 }
245
246 $directory = get_theme_root() . '/' . sanitize_file_name( $childtheme['title'] );
247
248 if ( ! $wp_filesystem->mkdir( $directory ) ) {
249 $status['message'] = '<p>' . esc_html__( 'Could not create directory. Does it exist already?', 'child-theme-wizard' ) . '</p>';
250 $status['alert'] = -1;
251 return $status;
252 }
253
254 $status['message'] .= '<p>' . esc_html__( 'Directory created successfully.', 'child-theme-wizard' ) . '</p>';
255
256 // Strip */ so user-supplied values cannot break out of the CSS comment block.
257 $safe = array_map( fn( $v ) => str_replace( '*/', '', (string) $v ), $childtheme );
258
259 $style = "/* \n Theme Name: " . $safe['title'];
260 $style .= "\n Theme URI: " . $safe['url'];
261 $style .= "\n Description: " . $safe['description'];
262 $style .= "\n Author: " . $safe['author'];
263 $style .= "\n Author URI: " . $safe['author-url'];
264 $style .= "\n Template: " . $safe['parent'];
265 $style .= "\n Version: " . $safe['version'];
266
267 if ( $safe['include-gpl'] === 'on' ) {
268 $style .= "\n License: GNU General Public License v2 or later";
269 $style .= "\n License URI: http://www.gnu.org/licenses/gpl-2.0.html";
270 }
271
272 $style .= "\n\n /* == Add your own styles below this line ==";
273 $style .= "\n--------------------------------------------*/\n\n";
274
275 if ( ! $wp_filesystem->put_contents( $directory . '/style.css', $style, FS_CHMOD_FILE ) ) {
276 $status['message'] .= '<p>' . esc_html__( 'There was a problem writing data to style.css.', 'child-theme-wizard' ) . '</p>';
277 $status['alert'] = -1;
278 return $status;
279 }
280
281 $status['message'] .= '<p>' . esc_html__( 'Writing data to style.css…', 'child-theme-wizard' ) . '</p>';
282
283 $functions = "<?php\n/*\n\n This file is part of a child theme called " . $safe['title'] . ".";
284 $functions .= "\n Functions in this file will be loaded before the parent theme's functions.";
285 $functions .= "\n For more information, please read";
286 $functions .= "\n https://developer.wordpress.org/themes/advanced-topics/child-themes/";
287 $functions .= "\n\n*/\n\n";
288 $functions .= "// Loads the parent stylesheet — leave this in place unless you know what you're doing.\n";
289 $functions .= "function your_theme_enqueue_styles() {\n\n";
290 $functions .= " \$parent_style = 'parent-style';\n\n";
291 $functions .= " wp_enqueue_style( \$parent_style,\n";
292 $functions .= " get_template_directory_uri() . '/style.css'\n";
293 $functions .= " );\n\n";
294 $functions .= " wp_enqueue_style( 'child-style',\n";
295 $functions .= " get_stylesheet_directory_uri() . '/style.css',\n";
296 $functions .= " array( \$parent_style ),\n";
297 $functions .= " wp_get_theme()->get( 'Version' )\n";
298 $functions .= " );\n";
299 $functions .= "}\n";
300 $functions .= "add_action( 'wp_enqueue_scripts', 'your_theme_enqueue_styles' );\n\n";
301 $functions .= "/* Add your own functions below this line.\n";
302 $functions .= " ======================================== */\n\n";
303
304 if ( ! $wp_filesystem->put_contents( $directory . '/functions.php', $functions, FS_CHMOD_FILE ) ) {
305 $status['message'] .= '<p>' . esc_html__( 'There was a problem writing data to functions.php.', 'child-theme-wizard' ) . '</p>';
306 $status['alert'] = -1;
307 return $status;
308 }
309
310 $status['message'] .= '<p>' . esc_html__( 'Writing data to functions.php…', 'child-theme-wizard' ) . '</p>';
311
312 $status['message'] .= ctw_make_thumbnail( $directory );
313
314 return $status;
315 }
316
317 function ctw_giveme_parents(): array {
318
319 $parent_themes = array();
320
321 foreach ( wp_get_themes() as $theme ) {
322 if ( $theme->parent() === false ) {
323 $parent_themes[] = $theme;
324 }
325 }
326
327 return $parent_themes;
328 }
329
330 function ctw_make_thumbnail( string $childpath ): string {
331
332 global $wp_filesystem;
333 if ( empty( $wp_filesystem ) ) {
334 require_once ABSPATH . 'wp-admin/includes/file.php';
335 WP_Filesystem();
336 }
337
338 $source = plugin_dir_path( __FILE__ ) . 'images/screenshot.png';
339 $destination = $childpath . '/screenshot.png';
340
341 if ( ! $wp_filesystem->copy( $source, $destination ) ) {
342 return '<p>' . esc_html__( 'Could not copy thumbnail image.', 'child-theme-wizard' ) . '</p>';
343 }
344
345 return '<p>' . esc_html__( 'Copied thumbnail file — looking good!', 'child-theme-wizard' ) . '</p>';
346 }
347