<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[38436] trunk: Customize: Allow users to more seamlessly create page-based nav menus during customization.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/38436">38436</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/38436","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>westonruter</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2016-08-29 22:58:32 +0000 (Mon, 29 Aug 2016)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Customize: Allow users to more seamlessly create page-based nav menus during customization.

Introduces the ability to create stubs for the various post types to add to a given menu. This eliminates the need to leave the customizer to first create the post in the admin and then return to managing menus. Only the title of the newly-created post can be supplied; the post content will be blank and will need to be provided in the normal edit post screen outside the customizer, unless a plugin enables a post editing in the customizer experience. When a post is created and added to a nav menu in the customizer, the newly created post that is added to a menu is given the `auto-draft` status, and if the changes are not published, the `auto-draft` post will be automatically deleted within 7 days via `wp_delete_auto_drafts()`. However, if the customizer changes are saved, then these nav menu item `auto-draft` post stubs will be transitioned to `publish`.

Includes portions of code from the Customize Posts <https://github.com/xwp/wp-customize-posts> and Front-end Editor <https://github.com/iseulde/wp-front-end-editor> plugins.

For more information, see https://make.wordpress.org/core/2016/06/16/feature-proposal-content-authorship-in-menus-with-live-preview/

Props celloexpressions, westonruter, valendesigns, afercia, melchoyce, mapk, iseulde, mrahmadawais.
Fixes <a href="https://core.trac.wordpress.org/ticket/34923">#34923</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadmincsscustomizecontrolscss">trunk/src/wp-admin/css/customize-controls.css</a></li>
<li><a href="#trunksrcwpadmincsscustomizenavmenuscss">trunk/src/wp-admin/css/customize-nav-menus.css</a></li>
<li><a href="#trunksrcwpadminjscustomizenavmenusjs">trunk/src/wp-admin/js/customize-nav-menus.js</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizenavmenusphp">trunk/src/wp-includes/class-wp-customize-nav-menus.php</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizesettingphp">trunk/src/wp-includes/class-wp-customize-setting.php</a></li>
<li><a href="#trunktestsphpunittestsajaxCustomizeMenusphp">trunk/tests/phpunit/tests/ajax/CustomizeMenus.php</a></li>
<li><a href="#trunktestsphpunittestscustomizenavmenusphp">trunk/tests/phpunit/tests/customize/nav-menus.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadmincsscustomizecontrolscss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/css/customize-controls.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/css/customize-controls.css     2016-08-29 21:07:31 UTC (rev 38435)
+++ trunk/src/wp-admin/css/customize-controls.css       2016-08-29 22:58:32 UTC (rev 38436)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1219,7 +1219,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> .add-new-widget:before,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.add-new-menu-item:before {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.add-new-menu-item:before,
+#available-menu-items .new-content-item .add-content:before {
</ins><span class="cx" style="display: block; padding: 0 10px">         content: "\f132";
</span><span class="cx" style="display: block; padding: 0 10px">        display: inline-block;
</span><span class="cx" style="display: block; padding: 0 10px">        position: relative;
</span></span></pre></div>
<a id="trunksrcwpadmincsscustomizenavmenuscss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/css/customize-nav-menus.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/css/customize-nav-menus.css    2016-08-29 21:07:31 UTC (rev 38435)
+++ trunk/src/wp-admin/css/customize-nav-menus.css      2016-08-29 22:58:32 UTC (rev 38436)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -61,6 +61,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">        text-align: right;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.customize-control-nav_menu_item.has-notifications .menu-item-handle {
+       border-left: 4px solid #00a0d2;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> .wp-customizer .menu-item-settings {
</span><span class="cx" style="display: block; padding: 0 10px">        max-width: 100%;
</span><span class="cx" style="display: block; padding: 0 10px">        overflow: hidden;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -497,7 +501,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        color: #23282d;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#available-menu-items .accordion-section-content {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#available-menu-items .available-menu-items-list {
</ins><span class="cx" style="display: block; padding: 0 10px">         overflow-y: auto;
</span><span class="cx" style="display: block; padding: 0 10px">        max-height: 200px; /* This gets set in JS to fit the screen size, and based on # of sections. */
</span><span class="cx" style="display: block; padding: 0 10px">        background: transparent;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -534,11 +538,60 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> #available-menu-items .accordion-section-content {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        padding: 1px 15px 15px 15px;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ max-height: 290px;
</ins><span class="cx" style="display: block; padding: 0 10px">         margin: 0;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        max-height: 290px;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ padding: 0;
+       position: relative;
+       background: transparent;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#available-menu-items .accordion-section-content .available-menu-items-list {
+       margin: 0 0 45px 0;
+       padding: 1px 15px 15px 15px;
+}
+
+#available-menu-items .accordion-section-content .available-menu-items-list:only-child { /* Types that do not support new items for the current user */
+       margin-bottom: 0;
+}
+
+#new-custom-menu-item .accordion-section-content {
+       padding: 0 15px 15px 15px;
+}
+
+#available-menu-items .accordion-section-content .new-content-item {
+       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;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> #available-menu-items .menu-item-tpl {
</span><span class="cx" style="display: block; padding: 0 10px">        margin: 0;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -546,7 +599,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> #custom-menu-item-name.invalid,
</span><span class="cx" style="display: block; padding: 0 10px"> #custom-menu-item-url.invalid,
</span><span class="cx" style="display: block; padding: 0 10px"> .menu-name-field.invalid,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.menu-name-field.invalid:focus {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.menu-name-field.invalid:focus,
+#available-menu-items .new-content-item .create-item-input.invalid,
+#available-menu-items .new-content-item .create-item-input.invalid:focus {
</ins><span class="cx" style="display: block; padding: 0 10px">         border: 1px solid #f00;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpadminjscustomizenavmenusjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/js/customize-nav-menus.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/customize-nav-menus.js      2016-08-29 21:07:31 UTC (rev 38435)
+++ trunk/src/wp-admin/js/customize-nav-menus.js        2016-08-29 22:58:32 UTC (rev 38436)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -80,7 +80,48 @@
</span><span class="cx" style="display: block; padding: 0 10px">        });
</span><span class="cx" style="display: block; padding: 0 10px">        api.Menus.availableMenuItems = new api.Menus.AvailableItemCollection( api.Menus.data.availableMenuItems );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        api.Menus.insertedAutoDrafts = [];
+
</ins><span class="cx" style="display: block; padding: 0 10px">         /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Insert a new `auto-draft` post.
+        *
+        * @param {object} params - Parameters for the draft post to create.
+        * @param {string} params.post_type - Post type to add.
+        * @param {string} params.post_title - Post title to use.
+        * @return {jQuery.promise} Promise resolved with the added post.
+        */
+       api.Menus.insertAutoDraftPost = function insertAutoDraftPost( params ) {
+               var request, deferred = $.Deferred();
+
+               request = wp.ajax.post( 'customize-nav-menus-insert-auto-draft', {
+                       'customize-menus-nonce': api.settings.nonce['customize-menus'],
+                       'wp_customize': 'on',
+                       'params': params
+               } );
+
+               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 ) );
+                       }
+               } );
+
+               request.fail( function( response ) {
+                       var error = response || '';
+
+                       if ( 'undefined' !== typeof response.message ) {
+                               error = response.message;
+                       }
+
+                       console.error( error );
+                       deferred.rejectWith( error );
+               } );
+
+               return deferred.promise();
+       };
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * wp.customize.Menus.AvailableMenuItemsPanelView
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * View class for the available menu items panel.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -100,6 +141,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'click .menu-item-tpl': '_submit',
</span><span class="cx" style="display: block; padding: 0 10px">                        'click #custom-menu-item-submit': '_submitLink',
</span><span class="cx" style="display: block; padding: 0 10px">                        'keypress #custom-menu-item-name': '_submitLink',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'click .new-content-item .add-content': '_submitNew',
+                       'keypress .create-item-input': '_submitNew',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'keydown': 'keyboardAccessible'
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -115,6 +158,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                pages: {},
</span><span class="cx" style="display: block; padding: 0 10px">                sectionContent: '',
</span><span class="cx" style="display: block; padding: 0 10px">                loading: false,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                addingNew: false,
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                initialize: function() {
</span><span class="cx" style="display: block; padding: 0 10px">                        var self = this;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -124,7 +168,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        this.$search = $( '#menu-items-search' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this.sectionContent = this.$el.find( '.accordion-section-content' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 this.sectionContent = this.$el.find( '.available-menu-items-list' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        this.debounceSearch = _.debounce( self.search, 500 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -160,7 +204,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Load more items.
</span><span class="cx" style="display: block; padding: 0 10px">                        this.sectionContent.scroll( function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                var totalHeight = self.$el.find( '.accordion-section.open .accordion-section-content' ).prop( 'scrollHeight' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         var totalHeight = self.$el.find( '.accordion-section.open .available-menu-items-list' ).prop( 'scrollHeight' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                         visibleHeight = self.$el.find( '.accordion-section.open' ).height();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                if ( ! self.loading && $( this ).scrollTop() > 3 / 4 * totalHeight - visibleHeight ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -337,7 +381,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px">                                items = new api.Menus.AvailableItemCollection( items ); // @todo Why is this collection created and then thrown away?
</span><span class="cx" style="display: block; padding: 0 10px">                                self.collection.add( items.models );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                typeInner = availableMenuItemContainer.find( '.accordion-section-content' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         typeInner = availableMenuItemContainer.find( '.available-menu-items-list' );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 items.each(function( menuItem ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                        typeInner.append( itemTemplate( menuItem.attributes ) );
</span><span class="cx" style="display: block; padding: 0 10px">                                });
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -356,13 +400,15 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Adjust the height of each section of items to fit the screen.
</span><span class="cx" style="display: block; padding: 0 10px">                itemSectionHeight: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        var sections, totalHeight, accordionHeight, diff;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 var sections, lists, totalHeight, accordionHeight, diff;
</ins><span class="cx" style="display: block; padding: 0 10px">                         totalHeight = window.innerHeight;
</span><span class="cx" style="display: block; padding: 0 10px">                        sections = this.$el.find( '.accordion-section:not( #available-menu-items-search ) .accordion-section-content' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        accordionHeight =  46 * ( 2 + sections.length ) - 13; // Magic numbers.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 lists = this.$el.find( '.accordion-section:not( #available-menu-items-search ) .available-menu-items-list:not(":only-child")' );
+                       accordionHeight =  46 * ( 1 + sections.length ) + 14; // Magic numbers.
</ins><span class="cx" style="display: block; padding: 0 10px">                         diff = totalHeight - accordionHeight;
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( 120 < diff && 290 > diff ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                sections.css( 'max-height', diff );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                lists.css( 'max-height', ( diff - 60 ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -456,6 +502,88 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        itemName.val( '' );
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Submit handler for keypress (enter) on field and click on button.
+               _submitNew: function( event ) {
+                       var container;
+
+                       // Only proceed with keypress if it is Enter.
+                       if ( 'keypress' === event.type && 13 !== event.which ) {
+                               return;
+                       }
+
+                       if ( this.addingNew ) {
+                               return;
+                       }
+
+                       container = $( event.target ).closest( '.accordion-section' );
+
+                       this.submitNew( container );
+               },
+
+               // Creates a new object and adds an associated menu item to the menu.
+               submitNew: function( container ) {
+                       var panel = this,
+                               itemName = container.find( '.create-item-input' ),
+                               title = itemName.val(),
+                               dataContainer = container.find( '.available-menu-items-list' ),
+                               itemType = dataContainer.data( 'type' ),
+                               itemObject = dataContainer.data( 'object' ),
+                               itemTypeLabel = dataContainer.data( 'type_label' ),
+                               promise;
+
+                       if ( ! this.currentMenuControl ) {
+                               return;
+                       }
+
+                       // Only posts are supported currently.
+                       if ( 'post_type' !== itemType ) {
+                               return;
+                       }
+
+                       if ( '' === $.trim( itemName.val() ) ) {
+                               itemName.addClass( 'invalid' );
+                               itemName.focus();
+                               return;
+                       } else {
+                               itemName.removeClass( 'invalid' );
+                               container.find( '.accordion-section-title' ).addClass( 'loading' );
+                       }
+
+                       panel.addingNew = true;
+                       itemName.attr( 'disabled', 'disabled' );
+                       promise = api.Menus.insertAutoDraftPost( {
+                               post_title: title,
+                               post_type: itemObject
+                       } );
+                       promise.done( function( data ) {
+                               var availableItem, $content, itemTemplate;
+                               availableItem = new api.Menus.AvailableItemModel( {
+                                       'id': 'post-' + data.post_id, // Used for available menu item Backbone models.
+                                       'title': itemName.val(),
+                                       'type': itemType,
+                                       'type_label': itemTypeLabel,
+                                       'object': itemObject,
+                                       'object_id': data.post_id,
+                                       'url': data.url
+                               } );
+
+                               // Add new item to menu.
+                               panel.currentMenuControl.addItemToMenu( availableItem.attributes );
+
+                               // Add the new item to the list of available items.
+                               api.Menus.availableMenuItemsPanel.collection.add( availableItem );
+                               $content = container.find( '.available-menu-items-list' );
+                               itemTemplate = wp.template( 'available-menu-item' );
+                               $content.prepend( itemTemplate( availableItem.attributes ) );
+                               $content.scrollTop();
+
+                               // Reset the create content form.
+                               itemName.val( '' ).removeAttr( 'disabled' );
+                               panel.addingNew = false;
+                               container.find( '.accordion-section-title' ).removeClass( 'loading' );
+                       } );
+               },
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Opens the panel.
</span><span class="cx" style="display: block; padding: 0 10px">                open: function( menuControl ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        this.currentMenuControl = menuControl;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2545,6 +2673,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( data.nav_menu_updates || data.nav_menu_item_updates ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                api.Menus.applySavedData( data );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       // Reset list of inserted auto draft post IDs.
+                       api.Menus.insertedAutoDrafts = [];
</ins><span class="cx" style="display: block; padding: 0 10px">                 } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Open and focus menu control.
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizenavmenusphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-customize-nav-menus.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-nav-menus.php    2016-08-29 21:07:31 UTC (rev 38435)
+++ trunk/src/wp-includes/class-wp-customize-nav-menus.php      2016-08-29 22:58:32 UTC (rev 38436)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -56,16 +56,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">                add_filter( 'customize_refresh_nonces', array( $this, 'filter_nonces' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'wp_ajax_load-available-menu-items-customizer', array( $this, 'ajax_load_available_items' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'wp_ajax_search-available-menu-items-customizer', array( $this, 'ajax_search_available_items' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                add_action( 'wp_ajax_customize-nav-menus-insert-auto-draft', array( $this, 'ajax_insert_auto_draft_post' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-               // Needs to run after core Navigation section is set up.
</del><span class="cx" style="display: block; padding: 0 10px">                 add_action( 'customize_register', array( $this, 'customize_register' ), 11 );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">                 add_filter( 'customize_dynamic_setting_args', array( $this, 'filter_dynamic_setting_args' ), 10, 2 );
</span><span class="cx" style="display: block; padding: 0 10px">                add_filter( 'customize_dynamic_setting_class', array( $this, 'filter_dynamic_setting_class' ), 10, 3 );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'customize_controls_print_footer_scripts', array( $this, 'print_templates' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'customize_controls_print_footer_scripts', array( $this, 'available_items_template' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                add_action( 'customize_preview_init', array( $this, 'make_auto_draft_status_previewable' ) );
+               add_action( 'customize_save_nav_menus_created_posts', array( $this, 'save_nav_menus_created_posts' ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Selective Refresh partials.
</span><span class="cx" style="display: block; padding: 0 10px">                add_filter( 'customize_dynamic_partial_args', array( $this, 'customize_dynamic_partial_args' ), 10, 2 );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -626,6 +626,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'section'  => 'add_menu',
</span><span class="cx" style="display: block; padding: 0 10px">                        'settings' => array(),
</span><span class="cx" style="display: block; padding: 0 10px">                ) ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $this->manager->add_setting( new WP_Customize_Filter_Setting( $this->manager, 'nav_menus_created_posts', array(
+                       'transport' => 'postMessage',
+                       'default' => array(),
+                       'sanitize_callback' => array( $this, 'sanitize_nav_menus_created_posts' ),
+               ) ) );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -648,6 +654,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * Return an array of all the available item types.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.3.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.7.0  Each array item now includes a `$type_label` in in addition to `$title`, `$type`, and `$object`.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @access public
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @return array The available menu item types.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -660,7 +667,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        foreach ( $post_types as $slug => $post_type ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $item_types[] = array(
</span><span class="cx" style="display: block; padding: 0 10px">                                        'title'  => $post_type->labels->name,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'type'   => 'post_type',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'type_label' => $post_type->labels->singular_name,
+                                       'type' => 'post_type',
</ins><span class="cx" style="display: block; padding: 0 10px">                                         'object' => $post_type->name,
</span><span class="cx" style="display: block; padding: 0 10px">                                );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -673,8 +681,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        continue;
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px">                                $item_types[] = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        'title'  => $taxonomy->labels->name,
-                                       'type'   => 'taxonomy',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 'title' => $taxonomy->labels->name,
+                                       'type_label' => $taxonomy->labels->singular_name,
+                                       'type' => 'taxonomy',
</ins><span class="cx" style="display: block; padding: 0 10px">                                         'object' => $taxonomy->name,
</span><span class="cx" style="display: block; padding: 0 10px">                                );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -684,6 +693,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * Filters the available menu item types.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @since 4.3.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @since 4.7.0  Each array item now includes a `$type_label` in in addition to `$title`, `$type`, and `$object`.
</ins><span class="cx" style="display: block; padding: 0 10px">                  *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param array $item_types Custom menu item types.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -693,6 +703,119 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Add a new `auto-draft` post.
+        *
+        * @access public
+        * @since 4.7.0
+        *
+        * @param array $postarr {
+        *     Abbreviated post array.
+        *
+        *     @var string $post_title Post title.
+        *     @var string $post_type  Post type.
+        * }
+        * @return WP_Post|WP_Error Inserted auto-draft post object or error.
+        */
+       public function insert_auto_draft_post( $postarr ) {
+               if ( ! isset( $postarr['post_type'] ) || ! post_type_exists( $postarr['post_type'] )  ) {
+                       return new WP_Error( 'unknown_post_type', __( 'Unknown post type' ) );
+               }
+               if ( ! isset( $postarr['post_title'] ) ) {
+                       $postarr['post_title'] = '';
+               }
+
+               add_filter( 'wp_insert_post_empty_content', '__return_false', 1000 );
+               $args = array(
+                       'post_status' => 'auto-draft',
+                       'post_type'   => $postarr['post_type'],
+                       'post_title'  => $postarr['post_title'],
+                       'post_name'   => sanitize_title( $postarr['post_title'] ), // Auto-drafts are allowed to have empty post_names, so we need to explicitly set it.
+               );
+               $r = wp_insert_post( wp_slash( $args ), true );
+               remove_filter( 'wp_insert_post_empty_content', '__return_false', 1000 );
+
+               if ( is_wp_error( $r ) ) {
+                       return $r;
+               } else {
+                       return get_post( $r );
+               }
+       }
+
+       /**
+        * Ajax handler for adding a new auto-draft post.
+        *
+        * @access public
+        * @since 4.7.0
+        */
+       public function ajax_insert_auto_draft_post() {
+               if ( ! check_ajax_referer( 'customize-menus', 'customize-menus-nonce', false ) ) {
+                       status_header( 400 );
+                       wp_send_json_error( 'bad_nonce' );
+               }
+
+               if ( ! current_user_can( 'customize' ) ) {
+                       status_header( 403 );
+                       wp_send_json_error( 'customize_not_allowed' );
+               }
+
+               if ( empty( $_POST['params'] ) || ! is_array( $_POST['params'] ) ) {
+                       status_header( 400 );
+                       wp_send_json_error( 'missing_params' );
+               }
+
+               $params = wp_array_slice_assoc(
+                       array_merge(
+                               array(
+                                       'post_type' => '',
+                                       'post_title' => '',
+                               ),
+                               wp_unslash( $_POST['params'] )
+                       ),
+                       array( 'post_type', 'post_title' )
+               );
+
+               if ( empty( $params['post_type'] ) || ! post_type_exists( $params['post_type'] ) ) {
+                       status_header( 400 );
+                       wp_send_json_error( 'missing_post_type_param' );
+               }
+
+               $post_type_object = get_post_type_object( $params['post_type'] );
+               if ( ! current_user_can( $post_type_object->cap->create_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) ) {
+                       status_header( 403 );
+                       wp_send_json_error( 'insufficient_post_permissions' );
+               }
+
+               $params['post_title'] = trim( $params['post_title'] );
+               if ( '' === $params['post_title'] ) {
+                       status_header( 400 );
+                       wp_send_json_error( 'missing_post_title' );
+               }
+
+               $r = $this->insert_auto_draft_post( $params );
+               if ( is_wp_error( $r ) ) {
+                       $error = $r;
+                       if ( ! empty( $post_type_object->labels->singular_name ) ) {
+                               $singular_name = $post_type_object->labels->singular_name;
+                       } else {
+                               $singular_name = __( 'Post' );
+                       }
+
+                       $data = array(
+                               /* translators: %1$s is the post type name and %2$s is the error message. */
+                               'message' => sprintf( __( '%1$s could not be created: %2$s' ), $singular_name, $error->get_error_message() ),
+                       );
+                       wp_send_json_error( $data );
+               } else {
+                       $post = $r;
+                       $data = array(
+                               'post_id' => $post->ID,
+                               'url'     => get_permalink( $post->ID ),
+                       );
+                       wp_send_json_success( $data );
+               }
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Print the JavaScript templates used to render Menu Customizer components.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * Templates are imported into the JS use wp.template.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -768,7 +891,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        <span class="spinner"></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                </div>
</span><span class="cx" style="display: block; padding: 0 10px">                                <button type="button" class="clear-results"><span class="screen-reader-text"><?php _e( 'Clear Results' ); ?></span></button>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <ul class="accordion-section-content" data-type="search"></ul>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <ul class="accordion-section-content available-menu-items-list" data-type="search"></ul>
</ins><span class="cx" style="display: block; padding: 0 10px">                         </div>
</span><span class="cx" style="display: block; padding: 0 10px">                        <div id="new-custom-menu-item" class="accordion-section">
</span><span class="cx" style="display: block; padding: 0 10px">                                <h4 class="accordion-section-title" role="presentation">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -797,7 +920,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                </div>
</span><span class="cx" style="display: block; padding: 0 10px">                        </div>
</span><span class="cx" style="display: block; padding: 0 10px">                        <?php
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // Containers for per-post-type item browsing; items added with JS.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       // Containers for per-post-type item browsing; items are added with JS.
</ins><span class="cx" style="display: block; padding: 0 10px">                         foreach ( $this->available_item_types() as $available_item_type ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $id = sprintf( 'available-menu-items-%s-%s', $available_item_type['type'], $available_item_type['object'] );
</span><span class="cx" style="display: block; padding: 0 10px">                                ?>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -813,7 +937,20 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                        <span class="toggle-indicator" aria-hidden="true"></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                                </button>
</span><span class="cx" style="display: block; padding: 0 10px">                                        </h4>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <ul class="accordion-section-content" data-type="<?php echo esc_attr( $available_item_type['type'] ); ?>" data-object="<?php echo esc_attr( $available_item_type['object'] ); ?>"></ul>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <div class="accordion-section-content">
+                                               <?php if ( 'post_type' === $available_item_type['type'] ) : ?>
+                                                       <?php $post_type_obj = get_post_type_object( $available_item_type['object'] ); ?>
+                                                       <?php if ( current_user_can( $post_type_obj->cap->create_posts ) && current_user_can( $post_type_obj->cap->publish_posts ) ) : ?>
+                                                               <div class="new-content-item">
+                                                                       <input type="text" class="create-item-input" placeholder="<?php
+                                                                       /* translators: %s: Singular title of post type or taxonomy */
+                                                                       printf( __( 'Create New %s' ), $post_type_obj->labels->singular_name ); ?>">
+                                                                       <button type="button" class="button add-content"><?php _e( 'Add' ); ?></button>
+                                                               </div>
+                                                       <?php endif; ?>
+                                               <?php endif; ?>
+                                               <ul class="available-menu-items-list" data-type="<?php echo esc_attr( $available_item_type['type'] ); ?>" data-object="<?php echo esc_attr( $available_item_type['object'] ); ?>" data-type_label="<?php echo esc_attr( isset( $available_item_type['type_label'] ) ? $available_item_type['type_label'] : $available_item_type['type'] ); ?>"></ul>
+                                       </div>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 </div>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -881,6 +1018,70 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Make the auto-draft status protected so that it can be queried.
+        *
+        * @since 4.7.0
+        * @access public
+        */
+       public function make_auto_draft_status_previewable() {
+               global $wp_post_statuses;
+               $wp_post_statuses['auto-draft']->protected = true;
+       }
+
+       /**
+        * Sanitize post IDs for auto-draft posts created for nav menu items to be published.
+        *
+        * @since 4.7.0
+        * @access public
+        *
+        * @param array $value Post IDs.
+        * @returns array Post IDs.
+        */
+       public function sanitize_nav_menus_created_posts( $value ) {
+               $post_ids = array();
+               foreach ( wp_parse_id_list( $value ) as $post_id ) {
+                       if ( empty( $post_id ) ) {
+                               continue;
+                       }
+                       $post = get_post( $post_id );
+                       if ( 'auto-draft' !== $post->post_status ) {
+                               continue;
+                       }
+                       $post_type_obj = get_post_type_object( $post->post_type );
+                       if ( ! $post_type_obj ) {
+                               continue;
+                       }
+                       if ( ! current_user_can( $post_type_obj->cap->publish_posts ) || ! current_user_can( $post_type_obj->cap->edit_post, $post_id ) ) {
+                               continue;
+                       }
+                       $post_ids[] = $post->ID;
+               }
+               return $post_ids;
+       }
+
+       /**
+        * Publish the auto-draft posts that were created for nav menu items.
+        *
+        * The post IDs will have been sanitized by already by
+        * `WP_Customize_Nav_Menu_Items::sanitize_nav_menus_created_posts()` to
+        * remove any post IDs for which the user cannot publish or for which the
+        * post is not an auto-draft.
+        *
+        * @since 4.7.0
+        * @access public
+        *
+        * @param WP_Customize_Setting $setting Customizer setting object.
+        */
+       public function save_nav_menus_created_posts( $setting ) {
+               $post_ids = $setting->post_value();
+               if ( ! empty( $post_ids ) ) {
+                       foreach ( $post_ids as $post_id ) {
+                               wp_publish_post( $post_id );
+                       }
+               }
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Keep track of the arguments that are being passed to wp_nav_menu().
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.3.0
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizesettingphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-customize-setting.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-setting.php      2016-08-29 21:07:31 UTC (rev 38435)
+++ trunk/src/wp-includes/class-wp-customize-setting.php        2016-08-29 22:58:32 UTC (rev 38436)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -498,6 +498,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Fetch and sanitize the $_POST value for the setting.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * During a save request prior to save, post_value() provides the new value while value() does not.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 3.4.0
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param mixed $default A default value which is used as a fallback. Default is null.
</span></span></pre></div>
<a id="trunktestsphpunittestsajaxCustomizeMenusphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/ajax/CustomizeMenus.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/ajax/CustomizeMenus.php 2016-08-29 21:07:31 UTC (rev 38435)
+++ trunk/tests/phpunit/tests/ajax/CustomizeMenus.php   2016-08-29 22:58:32 UTC (rev 38436)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -526,4 +526,114 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * Testing successful ajax_insert_auto_draft_post() call.
+        *
+        * @covers WP_Customize_Nav_Menus::ajax_insert_auto_draft_post()
+        */
+       function test_ajax_insert_auto_draft_post_success() {
+               $_POST = wp_slash( array(
+                       'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                       'params' => array(
+                               'post_type' => 'post',
+                               'post_title' => 'Hello World',
+                       ),
+               ) );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+
+               $this->assertTrue( $response['success'] );
+               $this->assertArrayHasKey( 'post_id', $response['data'] );
+               $this->assertArrayHasKey( 'url', $response['data'] );
+       }
+
+       /**
+        * Testing unsuccessful ajax_insert_auto_draft_post() call.
+        *
+        * @covers WP_Customize_Nav_Menus::ajax_insert_auto_draft_post()
+        */
+       function test_ajax_insert_auto_draft_failures() {
+               // No nonce.
+               $_POST = array();
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertEquals( 'bad_nonce', $response['data'] );
+
+               // Bad nonce.
+               $_POST = wp_slash( array(
+                       'customize-menus-nonce' => 'bad',
+               ) );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertEquals( 'bad_nonce', $response['data'] );
+
+               // Bad nonce.
+               wp_set_current_user( $this->factory()->user->create( array( 'role' => 'subscriber' ) ) );
+               $_POST = wp_slash( array(
+                       'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+               ) );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertEquals( 'customize_not_allowed', $response['data'] );
+
+               // Missing params.
+               wp_set_current_user( $this->factory()->user->create( array( 'role' => 'administrator' ) ) );
+               $_POST = wp_slash( array(
+                       'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+               ) );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertEquals( 'missing_params', $response['data'] );
+
+               // insufficient_post_permissions.
+               register_post_type( 'privilege', array( 'capability_type' => 'privilege' ) );
+               $_POST = wp_slash( array(
+                       'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                       'params' => array(
+                               'post_type' => 'privilege',
+                       ),
+               ) );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertEquals( 'insufficient_post_permissions', $response['data'] );
+
+               // insufficient_post_permissions.
+               $_POST = wp_slash( array(
+                       'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                       'params' => array(
+                               'post_type' => 'non-existent',
+                       ),
+               ) );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertEquals( 'missing_post_type_param', $response['data'] );
+
+               // missing_post_title.
+               $_POST = wp_slash( array(
+                       'customize-menus-nonce' => wp_create_nonce( 'customize-menus' ),
+                       'params' => array(
+                               'post_type' => 'post',
+                               'post_title' => '    ',
+                       ),
+               ) );
+               $this->_last_response = '';
+               $this->make_ajax_call( 'customize-nav-menus-insert-auto-draft' );
+               $response = json_decode( $this->_last_response, true );
+               $this->assertFalse( $response['success'] );
+               $this->assertEquals( 'missing_post_title', $response['data'] );
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunktestsphpunittestscustomizenavmenusphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/customize/nav-menus.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/customize/nav-menus.php 2016-08-29 21:07:31 UTC (rev 38435)
+++ trunk/tests/phpunit/tests/customize/nav-menus.php   2016-08-29 22:58:32 UTC (rev 38436)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -45,9 +45,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        function filter_item_types( $items ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $items[] = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'title'  => 'Custom',
-                       'type'   => 'custom_type',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'title' => 'Custom',
+                       'type' => 'custom_type',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'object' => 'custom_object',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'type_label' => 'Custom Type',
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                return $items;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -84,6 +85,21 @@
</span><span class="cx" style="display: block; padding: 0 10px">                do_action( 'customize_register', $this->wp_customize );
</span><span class="cx" style="display: block; padding: 0 10px">                $menus = new WP_Customize_Nav_Menus( $this->wp_customize );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertInstanceOf( 'WP_Customize_Manager', $menus->manager );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $this->assertEquals( 10, add_filter( 'customize_refresh_nonces', array( $menus, 'filter_nonces' ) ) );
+               $this->assertEquals( 10, add_action( 'wp_ajax_load-available-menu-items-customizer', array( $menus, 'ajax_load_available_items' ) ) );
+               $this->assertEquals( 10, add_action( 'wp_ajax_search-available-menu-items-customizer', array( $menus, 'ajax_search_available_items' ) ) );
+               $this->assertEquals( 10, add_action( 'wp_ajax_customize-nav-menus-insert-auto-draft', array( $menus, 'ajax_insert_auto_draft_post' ) ) );
+               $this->assertEquals( 10, add_action( 'customize_controls_enqueue_scripts', array( $menus, 'enqueue_scripts' ) ) );
+               $this->assertEquals( 11, add_action( 'customize_register', array( $menus, 'customize_register' ) ) );
+               $this->assertEquals( 10, add_filter( 'customize_dynamic_setting_args', array( $menus, 'filter_dynamic_setting_args' ) ) );
+               $this->assertEquals( 10, add_filter( 'customize_dynamic_setting_class', array( $menus, 'filter_dynamic_setting_class' ) ) );
+               $this->assertEquals( 10, add_action( 'customize_controls_print_footer_scripts', array( $menus, 'print_templates' ) ) );
+               $this->assertEquals( 10, add_action( 'customize_controls_print_footer_scripts', array( $menus, 'available_items_template' ) ) );
+               $this->assertEquals( 10, add_action( 'customize_preview_init', array( $menus, 'customize_preview_init' ) ) );
+               $this->assertEquals( 10, add_action( 'customize_preview_init', array( $menus, 'make_auto_draft_status_previewable' ) ) );
+               $this->assertEquals( 10, add_action( 'customize_save_nav_menus_created_posts', array( $menus, 'save_nav_menus_created_posts' ) ) );
+               $this->assertEquals( 10, add_filter( 'customize_dynamic_partial_args', array( $menus, 'customize_dynamic_partial_args' ) ) );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -444,10 +460,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'menu-item-title'     => 'Hello World',
</span><span class="cx" style="display: block; padding: 0 10px">                        'menu-item-status'    => 'publish',
</span><span class="cx" style="display: block; padding: 0 10px">                ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $setting = new WP_Customize_Nav_Menu_Item_Setting( $this->wp_customize, "nav_menu_item[$item_id]" );
</del><span class="cx" style="display: block; padding: 0 10px">                 do_action( 'customize_register', $this->wp_customize );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $this->assertInstanceOf( 'WP_Customize_Nav_Menu_Item_Setting', $this->wp_customize->get_setting( "nav_menu_item[$item_id]" ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->assertEquals( 'Primary', $this->wp_customize->get_section( "nav_menu[$menu_id]" )->title );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertEquals( 'Hello World', $this->wp_customize->get_control( "nav_menu_item[$item_id]" )->label );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $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 );
+               $this->assertEquals( 'postMessage', $nav_menus_created_posts_setting->transport );
+               $this->assertEquals( array(), $nav_menus_created_posts_setting->default );
+               $this->assertEquals( array( $this->wp_customize->nav_menus, 'sanitize_nav_menus_created_posts' ), $nav_menus_created_posts_setting->sanitize_callback );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -479,24 +501,24 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $menus = new WP_Customize_Nav_Menus( $this->wp_customize );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $expected = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        array( 'title' => 'Posts', 'type' => 'post_type', 'object' => 'post' ),
-                       array( 'title' => 'Pages', 'type' => 'post_type', 'object' => 'page' ),
-                       array( 'title' => 'Categories', 'type' => 'taxonomy', 'object' => 'category' ),
-                       array( 'title' => 'Tags', 'type' => 'taxonomy', 'object' => 'post_tag' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 array( 'title' => 'Posts', 'type' => 'post_type', 'object' => 'post', 'type_label' => __( 'Post' ) ),
+                       array( 'title' => 'Pages', 'type' => 'post_type', 'object' => 'page', 'type_label' => __( 'Page' ) ),
+                       array( 'title' => 'Categories', 'type' => 'taxonomy', 'object' => 'category', 'type_label' => __( 'Category' ) ),
+                       array( 'title' => 'Tags', 'type' => 'taxonomy', 'object' => 'post_tag', 'type_label' => __( 'Tag' ) ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( current_theme_supports( 'post-formats' ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $expected[] = array( 'title' => 'Format', 'type' => 'taxonomy', 'object' => 'post_format' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $expected[] = array( 'title' => 'Format', 'type' => 'taxonomy', 'object' => 'post_format', 'type_label' => __( 'Format' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertEquals( $expected, $menus->available_item_types() );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                register_taxonomy( 'wptests_tax', array( 'post' ), array( 'labels' => array( 'name' => 'Foo' ) ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $expected[] = array( 'title' => 'Foo', 'type' => 'taxonomy', 'object' => 'wptests_tax' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $expected[] = array( 'title' => 'Foo', 'type' => 'taxonomy', 'object' => 'wptests_tax', 'type_label' => 'Foo' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertEquals( $expected, $menus->available_item_types() );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $expected[] = array( 'title' => 'Custom', 'type' => 'custom_type', 'object' => 'custom_object' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $expected[] = array( 'title' => 'Custom', 'type' => 'custom_type', 'object' => 'custom_object', 'type_label' => 'Custom Type' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                add_filter( 'customize_nav_menu_available_item_types', array( $this, 'filter_item_types' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertEquals( $expected, $menus->available_item_types() );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -505,6 +527,29 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Test insert_auto_draft_post method.
+        *
+        * @covers WP_Customize_Nav_Menus::insert_auto_draft_post()
+        */
+       public function test_insert_auto_draft_post() {
+               $menus = new WP_Customize_Nav_Menus( $this->wp_customize );
+
+               $r = $menus->insert_auto_draft_post( array() );
+               $this->assertInstanceOf( 'WP_Error', $r );
+               $this->assertEquals( 'unknown_post_type', $r->get_error_code() );
+
+               $r = $menus->insert_auto_draft_post( array( 'post_type' => 'fake' ) );
+               $this->assertInstanceOf( 'WP_Error', $r );
+               $this->assertEquals( 'unknown_post_type', $r->get_error_code() );
+
+               $r = $menus->insert_auto_draft_post( array( 'post_title' => 'Hello World', 'post_type' => 'post' ) );
+               $this->assertInstanceOf( 'WP_Post', $r );
+               $this->assertEquals( 'Hello World', $r->post_title );
+               $this->assertEquals( 'post', $r->post_type );
+               $this->assertEquals( sanitize_title( $r->post_title ), $r->post_name );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Test the print_templates method.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @see WP_Customize_Nav_Menus::print_templates()
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -553,6 +598,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $this->assertRegExp( '#<h4 class="accordion-section-title".*>\s*' . esc_html( $type->labels->name ) . '#', $template );
</span><span class="cx" style="display: block; padding: 0 10px">                                $this->assertContains( 'data-type="post_type"', $template );
</span><span class="cx" style="display: block; padding: 0 10px">                                $this->assertContains( 'data-object="' . esc_attr( $type->name ) . '"', $template );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                $this->assertContains( 'data-type_label="' . esc_attr( $type->labels->singular_name ) . '"', $template );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -563,6 +609,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $this->assertRegExp( '#<h4 class="accordion-section-title".*>\s*' . esc_html( $tax->labels->name ) . '#', $template );
</span><span class="cx" style="display: block; padding: 0 10px">                                $this->assertContains( 'data-type="taxonomy"', $template );
</span><span class="cx" style="display: block; padding: 0 10px">                                $this->assertContains( 'data-object="' . esc_attr( $tax->name ) . '"', $template );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                $this->assertContains( 'data-type_label="' . esc_attr( $tax->labels->singular_name ) . '"', $template );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -570,6 +617,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertRegExp( '#<h4 class="accordion-section-title".*>\s*Custom#', $template );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertContains( 'data-type="custom_type"', $template );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertContains( 'data-object="custom_object"', $template );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $this->assertContains( 'data-type_label="Custom Type"', $template );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -610,6 +658,101 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Test make_auto_draft_status_previewable.
+        *
+        * @covers WP_Customize_Nav_Menus::make_auto_draft_status_previewable()
+        */
+       function test_make_auto_draft_status_previewable() {
+               global $wp_post_statuses;
+               $menus = new WP_Customize_Nav_Menus( $this->wp_customize );
+               $menus->make_auto_draft_status_previewable();
+               $this->assertTrue( $wp_post_statuses['auto-draft']->protected );
+       }
+
+       /**
+        * Test sanitize_nav_menus_created_posts.
+        *
+        * @covers WP_Customize_Nav_Menus::sanitize_nav_menus_created_posts()
+        */
+       function test_sanitize_nav_menus_created_posts() {
+               $menus = new WP_Customize_Nav_Menus( $this->wp_customize );
+               $contributor_user_id = $this->factory()->user->create( array( 'role' => 'contributor' ) );
+               $author_user_id = $this->factory()->user->create( array( 'role' => 'author' ) );
+               $administrator_user_id = $this->factory()->user->create( array( 'role' => 'administrator' ) );
+
+               $contributor_post_id = $this->factory()->post->create( array(
+                       'post_status' => 'auto-draft',
+                       'post_title' => 'Contributor Post',
+                       'post_type' => 'post',
+                       'post_author' => $contributor_user_id,
+               ) );
+               $author_post_id = $this->factory()->post->create( array(
+                       'post_status' => 'auto-draft',
+                       'post_title' => 'Author Post',
+                       'post_type' => 'post',
+                       'post_author' => $author_user_id,
+               ) );
+               $administrator_post_id = $this->factory()->post->create( array(
+                       'post_status' => 'auto-draft',
+                       'post_title' => 'Admin Post',
+                       'post_type' => 'post',
+                       'post_author' => $administrator_user_id,
+               ) );
+
+               $value = array(
+                       'bad',
+                       $contributor_post_id,
+                       $author_post_id,
+                       $administrator_post_id,
+               );
+
+               wp_set_current_user( $contributor_user_id );
+               $sanitized = $menus->sanitize_nav_menus_created_posts( $value );
+               $this->assertEquals( array(), $sanitized );
+
+               wp_set_current_user( $author_user_id );
+               $sanitized = $menus->sanitize_nav_menus_created_posts( $value );
+               $this->assertEquals( array( $author_post_id ), $sanitized );
+
+               wp_set_current_user( $administrator_user_id );
+               $sanitized = $menus->sanitize_nav_menus_created_posts( $value );
+               $this->assertEquals( array( $contributor_post_id, $author_post_id, $administrator_post_id ), $sanitized );
+       }
+
+       /**
+        * Test save_nav_menus_created_posts.
+        *
+        * @covers WP_Customize_Nav_Menus::save_nav_menus_created_posts()
+        */
+       function test_save_nav_menus_created_posts() {
+               $menus = new WP_Customize_Nav_Menus( $this->wp_customize );
+               do_action( 'customize_register', $this->wp_customize );
+
+               $post_ids = $this->factory()->post->create_many( 3, array(
+                       'post_status' => 'auto-draft',
+                       'post_type' => 'post',
+               ) );
+               $pre_published_post_id = $this->factory()->post->create( array( 'post_status' => 'publish' ) );
+
+               $setting_id = 'nav_menus_created_posts';
+               $this->wp_customize->set_post_value( $setting_id, array_merge( $post_ids, array( $pre_published_post_id ) ) );
+               $setting = $this->wp_customize->get_setting( $setting_id );
+               $this->assertInstanceOf( 'WP_Customize_Filter_Setting', $setting );
+               $this->assertEquals( array( $menus, 'sanitize_nav_menus_created_posts' ), $setting->sanitize_callback );
+               $this->assertEquals( $post_ids, $setting->post_value() );
+               foreach ( $post_ids as $post_id ) {
+                       $this->assertEquals( 'auto-draft', get_post_status( $post_id ) );
+               }
+
+               $save_action_count = did_action( 'customize_save_nav_menus_created_posts' );
+               $setting->save();
+               $this->assertEquals( $save_action_count + 1, did_action( 'customize_save_nav_menus_created_posts' ) );
+               foreach ( $post_ids as $post_id ) {
+                       $this->assertEquals( 'publish', get_post_status( $post_id ) );
+               }
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Test the filter_wp_nav_menu_args method.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @see WP_Customize_Nav_Menus::filter_wp_nav_menu_args()
</span></span></pre>
</div>
</div>

</body>
</html>