From 76226c9b411be1cf20a76c4f9f88968d88cb66de Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 19 Oct 2016 18:14:21 +0000 Subject: [PATCH] Customize: Introduce custom CSS for extending theme styles. * Custom CSS is associated with a given theme and is displayed in an inline `style` element at the `wp_head` hook after the `wp_print_styles` is called so that it overrides any enqueued stylesheets. * A `wp_get_custom_css()` function is used for accessing the CSS associated with the current theme (or another theme) and a `wp_get_custom_css` filter for manipulating it. * CSS is managed in customizer via a new "Additional CSS" section with a single `textarea` control. * `WP_Customize_Section::$description_hidden` is introduced for hiding extended descriptions in customizer sections behind a help toggle as done with panels. * CSS is stored in a `custom_css` post type with the theme (stylesheet) slug as the `post_name`. * `WP_Customize_Custom_CSS_Setting` is introduced to handle validation of CSS, previewing, and persisting the CSS to the `custom_css` post type. * The `custom_css` setting is tied to a new `unfiltered_css` capability which maps to `unfiltered_html` by default. * Escaping the message in the notification template is removed to allow markup (`code` tags) to be rendered. See https://make.wordpress.org/core/2016/10/11/feature-proposal-better-theme-customizations-via-custom-css-with-live-previews/ Props johnregan3, celloexpressions, folletto, westonruter. Fixes #35395. git-svn-id: https://develop.svn.wordpress.org/trunk@38829 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/customize-controls.css | 64 ++- src/wp-admin/js/customize-controls.js | 17 +- src/wp-includes/capabilities.php | 3 + .../class-wp-customize-manager.php | 29 +- .../class-wp-customize-section.php | 29 +- .../class-wp-customize-custom-css-setting.php | 373 ++++++++++++++++++ src/wp-includes/default-filters.php | 1 + src/wp-includes/js/customize-preview.js | 6 + src/wp-includes/post.php | 29 ++ src/wp-includes/theme.php | 83 ++++ .../tests/customize/custom-css-setting.php | 231 +++++++++++ tests/phpunit/tests/user/capabilities.php | 1 + 12 files changed, 855 insertions(+), 11 deletions(-) create mode 100644 src/wp-includes/customize/class-wp-customize-custom-css-setting.php create mode 100644 tests/phpunit/tests/customize/custom-css-setting.php diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css index be32bd7b3b..96aba5455c 100644 --- a/src/wp-admin/css/customize-controls.css +++ b/src/wp-admin/css/customize-controls.css @@ -53,6 +53,10 @@ body { margin-bottom: 15px; } +#customize-controls .customize-info.section-meta { + margin-bottom: 15px; +} + #customize-controls .customize-info .accordion-section-title { background: #fff; color: #555; @@ -124,6 +128,7 @@ body { } #customize-controls .customize-info .customize-panel-description, +#customize-controls .customize-info .customize-section-description, #customize-controls .no-widget-areas-rendered-notice { color: #555; display: none; @@ -131,15 +136,22 @@ body { padding: 12px 15px; border-top: 1px solid #ddd; } + #customize-controls .customize-info .customize-panel-description.open + .no-widget-areas-rendered-notice { border-top: none; } -#customize-controls .customize-info .customize-panel-description p:first-child { +#customize-controls .customize-info .customize-section-description { + margin-bottom: 15px; +} + +#customize-controls .customize-info .customize-panel-description p:first-child, +#customize-controls .customize-info .customize-section-description p:first-child { margin-top: 0; } -#customize-controls .customize-info .customize-panel-description p:last-child { +#customize-controls .customize-info .customize-panel-description p:last-child, +#customize-controls .customize-info .customize-section-description p:last-child { margin-bottom: 0; } @@ -327,6 +339,10 @@ div.customize-section-description { margin-top: 22px; } +.customize-info div.customize-section-description { + margin-top: 0; +} + div.customize-section-description p:first-child { margin-top: 0; } @@ -542,6 +558,16 @@ p.customize-section-description { margin-bottom: 5px; } +.customize-section-description a.external-link:after { + font: 16px/11px dashicons; + content: "\f310"; + top: 3px; + position: relative; + padding-left: 3px; + display: inline-block; + text-decoration: none; +} + .customize-control-color .color-picker, .customize-control-upload div { line-height: 28px; @@ -962,6 +988,40 @@ p.customize-section-description { float: right; } +/** + * Custom CSS Section + * + * Modifications to the Section Container to + * make the textarea full-width. + */ +#customize-theme-controls #sub-accordion-section-custom_css { + padding-left: 0; + padding-right: 0; +} + +#customize-theme-controls #sub-accordion-section-custom_css .customize-section-title { + margin-left: 0; +} + +#customize-theme-controls #sub-accordion-section-custom_css .customize-control-title, +#customize-theme-controls #sub-accordion-section-custom_css .notice { + margin-left: 10px; + margin-right: 10px; +} + +#sub-accordion-section-custom_css .customize-control-notifications-container { + margin-bottom: 15px; +} + +#sub-accordion-section-custom_css textarea { + border-right: 0; + border-left: 0; + font-family: Consolas, Monaco, monospace; + font-size: 12px; + padding: 6px 8px; + height: 553px; +} + /** * Themes */ diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index 9b0e78e3b7..e354d0decd 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -904,7 +904,7 @@ * @since 4.1.0 */ attachEvents: function () { - var section = this; + var meta, content, section = this; // Expand/Collapse accordion sections on click. section.container.find( '.accordion-section-title, .customize-section-back' ).on( 'click keydown', function( event ) { @@ -919,6 +919,21 @@ section.expand(); } }); + + // This is very similar to what is found for api.Panel.attachEvents(). + section.container.find( '.customize-section-title .customize-help-toggle' ).on( 'click', function() { + + meta = section.container.find( '.section-meta' ); + if ( meta.hasClass( 'cannot-expand' ) ) { + return; + } + content = meta.find( '.customize-section-description:first' ); + content.toggleClass( 'open' ); + content.slideToggle(); + content.attr( 'aria-expanded', function ( i, attr ) { + return attr === 'true' ? 'false' : 'true'; + }); + }); }, /** diff --git a/src/wp-includes/capabilities.php b/src/wp-includes/capabilities.php index 841fba1488..c9158b6071 100644 --- a/src/wp-includes/capabilities.php +++ b/src/wp-includes/capabilities.php @@ -329,6 +329,9 @@ function map_meta_cap( $cap, $user_id ) { else $caps[] = $cap; break; + case 'unfiltered_css' : + $caps[] = 'unfiltered_html'; + break; case 'edit_files': case 'edit_plugins': case 'edit_themes': diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index 28ec792d0f..23a65f127f 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -300,6 +300,7 @@ final class WP_Customize_Manager { require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-section.php' ); + require_once( ABSPATH . WPINC . '/customize/class-wp-customize-custom-css-setting.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php' ); @@ -2493,7 +2494,7 @@ final class WP_Customize_Manager { @@ -3220,7 +3221,6 @@ final class WP_Customize_Manager { 'section' => 'colors', ) ) ); - /* Custom Header */ $this->add_section( 'header_image', array( @@ -3366,6 +3366,30 @@ final class WP_Customize_Manager { 'section' => 'static_front_page', 'type' => 'dropdown-pages', ) ); + + /* Custom CSS */ + $this->add_section( 'custom_css', array( + 'title' => __( 'Additional CSS' ), + 'priority' => 140, + 'description_hidden' => true, + 'description' => sprintf( '%s
%s%s', + __( 'CSS allows you to customize the appearance and layout of your site with code. Separate CSS is saved for each of your themes.' ), + 'https://codex.wordpress.org/Know_Your_Sources#CSS', + __( 'Learn more about CSS' ), + __( '(link opens in a new window)' ) + ), + ) ); + + $custom_css_setting = new WP_Customize_Custom_CSS_Setting( $this, sprintf( 'custom_css[%s]', get_stylesheet() ), array( + 'capability' => 'unfiltered_css', + ) ); + $this->add_setting( $custom_css_setting ); + + $this->add_control( 'custom_css', array( + 'type' => 'textarea', + 'section' => 'custom_css', + 'settings' => array( 'default' => $custom_css_setting->id ), + ) ); } /** @@ -3388,7 +3412,6 @@ final class WP_Customize_Manager { } } } - return 0 !== count( get_pages() ); } diff --git a/src/wp-includes/class-wp-customize-section.php b/src/wp-includes/class-wp-customize-section.php index 0a916f2f54..68d677133e 100644 --- a/src/wp-includes/class-wp-customize-section.php +++ b/src/wp-includes/class-wp-customize-section.php @@ -145,6 +145,18 @@ class WP_Customize_Section { */ public $active_callback = ''; + /** + * Show the description or hide it behind the help icon. + * + * @since 4.7.0 + * @access public + * + * @var bool Indicates whether the Section's description should be + * hidden behind a help icon ("?") in the Section header, + * similar to how help icons are displayed on Panels. + */ + public $description_hidden = false; + /** * Constructor. * @@ -223,7 +235,7 @@ class WP_Customize_Section { * @return array The array to be exported to the client as JSON. */ public function json() { - $array = wp_array_slice_assoc( (array) $this, array( 'id', 'description', 'priority', 'panel', 'type' ) ); + $array = wp_array_slice_assoc( (array) $this, array( 'id', 'description', 'priority', 'panel', 'type', 'description_hidden' ) ); $array['title'] = html_entity_decode( $this->title, ENT_QUOTES, get_bloginfo( 'charset' ) ); $array['content'] = $this->get_content(); $array['active'] = $this->active(); @@ -324,11 +336,11 @@ class WP_Customize_Section { * @see WP_Customize_Manager::render_template() */ public function print_template() { - ?> + ?> -