diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php index 8413627207996..e01a3c60ce927 100644 --- a/lib/block-supports/layout.php +++ b/lib/block-supports/layout.php @@ -138,8 +138,8 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) { return $block_content; } - $block_gap = wp_get_global_settings( array( 'spacing', 'blockGap' ) ); - $default_layout = wp_get_global_settings( array( 'layout' ) ); + $block_gap = gutenberg_get_global_settings( array( 'spacing', 'blockGap' ) ); + $default_layout = gutenberg_get_global_settings( array( 'layout' ) ); $has_block_gap_support = isset( $block_gap ) ? null !== $block_gap : false; $default_block_layout = _wp_array_get( $block_type->supports, array( '__experimentalLayout', 'default' ), array() ); $used_layout = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $default_block_layout; diff --git a/lib/compat/wordpress-5.9/get-global-styles-and-settings.php b/lib/compat/wordpress-5.9/get-global-styles-and-settings.php deleted file mode 100644 index 0ac1456bd2e42..0000000000000 --- a/lib/compat/wordpress-5.9/get-global-styles-and-settings.php +++ /dev/null @@ -1,194 +0,0 @@ -get_settings(); - - return _wp_array_get( $settings, $path, $settings ); - } -} - -if ( ! function_exists( 'wp_get_global_styles' ) ) { - /** - * Function to get the styles resulting of merging core, theme, and user data. - * - * @param array $path Path to the specific style to retrieve. Optional. - * If empty, will return all styles. - * @param array $context { - * Metadata to know where to retrieve the $path from. Optional. - * - * @type string $block_name Which block to retrieve the styles from. - * If empty, it'll return the styles for the global context. - * @type string $origin Which origin to take data from. - * Valid values are 'all' (core, theme, and user) or 'base' (core and theme). - * If empty or unknown, 'all' is used. - * } - * - * @return array The styles to retrieve. - */ - function wp_get_global_styles( $path = array(), $context = array() ) { - if ( ! empty( $context['block_name'] ) ) { - $path = array_merge( array( 'blocks', $context['block_name'] ), $path ); - } - - $origin = 'custom'; - if ( isset( $context['origin'] ) && 'base' === $context['origin'] ) { - $origin = 'theme'; - } - - $styles = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $origin )->get_raw_data()['styles']; - - return _wp_array_get( $styles, $path, $styles ); - } -} - -if ( ! function_exists( 'wp_get_global_stylesheet' ) ) { - /** - * Returns the stylesheet resulting of merging core, theme, and user data. - * - * @param array $types Types of styles to load. Optional. - * It accepts 'variables', 'styles', 'presets' as values. - * If empty, it'll load all for themes with theme.json support - * and only [ 'variables', 'presets' ] for themes without theme.json support. - * - * @return string Stylesheet. - */ - function wp_get_global_stylesheet( $types = array() ) { - // Return cached value if it can be used and exists. - // It's cached by theme to make sure that theme switching clears the cache. - $can_use_cached = ( - ( empty( $types ) ) && - ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) && - ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) && - ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) && - ! is_admin() - ); - $transient_name = 'gutenberg_global_styles_' . get_stylesheet(); - if ( $can_use_cached ) { - $cached = get_transient( $transient_name ); - if ( $cached ) { - return $cached; - } - } - - $tree = WP_Theme_JSON_Resolver::get_merged_data(); - - $supports_theme_json = WP_Theme_JSON_Resolver::theme_has_support(); - if ( empty( $types ) && ! $supports_theme_json ) { - $types = array( 'variables', 'presets' ); - } elseif ( empty( $types ) ) { - $types = array( 'variables', 'styles', 'presets' ); - } - - /* - * If variables are part of the stylesheet, - * we add them for all origins (default, theme, user). - * This is so themes without a theme.json still work as before 5.9: - * they can override the default presets. - * See https://core.trac.wordpress.org/ticket/54782 - */ - $styles_variables = ''; - if ( in_array( 'variables', $types, true ) ) { - $styles_variables = $tree->get_stylesheet( array( 'variables' ) ); - $types = array_diff( $types, array( 'variables' ) ); - } - - /* - * For the remaining types (presets, styles), we do consider origins: - * - * - themes without theme.json: only the classes for the presets defined by core - * - themes with theme.json: the presets and styles classes, both from core and the theme - */ - $styles_rest = ''; - if ( ! empty( $types ) ) { - $origins = array( 'default', 'theme', 'custom' ); - if ( ! $supports_theme_json ) { - $origins = array( 'default' ); - } - $styles_rest = $tree->get_stylesheet( $types, $origins ); - } - - $stylesheet = $styles_variables . $styles_rest; - - if ( $can_use_cached ) { - // Cache for a minute. - // This cache doesn't need to be any longer, we only want to avoid spikes on high-traffic sites. - set_transient( $transient_name, $stylesheet, MINUTE_IN_SECONDS ); - } - - return $stylesheet; - } -} - -if ( ! function_exists( 'wp_get_global_styles_svg_filters' ) ) { - /** - * Returns a string containing the SVGs to be referenced as filters (duotone). - * - * @return string - */ - function wp_get_global_styles_svg_filters() { - // Return cached value if it can be used and exists. - // It's cached by theme to make sure that theme switching clears the cache. - $transient_name = 'gutenberg_global_styles_svg_filters_' . get_stylesheet(); - $can_use_cached = ( - ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) && - ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) && - ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) && - ! is_admin() - ); - if ( $can_use_cached ) { - $cached = get_transient( $transient_name ); - if ( $cached ) { - return $cached; - } - } - - $supports_theme_json = WP_Theme_JSON_Resolver_Gutenberg::theme_has_support(); - - $origins = array( 'default', 'theme', 'custom' ); - if ( ! $supports_theme_json ) { - $origins = array( 'default' ); - } - - $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(); - $svgs = $tree->get_svg_filters( $origins ); - - if ( $can_use_cached ) { - // Cache for a minute, same as gutenberg_get_global_stylesheet. - set_transient( $transient_name, $svgs, MINUTE_IN_SECONDS ); - } - - return $svgs; - } -} diff --git a/lib/compat/wordpress-5.9/global-styles-css-custom-properties.php b/lib/compat/wordpress-5.9/global-styles-css-custom-properties.php index 990f551a625ca..bcc30c1411700 100644 --- a/lib/compat/wordpress-5.9/global-styles-css-custom-properties.php +++ b/lib/compat/wordpress-5.9/global-styles-css-custom-properties.php @@ -13,7 +13,7 @@ */ function wp_enqueue_global_styles_css_custom_properties() { wp_register_style( 'global-styles-css-custom-properties', false, array(), true, true ); - wp_add_inline_style( 'global-styles-css-custom-properties', wp_get_global_stylesheet( array( 'variables' ) ) ); + wp_add_inline_style( 'global-styles-css-custom-properties', gutenberg_get_global_stylesheet( array( 'variables' ) ) ); wp_enqueue_style( 'global-styles-css-custom-properties' ); } add_filter( 'enqueue_block_editor_assets', 'wp_enqueue_global_styles_css_custom_properties' ); diff --git a/lib/compat/wordpress-5.9/script-loader.php b/lib/compat/wordpress-5.9/script-loader.php index 36f44f41c4a92..b88cdc65ee63b 100644 --- a/lib/compat/wordpress-5.9/script-loader.php +++ b/lib/compat/wordpress-5.9/script-loader.php @@ -28,7 +28,7 @@ function gutenberg_enqueue_global_styles_assets() { return; } - $stylesheet = wp_get_global_stylesheet(); + $stylesheet = gutenberg_get_global_stylesheet(); if ( empty( $stylesheet ) ) { return; diff --git a/lib/compat/wordpress-6.0/class-gutenberg-rest-global-styles-controller.php b/lib/compat/wordpress-6.0/class-gutenberg-rest-global-styles-controller.php index a18c5a61531a0..9c8b03ec92b73 100644 --- a/lib/compat/wordpress-6.0/class-gutenberg-rest-global-styles-controller.php +++ b/lib/compat/wordpress-6.0/class-gutenberg-rest-global-styles-controller.php @@ -60,6 +60,151 @@ public function get_theme_items_permissions_check( $request ) { // phpcs:ignore return true; } + /** + * Prepares a single global styles config for update. + * + * @since 5.9.0 + * + * @param WP_REST_Request $request Request object. + * @return stdClass Changes to pass to wp_update_post. + */ + protected function prepare_item_for_database( $request ) { + $changes = new stdClass(); + $changes->ID = $request['id']; + $post = get_post( $request['id'] ); + $existing_config = array(); + if ( $post ) { + $existing_config = json_decode( $post->post_content, true ); + $json_decoding_error = json_last_error(); + if ( JSON_ERROR_NONE !== $json_decoding_error || ! isset( $existing_config['isGlobalStylesUserThemeJSON'] ) || + ! $existing_config['isGlobalStylesUserThemeJSON'] ) { + $existing_config = array(); + } + } + if ( isset( $request['styles'] ) || isset( $request['settings'] ) ) { + $config = array(); + if ( isset( $request['styles'] ) ) { + $config['styles'] = $request['styles']; + } elseif ( isset( $existing_config['styles'] ) ) { + $config['styles'] = $existing_config['styles']; + } + if ( isset( $request['settings'] ) ) { + $config['settings'] = $request['settings']; + } elseif ( isset( $existing_config['settings'] ) ) { + $config['settings'] = $existing_config['settings']; + } + $config['isGlobalStylesUserThemeJSON'] = true; + $config['version'] = WP_Theme_JSON_Gutenberg::LATEST_SCHEMA; + $changes->post_content = wp_json_encode( $config ); + } + // Post title. + if ( isset( $request['title'] ) ) { + if ( is_string( $request['title'] ) ) { + $changes->post_title = $request['title']; + } elseif ( ! empty( $request['title']['raw'] ) ) { + $changes->post_title = $request['title']['raw']; + } + } + return $changes; + } + + /** + * Prepare a global styles config output for response. + * + * @since 5.9.0 + * + * @param WP_Post $post Global Styles post object. + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response Response object. + */ + public function prepare_item_for_response( $post, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + $raw_config = json_decode( $post->post_content, true ); + $is_global_styles_user_theme_json = isset( $raw_config['isGlobalStylesUserThemeJSON'] ) && true === $raw_config['isGlobalStylesUserThemeJSON']; + $config = array(); + if ( $is_global_styles_user_theme_json ) { + $config = ( new WP_Theme_JSON_Gutenberg( $raw_config, 'custom' ) )->get_raw_data(); + } + // Base fields for every post. + $data = array(); + $fields = $this->get_fields_for_response( $request ); + if ( rest_is_field_included( 'id', $fields ) ) { + $data['id'] = $post->ID; + } + if ( rest_is_field_included( 'title', $fields ) ) { + $data['title'] = array(); + } + if ( rest_is_field_included( 'title.raw', $fields ) ) { + $data['title']['raw'] = $post->post_title; + } + if ( rest_is_field_included( 'title.rendered', $fields ) ) { + add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); + $data['title']['rendered'] = get_the_title( $post->ID ); + remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); + } + if ( rest_is_field_included( 'settings', $fields ) ) { + $data['settings'] = ! empty( $config['settings'] ) && $is_global_styles_user_theme_json ? $config['settings'] : new stdClass(); + } + if ( rest_is_field_included( 'styles', $fields ) ) { + $data['styles'] = ! empty( $config['styles'] ) && $is_global_styles_user_theme_json ? $config['styles'] : new stdClass(); + } + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; + $data = $this->add_additional_fields_to_object( $data, $request ); + $data = $this->filter_response_by_context( $data, $context ); + // Wrap the data in a response object. + $response = rest_ensure_response( $data ); + $links = $this->prepare_links( $post->ID ); + $response->add_links( $links ); + if ( ! empty( $links['self']['href'] ) ) { + $actions = $this->get_available_actions(); + $self = $links['self']['href']; + foreach ( $actions as $rel ) { + $response->add_link( $rel, $self ); + } + } + return $response; + } + + /** + * Returns the given theme global styles config. + * + * @since 5.9.0 + * + * @param WP_REST_Request $request The request instance. + * @return WP_REST_Response|WP_Error + */ + public function get_theme_item( $request ) { + if ( wp_get_theme()->get_stylesheet() !== $request['stylesheet'] ) { + // This endpoint only supports the active theme for now. + return new WP_Error( + 'rest_theme_not_found', + __( 'Theme not found.', 'gutenberg' ), + array( 'status' => 404 ) + ); + } + $theme = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( 'theme' ); + $data = array(); + $fields = $this->get_fields_for_response( $request ); + if ( rest_is_field_included( 'settings', $fields ) ) { + $data['settings'] = $theme->get_settings(); + } + if ( rest_is_field_included( 'styles', $fields ) ) { + $raw_data = $theme->get_raw_data(); + $data['styles'] = isset( $raw_data['styles'] ) ? $raw_data['styles'] : array(); + } + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; + $data = $this->add_additional_fields_to_object( $data, $request ); + $data = $this->filter_response_by_context( $data, $context ); + $response = rest_ensure_response( $data ); + $links = array( + 'self' => array( + 'href' => rest_url( sprintf( '%s/%s/themes/%s', $this->namespace, $this->rest_base, $request['stylesheet'] ) ), + ), + ); + $response->add_links( $links ); + return $response; + } + + /** * Returns the given theme global styles variations. * @@ -94,4 +239,5 @@ public function get_theme_items( $request ) { return $response; } + } diff --git a/lib/compat/wordpress-6.0/get-global-styles-and-settings.php b/lib/compat/wordpress-6.0/get-global-styles-and-settings.php new file mode 100644 index 0000000000000..5e78029d0d191 --- /dev/null +++ b/lib/compat/wordpress-6.0/get-global-styles-and-settings.php @@ -0,0 +1,177 @@ +get_settings(); + return _wp_array_get( $settings, $path, $settings ); +} + +/** + * Function to get the styles resulting of merging core, theme, and user data. + * + * @param array $path Path to the specific style to retrieve. Optional. + * If empty, will return all styles. + * @param array $context { + * Metadata to know where to retrieve the $path from. Optional. + * + * @type string $block_name Which block to retrieve the styles from. + * If empty, it'll return the styles for the global context. + * @type string $origin Which origin to take data from. + * Valid values are 'all' (core, theme, and user) or 'base' (core and theme). + * If empty or unknown, 'all' is used. + * } + * + * @return array The styles to retrieve. + */ +function gutenberg_get_global_styles( $path = array(), $context = array() ) { + if ( ! empty( $context['block_name'] ) ) { + $path = array_merge( array( 'blocks', $context['block_name'] ), $path ); + } + $origin = 'custom'; + if ( isset( $context['origin'] ) && 'base' === $context['origin'] ) { + $origin = 'theme'; + } + $styles = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $origin )->get_raw_data()['styles']; + return _wp_array_get( $styles, $path, $styles ); +} + +/** + * Returns the stylesheet resulting of merging core, theme, and user data. + * + * @param array $types Types of styles to load. Optional. + * It accepts 'variables', 'styles', 'presets' as values. + * If empty, it'll load all for themes with theme.json support + * and only [ 'variables', 'presets' ] for themes without theme.json support. + * + * @return string Stylesheet. + */ +function gutenberg_get_global_stylesheet( $types = array() ) { + // Return cached value if it can be used and exists. + // It's cached by theme to make sure that theme switching clears the cache. + $can_use_cached = ( + ( empty( $types ) ) && + ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) && + ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) && + ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) && + ! is_admin() + ); + $transient_name = 'gutenberg_global_styles_' . get_stylesheet(); + if ( $can_use_cached ) { + $cached = get_transient( $transient_name ); + if ( $cached ) { + return $cached; + } + } + $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(); + $supports_theme_json = WP_Theme_JSON_Resolver_Gutenberg::theme_has_support(); + if ( empty( $types ) && ! $supports_theme_json ) { + $types = array( 'variables', 'presets' ); + } elseif ( empty( $types ) ) { + $types = array( 'variables', 'styles', 'presets' ); + } + + /* + * If variables are part of the stylesheet, + * we add them for all origins (default, theme, user). + * This is so themes without a theme.json still work as before 5.9: + * they can override the default presets. + * See https://core.trac.wordpress.org/ticket/54782 + */ + $styles_variables = ''; + if ( in_array( 'variables', $types, true ) ) { + $styles_variables = $tree->get_stylesheet( array( 'variables' ) ); + $types = array_diff( $types, array( 'variables' ) ); + } + + /* + * For the remaining types (presets, styles), we do consider origins: + * + * - themes without theme.json: only the classes for the presets defined by core + * - themes with theme.json: the presets and styles classes, both from core and the theme + */ + $styles_rest = ''; + if ( ! empty( $types ) ) { + $origins = array( 'default', 'theme', 'custom' ); + if ( ! $supports_theme_json ) { + $origins = array( 'default' ); + } + $styles_rest = $tree->get_stylesheet( $types, $origins ); + } + $stylesheet = $styles_variables . $styles_rest; + if ( $can_use_cached ) { + // Cache for a minute. + // This cache doesn't need to be any longer, we only want to avoid spikes on high-traffic sites. + set_transient( $transient_name, $stylesheet, MINUTE_IN_SECONDS ); + } + return $stylesheet; +} + +if ( ! function_exists( 'wp_get_global_styles_svg_filters' ) ) { + /** + * Returns a string containing the SVGs to be referenced as filters (duotone). + * + * @return string + */ + function wp_get_global_styles_svg_filters() { + // Return cached value if it can be used and exists. + // It's cached by theme to make sure that theme switching clears the cache. + $transient_name = 'gutenberg_global_styles_svg_filters_' . get_stylesheet(); + $can_use_cached = ( + ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) && + ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) && + ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) && + ! is_admin() + ); + if ( $can_use_cached ) { + $cached = get_transient( $transient_name ); + if ( $cached ) { + return $cached; + } + } + + $supports_theme_json = WP_Theme_JSON_Resolver_Gutenberg::theme_has_support(); + + $origins = array( 'default', 'theme', 'custom' ); + if ( ! $supports_theme_json ) { + $origins = array( 'default' ); + } + + $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(); + $svgs = $tree->get_svg_filters( $origins ); + + if ( $can_use_cached ) { + // Cache for a minute, same as gutenberg_get_global_stylesheet. + set_transient( $transient_name, $svgs, MINUTE_IN_SECONDS ); + } + + return $svgs; + } +} diff --git a/lib/global-styles.php b/lib/global-styles.php index a54373288421a..4f5555a028507 100644 --- a/lib/global-styles.php +++ b/lib/global-styles.php @@ -33,7 +33,7 @@ function_exists( 'gutenberg_is_edit_site_page' ) && } if ( 'mobile' === $context && WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) { - $settings['__experimentalStyles'] = wp_get_global_styles(); + $settings['__experimentalStyles'] = gutenberg_get_global_styles(); } if ( 'other' === $context ) { @@ -53,7 +53,7 @@ function_exists( 'gutenberg_is_edit_site_page' ) && $new_global_styles = array(); - $css_variables = wp_get_global_stylesheet( array( 'variables' ) ); + $css_variables = gutenberg_get_global_stylesheet( array( 'variables' ) ); if ( '' !== $css_variables ) { $new_global_styles[] = array( 'css' => $css_variables, @@ -61,7 +61,7 @@ function_exists( 'gutenberg_is_edit_site_page' ) && ); } - $css_presets = wp_get_global_stylesheet( array( 'presets' ) ); + $css_presets = gutenberg_get_global_stylesheet( array( 'presets' ) ); if ( '' !== $css_presets ) { $new_global_styles[] = array( 'css' => $css_presets, @@ -70,7 +70,7 @@ function_exists( 'gutenberg_is_edit_site_page' ) && } if ( WP_Theme_JSON_Resolver_Gutenberg::theme_has_support() ) { - $css_blocks = wp_get_global_stylesheet( array( 'styles' ) ); + $css_blocks = gutenberg_get_global_stylesheet( array( 'styles' ) ); if ( '' !== $css_blocks ) { $new_global_styles[] = array( 'css' => $css_blocks, @@ -83,7 +83,7 @@ function_exists( 'gutenberg_is_edit_site_page' ) && } // Copied from get_block_editor_settings() at wordpress-develop/block-editor.php. - $settings['__experimentalFeatures'] = wp_get_global_settings(); + $settings['__experimentalFeatures'] = gutenberg_get_global_settings(); if ( isset( $settings['__experimentalFeatures']['color']['palette'] ) ) { $colors_by_origin = $settings['__experimentalFeatures']['color']['palette']; diff --git a/lib/load.php b/lib/load.php index 8c309c2848b54..443942c98ee80 100644 --- a/lib/load.php +++ b/lib/load.php @@ -69,12 +69,8 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-5.9/block-template-utils.php'; require __DIR__ . '/compat/wordpress-5.9/default-editor-styles.php'; require __DIR__ . '/compat/wordpress-5.9/register-global-styles-cpt.php'; -// Needs to be loaded before get-global-styles-and-settings.php -// to make sure we can use the check "function_exists( 'wp_get_global_styles' )". -// If it loads after, that function will always be present at that point -// and the global styles assets won't be loaded. require __DIR__ . '/compat/wordpress-5.9/script-loader.php'; -require __DIR__ . '/compat/wordpress-5.9/get-global-styles-and-settings.php'; +require __DIR__ . '/compat/wordpress-6.0/get-global-styles-and-settings.php'; require __DIR__ . '/compat/wordpress-5.9/render-svg-filters.php'; require __DIR__ . '/compat/wordpress-5.9/json-file-decode.php'; require __DIR__ . '/compat/wordpress-5.9/translate-settings-using-i18n-schema.php';