From 4e8410a886a9635c7c149200acc5dda01089f69d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 25 Oct 2016 06:30:27 +0000 Subject: [PATCH] Customize: Allow page stubs to be created via `dropdown-pages` controls in the Static Front Page section. This ability was previously added to nav menus via the available page items panel. The "Add New Page" button only appears when the `allow_addition` control param is supplied as `true`. Code is adapted from the Customize Posts feature plugin. Props celloexpressions, westonruter. See #38013, #34923. Fixes #38164. git-svn-id: https://develop.svn.wordpress.org/trunk@38906 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/customize-controls.css | 53 ++++++++++- src/wp-admin/css/customize-nav-menus.css | 35 -------- src/wp-admin/js/customize-controls.js | 90 ++++++++++++++++++- src/wp-admin/js/customize-nav-menus.js | 2 +- .../class-wp-customize-control.php | 39 +++++++- .../class-wp-customize-manager.php | 2 + .../class-wp-customize-nav-menus.php | 1 + tests/phpunit/tests/customize/control.php | 63 ++++++++++++- 8 files changed, 243 insertions(+), 42 deletions(-) diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css index 3234eb6249..adbd862be8 100644 --- a/src/wp-admin/css/customize-controls.css +++ b/src/wp-admin/css/customize-controls.css @@ -565,8 +565,8 @@ p.customize-section-description { } .customize-control select { - min-width: 50%; - max-width: 100%; + width: 100%; + max-width: 300px; height: 28px; line-height: 28px; } @@ -587,6 +587,7 @@ p.customize-section-description { display: block; font-style: italic; line-height: 18px; + margin-top: 0; margin-bottom: 5px; } @@ -674,6 +675,54 @@ p.customize-section-description { float: left; } +#available-menu-items .accordion-section-content .new-content-item, +.customize-control-dropdown-pages .new-content-item { + width: -webkit-calc(100% - 30px); + width: calc(100% - 30px); + padding: 8px 15px; + position: absolute; + bottom: 0; + z-index: 10; + background: #eee; + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; +} + +.customize-control-dropdown-pages .new-content-item { + width: 100%; + max-width: 300px; + padding: 5px 0 5px 1px; + position: relative; +} + +#available-menu-items .new-content-item .create-item-input, +.customize-control-dropdown-pages .new-content-item .create-item-input { + -webkit-box-flex: 10; + -webkit-flex-grow: 10; + -moz-box-flex: 10; + -ms-flex-positive: 10; + -ms-flex: 10; + flex-grow: 10; +} + +#available-menu-items .new-content-item .add-content, +.customize-control-dropdown-pages .new-content-item .add-content { + margin: 2px 0 2px 6px; + -webkit-box-flex: 10; + -webkit-flex-grow: 10; + -moz-box-flex: 10; + -ms-flex-positive: 10; + -ms-flex: 10; + flex-grow: 1; +} + +.customize-control-dropdown-pages .new-content-item .create-item-input.invalid { + border: 1px solid #f00; +} + #customize-preview iframe { width: 100%; height: 100%; diff --git a/src/wp-admin/css/customize-nav-menus.css b/src/wp-admin/css/customize-nav-menus.css index 1386fe0940..a63df15975 100644 --- a/src/wp-admin/css/customize-nav-menus.css +++ b/src/wp-admin/css/customize-nav-menus.css @@ -572,41 +572,6 @@ padding: 0 15px 15px 15px; } -#available-menu-items .accordion-section-content .new-content-item { - width: -webkit-calc(100% - 30px); - width: calc(100% - 30px); - padding: 8px 15px; - position: absolute; - bottom: 0; - z-index: 10; - background: #eee; - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; -} - -#available-menu-items .new-content-item .create-item-input { - -webkit-box-flex: 10; - -webkit-flex-grow: 10; - -moz-box-flex: 10; - -ms-flex-positive: 10; - -ms-flex: 10; - flex-grow: 10; - margin-left: 5px; - padding: 4.5px; -} -#available-menu-items .new-content-item .add-content { - padding-left: 6px; - -webkit-box-flex: 10; - -webkit-flex-grow: 10; - -moz-box-flex: 10; - -ms-flex-positive: 10; - -ms-flex: 10; - flex-grow: 1; -} - #available-menu-items .menu-item-tpl { margin: 0; } diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index a23a130c0b..90b8411fb6 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -2516,9 +2516,28 @@ /** * Triggered when the control's markup has been injected into the DOM. * - * @abstract + * @returns {void} */ - ready: function() {}, + ready: function() { + var control = this, newItem; + if ( 'dropdown-pages' === control.params.type && control.params.allow_addition ) { + newItem = control.container.find( '.new-content-item' ); + newItem.hide(); // Hide in JS to preserve flex display when showing. + control.container.on( 'click', '.add-new-toggle', function( e ) { + $( e.currentTarget ).slideUp( 180 ); + newItem.slideDown( 180 ); + newItem.find( '.create-item-input' ).focus(); + }); + control.container.on( 'click', '.add-content', function() { + control.addNewPage(); + }); + control.container.on( 'keyup', '.create-item-input', function( e ) { + if ( 13 === e.which ) { // Enter + control.addNewPage(); + } + }); + } + }, /** * Get the element inside of a control's container that contains the validation error message. @@ -2736,6 +2755,73 @@ control.container.html( template( control.params ) ); } } + }, + + /** + * Add a new page to a dropdown-pages control reusing menus code for this. + * + * @since 4.7.0 + * @access private + * @returns {void} + */ + addNewPage: function () { + var control = this, promise, toggle, container, input, title, select; + + if ( 'dropdown-pages' !== control.params.type || ! control.params.allow_addition || ! api.Menus ) { + return; + } + + toggle = control.container.find( '.add-new-toggle' ); + container = control.container.find( '.new-content-item' ); + input = control.container.find( '.create-item-input' ); + title = input.val(); + select = control.container.find( 'select' ); + + if ( ! title ) { + input.addClass( 'invalid' ); + return; + } + + input.removeClass( 'invalid' ); + input.attr( 'disabled', 'disabled' ); + + // The menus functions add the page, publish when appropriate, and also add the new page to the dropdown-pages controls. + promise = api.Menus.insertAutoDraftPost( { + post_title: title, + post_type: 'page' + } ); + promise.done( function( data ) { + var availableItem, $content, itemTemplate; + + // Prepare the new page as an available menu item. + // See api.Menus.submitNew(). + availableItem = new api.Menus.AvailableItemModel( { + 'id': 'post-' + data.post_id, // Used for available menu item Backbone models. + 'title': title, + 'type': 'page', + 'type_label': api.Menus.data.l10n.page_label, + 'object': 'post_type', + 'object_id': data.post_id, + 'url': data.url + } ); + + // Add the new item to the list of available menu items. + api.Menus.availableMenuItemsPanel.collection.add( availableItem ); + $content = $( '#available-menu-items-post_type-page' ).find( '.available-menu-items-list' ); + itemTemplate = wp.template( 'available-menu-item' ); + $content.prepend( itemTemplate( availableItem.attributes ) ); + + // Focus the select control. + select.focus(); + control.setting.set( String( data.post_id ) ); // Triggers a preview refresh and updates the setting. + + // Reset the create page form. + container.slideUp( 180 ); + toggle.slideDown( 180 ); + } ) + .always( function() { + input.val( '' ).removeAttr( 'disabled' ); + } ); } }); diff --git a/src/wp-admin/js/customize-nav-menus.js b/src/wp-admin/js/customize-nav-menus.js index 9981cc92ef..6024739059 100644 --- a/src/wp-admin/js/customize-nav-menus.js +++ b/src/wp-admin/js/customize-nav-menus.js @@ -101,7 +101,6 @@ request.done( function( response ) { if ( response.post_id ) { - deferred.resolve( response ); api.Menus.insertedAutoDrafts.push( response.post_id ); api( 'nav_menus_created_posts' ).set( _.clone( api.Menus.insertedAutoDrafts ) ); @@ -121,6 +120,7 @@ } } ); } + deferred.resolve( response ); } } ); diff --git a/src/wp-includes/class-wp-customize-control.php b/src/wp-includes/class-wp-customize-control.php index e8482b2df0..c45af67d8f 100644 --- a/src/wp-includes/class-wp-customize-control.php +++ b/src/wp-includes/class-wp-customize-control.php @@ -114,6 +114,15 @@ class WP_Customize_Control { */ public $input_attrs = array(); + /** + * Show UI for adding new content, currently only used for the dropdown-pages control. + * + * @since 4.7.0 + * @access public + * @var array + */ + public $allow_addition = false; + /** * @deprecated It is better to just call the json() method * @access public @@ -296,6 +305,10 @@ class WP_Customize_Control { $this->json['label'] = $this->label; $this->json['description'] = $this->description; $this->json['instanceNumber'] = $this->instance_number; + + if ( 'dropdown-pages' === $this->type ) { + $this->json['allow_addition'] = $this->allow_addition; + } } /** @@ -554,10 +567,34 @@ class WP_Customize_Control { // Hackily add in the data link parameter. $dropdown = str_replace( 'get_link(), $dropdown ); + + // Even more hacikly add auto-draft page stubs. + // @todo Eventually this should be removed in favor of the pages being injected into the underlying get_pages() call. See . + $nav_menus_created_posts_setting = $this->manager->get_setting( 'nav_menus_created_posts' ); + if ( $nav_menus_created_posts_setting && current_user_can( 'publish_pages' ) ) { + $auto_draft_page_options = ''; + foreach ( $nav_menus_created_posts_setting->value() as $auto_draft_page_id ) { + $post = get_post( $auto_draft_page_id ); + if ( $post && 'page' === $post->post_type ) { + $auto_draft_page_options .= sprintf( '', esc_attr( $post->ID ), esc_html( $post->post_title ) ); + } + } + if ( $auto_draft_page_options ) { + $dropdown = str_replace( '', $auto_draft_page_options . '', $dropdown ); + } + } + echo $dropdown; ?> - allow_addition && current_user_can( 'publish_pages' ) && current_user_can( 'edit_theme_options' ) ) : // Currently tied to menus functionality. ?> + +
+ + + +
+ diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index 5a7cd46421..229be36da6 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -3356,6 +3356,7 @@ final class WP_Customize_Manager { 'label' => __( 'Front page' ), 'section' => 'static_front_page', 'type' => 'dropdown-pages', + 'allow_addition' => true, ) ); $this->add_setting( 'page_for_posts', array( @@ -3367,6 +3368,7 @@ final class WP_Customize_Manager { 'label' => __( 'Posts page' ), 'section' => 'static_front_page', 'type' => 'dropdown-pages', + 'allow_addition' => true, ) ); /* Custom CSS */ diff --git a/src/wp-includes/class-wp-customize-nav-menus.php b/src/wp-includes/class-wp-customize-nav-menus.php index 2d9c13a40e..c8ef7a8a5f 100644 --- a/src/wp-includes/class-wp-customize-nav-menus.php +++ b/src/wp-includes/class-wp-customize-nav-menus.php @@ -370,6 +370,7 @@ final class WP_Customize_Nav_Menus { 'untitled' => _x( '(no label)', 'missing menu item navigation label' ), 'unnamed' => _x( '(unnamed)', 'Missing menu name.' ), 'custom_label' => __( 'Custom Link' ), + 'page_label' => get_post_type_object( 'page' )->labels->singular_name, /* translators: %s: menu location */ 'menuLocation' => _x( '(Currently set to: %s)', 'menu' ), 'menuNameLabel' => __( 'Menu Name' ), diff --git a/tests/phpunit/tests/customize/control.php b/tests/phpunit/tests/customize/control.php index e6d1141219..ccd850ecc5 100644 --- a/tests/phpunit/tests/customize/control.php +++ b/tests/phpunit/tests/customize/control.php @@ -26,6 +26,7 @@ class Test_WP_Customize_Control extends WP_UnitTestCase { */ function setUp() { parent::setUp(); + wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) ); require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); // @codingStandardsIgnoreStart $GLOBALS['wp_customize'] = new WP_Customize_Manager(); @@ -39,7 +40,6 @@ class Test_WP_Customize_Control extends WP_UnitTestCase { * @see WP_Customize_Control::check_capabilities() */ function test_check_capabilities() { - wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); do_action( 'customize_register', $this->wp_customize ); $control = new WP_Customize_Control( $this->wp_customize, 'blogname', array( 'settings' => array( 'blogname' ), @@ -77,6 +77,67 @@ class Test_WP_Customize_Control extends WP_UnitTestCase { $this->assertTrue( $control->check_capabilities() ); } + /** + * @ticket 38164 + */ + function test_dropdown_pages() { + do_action( 'customize_register', $this->wp_customize ); + + $this->assertInstanceOf( 'WP_Customize_Nav_Menus', $this->wp_customize->nav_menus ); + $nav_menus_created_posts_setting = $this->wp_customize->get_setting( 'nav_menus_created_posts' ); + $this->assertInstanceOf( 'WP_Customize_Filter_Setting', $nav_menus_created_posts_setting ); + $page_on_front_control = $this->wp_customize->get_control( 'page_on_front' ); + + // Ensure the add-new-toggle is absent if allow_addition param is not set. + $page_on_front_control->allow_addition = false; + ob_start(); + $page_on_front_control->maybe_render(); + $content = ob_get_clean(); + $this->assertNotContains( 'add-new-toggle', $content ); + + // Ensure the add-new-toggle is absent if allow_addition param is set. + $page_on_front_control->allow_addition = true; + ob_start(); + $page_on_front_control->maybe_render(); + $content = ob_get_clean(); + $this->assertContains( 'add-new-toggle', $content ); + + // Ensure that dropdown-pages delect is rendered even if there are no pages published (yet). + foreach ( get_pages() as $page ) { + wp_delete_post( $page->ID ); + } + $page_on_front_control->allow_addition = true; + ob_start(); + $page_on_front_control->maybe_render(); + $content = ob_get_clean(); + $this->assertContains( '', $auto_draft_page_id ), $content ); + $this->assertNotContains( 'Auto Draft Post', $content ); + $this->assertNotContains( 'Orphan Auto Draft Page', $content ); + } + /** * Tear down. */