Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 4 additions & 37 deletions includes/class-create-block-theme-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
require_once __DIR__ . '/create-theme/theme-readme.php';
require_once __DIR__ . '/create-theme/theme-fonts.php';
require_once __DIR__ . '/create-theme/theme-create.php';
require_once __DIR__ . '/create-theme/theme-save.php';
require_once __DIR__ . '/create-theme/theme-settings-save.php';

/**
Expand Down Expand Up @@ -362,46 +363,12 @@ function rest_update_theme( $request ) {
* Save the user changes to the theme and clear user changes.
*/
function rest_save_theme( $request ) {
$result = CBT_Theme_Save::run( $request->get_params() );

$options = $request->get_params();

if ( isset( $options['saveFonts'] ) && true === $options['saveFonts'] ) {
CBT_Theme_Fonts::persist_font_settings();
}

if ( isset( $options['saveTemplates'] ) && true === $options['saveTemplates'] ) {
if ( true === $options['processOnlySavedTemplates'] ) {
CBT_Theme_Templates::add_templates_to_local( 'user', null, null, $options );
} else {
if ( is_child_theme() ) {
CBT_Theme_Templates::add_templates_to_local( 'current', null, null, $options );
} else {
CBT_Theme_Templates::add_templates_to_local( 'all', null, null, $options );
}
}
CBT_Theme_Templates::clear_user_templates_customizations();
CBT_Theme_Templates::clear_user_template_parts_customizations();
}

if ( isset( $options['saveStyle'] ) && true === $options['saveStyle'] ) {
if ( is_child_theme() ) {
CBT_Theme_JSON::add_theme_json_to_local( 'current', null, null, $options );
} else {
CBT_Theme_JSON::add_theme_json_to_local( 'all', null, null, $options );
}
CBT_Theme_Styles::clear_user_styles_customizations();
}

if ( isset( $options['savePatterns'] ) && true === $options['savePatterns'] ) {
$response = CBT_Theme_Patterns::add_patterns_to_theme( $options );

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

wp_get_theme()->cache_delete();

return new WP_REST_Response(
array(
'status' => 'SUCCESS',
Expand Down
105 changes: 105 additions & 0 deletions includes/create-theme/theme-save.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php
/**
* Theme Save
*
* Service that persists user changes from the Editor to the active theme on
* disk. Shared by the REST endpoint at `/create-block-theme/v1/save` and any
* future non-REST callers (e.g. WP-CLI). The service does not perform
* capability or input-sanitization checks on its own — callers are
* responsible for those before invoking `run()`.
*
* @package Create_Block_Theme
*/
class CBT_Theme_Save {

/**
* Persist user changes from the Editor to the active theme on disk.
*
* Orchestrates the four optional save steps — fonts, templates, styles,
* patterns — gated by truthy flags on the options array. Templates and
* styles select scope ('user' / 'current' / 'all') based on
* `processOnlySavedTemplates` and `is_child_theme()`. After all steps run,
* the theme cache is invalidated once.
*
* Callers are responsible for capability enforcement — for REST this is
* handled by the route's `permission_callback` (currently
* `CBT_Theme_API::can_modify_theme()`, which requires `edit_theme_options`
* plus super-admin on multisite). CLI and other callers must apply an
* equivalent check before invoking `run()`.
*
* Callers are also responsible for validating any non-flag values in
* `$options` that flow into downstream services such as
* `CBT_Theme_Patterns::add_patterns_to_theme()` and
* `CBT_Theme_Templates::add_templates_to_local()`. The `/save` route
* does not declare `args`, so no framework-level sanitization is applied;
* CLI and other callers must sanitize equivalently.
*
* @param array $options Options array with keys:
* - saveFonts (bool)
* - saveTemplates (bool)
* - processOnlySavedTemplates (bool)
* - saveStyle (bool)
* - savePatterns (bool)
* Plus any nested options consumed by downstream services.
*
* @return true|WP_Error true on success, WP_Error if the patterns step fails.
*/
public static function run( array $options ) {
$flags = self::normalize_flags( $options );

if ( $flags['saveFonts'] ) {
CBT_Theme_Fonts::persist_font_settings();
}

if ( $flags['saveTemplates'] ) {
$scope = $flags['processOnlySavedTemplates']
? 'user'
: ( is_child_theme() ? 'current' : 'all' );
CBT_Theme_Templates::add_templates_to_local( $scope, null, null, $options );
CBT_Theme_Templates::clear_user_templates_customizations();
CBT_Theme_Templates::clear_user_template_parts_customizations();
}

if ( $flags['saveStyle'] ) {
$scope = is_child_theme() ? 'current' : 'all';
CBT_Theme_JSON::add_theme_json_to_local( $scope, null, null, $options );
CBT_Theme_Styles::clear_user_styles_customizations();
}

if ( $flags['savePatterns'] ) {
$result = CBT_Theme_Patterns::add_patterns_to_theme( $options );
if ( is_wp_error( $result ) ) {
return $result;
}
}

wp_get_theme()->cache_delete();

return true;
}

/**
* Normalize the save flags through wp_validate_boolean().
*
* Collapses two prior issues: undefined-index notices when a flag was
* read without isset() (notably processOnlySavedTemplates), and strict
* `true ===` checks that rejected `1` / `"true"` / `"1"` from non-JS
* callers (CLI, form-encoded REST clients).
*
* Uses wp_validate_boolean() rather than ! empty() so the string
* "false" (commonly sent by query-string and form-encoded callers) is
* treated as falsy, not truthy.
*
* @param array $options Raw options array.
* @return array<string,bool> Normalized boolean flags.
*/
private static function normalize_flags( array $options ) {
return array(
'saveFonts' => wp_validate_boolean( $options['saveFonts'] ?? false ),
'saveTemplates' => wp_validate_boolean( $options['saveTemplates'] ?? false ),
'processOnlySavedTemplates' => wp_validate_boolean( $options['processOnlySavedTemplates'] ?? false ),
'saveStyle' => wp_validate_boolean( $options['saveStyle'] ?? false ),
'savePatterns' => wp_validate_boolean( $options['savePatterns'] ?? false ),
);
}
}
Loading
Loading