Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add/child theme creation #531

Merged
merged 2 commits into from
Apr 2, 2024
Merged
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
51 changes: 51 additions & 0 deletions admin/create-theme/theme-create.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,57 @@ public static function clone_current_theme( $theme ) {
}
}

public static function create_child_theme( $theme, $screenshot ) {

// Create theme directory.
$new_theme_path = get_theme_root() . DIRECTORY_SEPARATOR . $theme['slug'];

if ( $theme['subfolder'] ) {
$new_theme_path = get_theme_root() . DIRECTORY_SEPARATOR . $theme['subfolder'] . DIRECTORY_SEPARATOR . $theme['slug'];
}

if ( file_exists( $new_theme_path ) ) {
return new WP_Error( 'theme_already_exists', __( 'Theme already exists.', 'create-block-theme' ) );
}

wp_mkdir_p( $new_theme_path );

// Add readme.txt.
file_put_contents(
$new_theme_path . DIRECTORY_SEPARATOR . 'readme.txt',
Theme_Readme::build_readme_txt( $theme )
);

// Add style.css.
$theme['template'] = wp_get_theme()->get( 'TextDomain' );
$css_contents = Theme_Styles::build_style_css( $theme );
file_put_contents(
$new_theme_path . DIRECTORY_SEPARATOR . 'style.css',
$css_contents
);

// Add theme.json
Theme_Templates::add_templates_to_local( 'user', $new_theme_path, $theme['slug'] );
file_put_contents( $new_theme_path . DIRECTORY_SEPARATOR . 'theme.json', MY_Theme_JSON_Resolver::export_theme_data( 'variation' ) );

// Add Screenshot
if ( static::is_valid_screenshot( $screenshot ) ) {
file_put_contents(
$new_theme_path . DIRECTORY_SEPARATOR . 'screenshot.png',
file_get_contents( $screenshot['tmp_name'] )
);
} else {
$source = plugin_dir_path( __DIR__ ) . '../assets/boilerplate/screenshot.png';
copy( $source, $new_theme_path . DIRECTORY_SEPARATOR . 'screenshot.png' );
}

if ( $theme['subfolder'] ) {
switch_theme( $theme['subfolder'] . '/' . $theme['slug'] );
} else {
switch_theme( $theme['slug'] );
}
}

public static function create_blank_theme( $theme, $screenshot ) {

// Create theme directory.
Expand Down
8 changes: 5 additions & 3 deletions admin/create-theme/theme-styles.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@ public static function build_style_css( $theme ) {
$author = stripslashes( $theme['author'] );
$author_uri = $theme['author_uri'];
$wp_version = get_bloginfo( 'version' );
$template = $theme['template'];
$text_domain = sanitize_title( $name );
$version = '1.0.0';
$tags = Theme_Tags::theme_tags_list( $theme );
if ( isset( $theme['template'] ) ) {
$template = $theme['template'];
}
$version = '1.0.0';
$tags = Theme_Tags::theme_tags_list( $theme );

if ( isset( $theme['version'] ) ) {
$version = $theme['version'];
Expand Down
21 changes: 12 additions & 9 deletions admin/create-theme/theme-templates.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,21 +186,16 @@ public static function add_templates_to_local( $export_type, $path = null, $slug
$template_part_dir = $base_dir . DIRECTORY_SEPARATOR . $template_folders['wp_template_part'];
$patterns_dir = $base_dir . DIRECTORY_SEPARATOR . 'patterns';

// If there is no templates folder, create it.
if ( ! is_dir( $template_dir ) ) {
// If there is no templates folder, and it is needed, create it.
if ( ! is_dir( $template_dir ) && count( $theme_templates->templates ) > 0 ) {
wp_mkdir_p( $template_dir );
}

// If there is no parts folder, create it.
if ( ! is_dir( $template_part_dir ) ) {
// If there is no parts folder, and it is needed, create it.
if ( ! is_dir( $template_part_dir ) && count( $theme_templates->parts ) > 0 ) {
wp_mkdir_p( $template_part_dir );
}

// If there is no patterns folder, create it.
if ( ! is_dir( $patterns_dir ) ) {
wp_mkdir_p( $patterns_dir );
}

foreach ( $theme_templates->templates as $template ) {

$template = self::prepare_template_for_export( $template, $slug );
Expand All @@ -218,6 +213,10 @@ public static function add_templates_to_local( $export_type, $path = null, $slug

// Write the pattern if it exists
if ( isset( $template->pattern ) ) {
// If there is no patterns folder, create it.
if ( ! is_dir( $patterns_dir ) ) {
wp_mkdir_p( $patterns_dir );
}
file_put_contents(
$patterns_dir . DIRECTORY_SEPARATOR . $template->slug . '.php',
$template->pattern
Expand All @@ -242,6 +241,10 @@ public static function add_templates_to_local( $export_type, $path = null, $slug

// Write the pattern if it exists
if ( isset( $template->pattern ) ) {
// If there is no patterns folder, create it.
if ( ! is_dir( $patterns_dir ) ) {
wp_mkdir_p( $patterns_dir );
}
file_put_contents(
$patterns_dir . DIRECTORY_SEPARATOR . $template->slug . '.php',
$template->pattern
Expand Down
92 changes: 92 additions & 0 deletions includes/class-create-block-theme-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ public function register_rest_routes() {
},
)
);
register_rest_route(
'create-block-theme/v1',
'/create-child',
array(
'methods' => 'POST',
'callback' => array( $this, 'rest_create_child_theme' ),
'permission_callback' => function () {
return current_user_can( 'edit_theme_options' );
},
)
);
register_rest_route(
'create-block-theme/v1',
'/export-clone',
Expand All @@ -86,6 +97,17 @@ public function register_rest_routes() {
},
)
);
register_rest_route(
'create-block-theme/v1',
'/export-child-clone',
array(
'methods' => 'POST',
'callback' => array( $this, 'rest_export_child_cloned_theme' ),
'permission_callback' => function () {
return current_user_can( 'edit_theme_options' );
},
)
);
register_rest_route(
'create-block-theme/v1',
'/get-readme-data',
Expand Down Expand Up @@ -135,6 +157,26 @@ function rest_clone_theme( $request ) {
);
}

function rest_create_child_theme( $request ) {

$theme = $this->sanitize_theme_data( $request->get_params() );
//TODO: Handle screenshots
$screenshot = null;

$response = Theme_Create::create_child_theme( $theme, $screenshot );

if ( is_wp_error( $response ) ) {
return $response;
}

return new WP_REST_Response(
array(
'status' => 'SUCCESS',
'message' => __( 'Child Theme Created.', 'create-block-theme' ),
)
);
}

function rest_create_blank_theme( $request ) {

$theme = $this->sanitize_theme_data( $request->get_params() );
Expand Down Expand Up @@ -206,6 +248,56 @@ function rest_export_cloned_theme( $request ) {
echo readfile( $filename );
}

function rest_export_child_cloned_theme( $request ) {

//TODO: Handle Screenshots
$screenshot = null;
$theme = $this->sanitize_theme_data( $request->get_params() );

// Create ZIP file in the temporary directory.
$filename = tempnam( get_temp_dir(), $theme['slug'] );
$zip = Theme_Zip::create_zip( $filename, $theme['slug'] );

$zip = Theme_Zip::add_templates_to_zip( $zip, 'user', $theme['slug'] );
$zip = Theme_Zip::add_theme_json_to_zip( $zip, 'variation' );

// Add readme.txt.
$zip->addFromStringToTheme(
'readme.txt',
Theme_Readme::build_readme_txt( $theme )
);

// Build style.css with new theme metadata
$theme['template'] = wp_get_theme()->get( 'TextDomain' );
$css_contents = Theme_Styles::build_style_css( $theme );
$zip->addFromStringToTheme(
'style.css',
$css_contents
);

// Add / replace screenshot.
if ( Theme_Utils::is_valid_screenshot( $screenshot ) ) {
$zip->addFileToTheme(
$screenshot['tmp_name'],
'screenshot.png'
);
} else {
$source = plugin_dir_path( __DIR__ ) . 'assets/boilerplate/screenshot.png';
$zip->addFileToTheme(
$source,
'screenshot.png'
);
}

$zip->close();

header( 'Content-Type: application/zip' );
header( 'Content-Disposition: attachment; filename=' . $theme['slug'] . '.zip' );
header( 'Content-Length: ' . filesize( $filename ) );
flush();
echo readfile( $filename );
}

/**
* Export the theme as a ZIP file.
*/
Expand Down
93 changes: 93 additions & 0 deletions src/editor-sidebar/create-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,36 @@ export const CreateThemePanel = () => {
exportCloneTheme();
};

const handleExportChildClick = () => {
const fetchOptions = {
path: '/create-block-theme/v1/export-child-clone',
method: 'POST',
data: theme,
headers: {
'Content-Type': 'application/json',
},
parse: false,
};

async function exportCloneTheme() {
try {
const response = await apiFetch( fetchOptions );
downloadFile( response );
} catch ( error ) {
const errorMessage =
error.message && error.code !== 'unknown_error'
? error.message
: __(
'An error occurred while attempting to export the child theme.',
'create-block-theme'
);
createErrorNotice( errorMessage, { type: 'snackbar' } );
}
}

exportCloneTheme();
};

const handleCreateBlankClick = () => {
apiFetch( {
path: '/create-block-theme/v1/create-blank',
Expand Down Expand Up @@ -142,6 +172,36 @@ export const CreateThemePanel = () => {
} );
};

const handleCreateChildClick = () => {
apiFetch( {
path: '/create-block-theme/v1/create-child',
method: 'POST',
data: theme,
headers: {
'Content-Type': 'application/json',
},
} )
.then( () => {
// eslint-disable-next-line
alert(
__(
'Child theme created successfully. The editor will now reload.',
'create-block-theme'
)
);
window.location.reload();
} )
.catch( ( error ) => {
const errorMessage =
error.message ||
__(
'An error occurred while attempting to create the theme.',
'create-block-theme'
);
createErrorNotice( errorMessage, { type: 'snackbar' } );
} );
};

return (
<PanelBody>
<Heading>
Expand Down Expand Up @@ -234,6 +294,23 @@ export const CreateThemePanel = () => {
'create-block-theme'
) }
</Text>
<hr></hr>
<Spacer />
<Button
icon={ copy }
variant="secondary"
onClick={ handleCreateChildClick }
>
{ __( 'Create Child Theme', 'create-block-theme' ) }
</Button>
<Spacer />
<Text variant="muted">
{ __(
'Create a child theme on the server and activate it. The user changes will be preserved in the new theme.',
'create-block-theme'
) }
</Text>

<hr></hr>
<Spacer />
<Button
Expand All @@ -252,6 +329,22 @@ export const CreateThemePanel = () => {
</Text>
<hr></hr>
<Spacer />
<Button
icon={ download }
variant="secondary"
onClick={ handleExportChildClick }
>
{ __( 'Export Child Theme', 'create-block-theme' ) }
</Button>
<Spacer />
<Text variant="muted">
{ __(
'Export a child of this theme as a .zip file. The user changes will be preserved in the new theme.',
'create-block-theme'
) }
</Text>
<hr></hr>
<Spacer />
<Button
icon={ addCard }
variant="secondary"
Expand Down
Loading