From c2945a394f1a3eeb5f0372e53e0f08f3b3c7aee7 Mon Sep 17 00:00:00 2001
From: Mark Jaquith
Date: Sat, 16 Feb 2013 04:53:59 +0000
Subject: [PATCH] Improve the UX of the Nav Menus screen. Kill the tabs, and
change to a dropdown, unless you have zero or one menus (which is the most
common), in which case you jump right into editing your sole menu.
Do assignment to location using checkboxes in the main menu editing
section instead of the backwards menu => location assignment in a
random meta box.
More to come, but this gets us started.
props lessbloat, DrewAPicture, jkudish. see #23119
git-svn-id: https://develop.svn.wordpress.org/trunk@23441 602fd350-edb4-49c9-b593-d223f7449a82
---
wp-admin/css/colors-fresh.css | 24 +-
wp-admin/css/wp-admin-rtl.css | 41 +++-
wp-admin/css/wp-admin.css | 186 ++++++++------
wp-admin/includes/nav-menu.php | 169 +++++++++++--
wp-admin/js/nav-menu.js | 356 +++++++++++++++------------
wp-admin/nav-menus.php | 433 ++++++++++++++++-----------------
6 files changed, 731 insertions(+), 478 deletions(-)
diff --git a/wp-admin/css/colors-fresh.css b/wp-admin/css/colors-fresh.css
index f8702d1528..febb39f530 100644
--- a/wp-admin/css/colors-fresh.css
+++ b/wp-admin/css/colors-fresh.css
@@ -400,7 +400,8 @@ div.dashboard-widget-submit input:hover,
.submitbox .submitdelete:hover,
#media-items a.delete:hover,
-#media-items a.delete-permanently:hover {
+#media-items a.delete-permanently:hover,
+#nav-menu-footer .menu-delete:hover {
color: #fff;
background-color: #f00;
border-bottom-color: #f00;
@@ -1682,6 +1683,27 @@ div.widgets-sortables,
background: #21759b;
color: #fff;
}
+
+.manage-menus {
+ border: 1px solid #eeeeee;
+ background: #fbfbfb;
+}
+
+.menu-settings {
+ border-top: 1px solid #eeeeee;
+}
+
+.theme-location-set {
+ color: #999999;
+}
+
+.nav-menus-php .delete-action a {
+ color: #bc0b0b;
+}
+
+.is-submenu {
+ color: #999999;
+}
/* end added from nav-menu.css */
.nav-tab {
diff --git a/wp-admin/css/wp-admin-rtl.css b/wp-admin/css/wp-admin-rtl.css
index 5d120e526f..78ba55381d 100644
--- a/wp-admin/css/wp-admin-rtl.css
+++ b/wp-admin/css/wp-admin-rtl.css
@@ -44,6 +44,8 @@ TABLE OF CONTENTS:
25.0 - TinyMCE tweaks
26.0 - Full Overlay w/ Sidebar
27.0 - Customize Loader
+28.0 - Nav Menus
+29.0 - HiDPI
------------------------------------------------------------------------------*/
@@ -2303,10 +2305,6 @@ body.login {
margin-right:0;
}
-.auto-add-pages {
- float: right;
-}
-
/* Star ratings */
div.star-holder {
background: url('../images/stars-rtl.png?ver=20121108') repeat-x bottom right;
@@ -2543,9 +2541,38 @@ div.sidebar-name h3 {
left: 20px;
}
-/**
- * HiDPI Displays
- */
+/*------------------------------------------------------------------------------
+ 28.0 - Nav Menus
+------------------------------------------------------------------------------*/
+.nav-menus-php .major-publishing-actions .publishing-action {
+ float: left;
+}
+
+.menu-settings dd {
+ float: right;
+ padding-right: 170px;
+}
+
+.manage-menus span {
+ float: right;
+}
+
+.menu-settings dt {
+ left: auto;
+ right: 0;
+}
+
+.menu-settings dd input,
+.menu-settings dd span,
+.menu-settings dd label,
+.manage-menus select {
+ float: right;
+ margin-right: 6px;
+}
+
+/*------------------------------------------------------------------------------
+ 29.0 - HiDPI
+------------------------------------------------------------------------------*/
@media print,
(-o-min-device-pixel-ratio: 5/4),
(-webkit-min-device-pixel-ratio: 1.25),
diff --git a/wp-admin/css/wp-admin.css b/wp-admin/css/wp-admin.css
index 55aa296fda..c22c5915ef 100644
--- a/wp-admin/css/wp-admin.css
+++ b/wp-admin/css/wp-admin.css
@@ -6704,8 +6704,17 @@ a.rsswidget {
/* nav-menu */
+.nav-menus-php #message {
+ display: none;
+}
+
+.no-js #message {
+ display: block;
+}
+
#nav-menus-frame {
margin-left: 300px;
+ margin-top: 28px;
}
#wpbody-content #menu-settings-column {
@@ -6714,11 +6723,7 @@ a.rsswidget {
margin-left: -300px;
clear: both;
float: left;
- padding-top: 24px;
-}
-
-.no-js #wpbody-content #menu-settings-column {
- padding-top: 31px;
+ padding-top: 0;
}
#menu-settings-column .inside {
@@ -6739,10 +6744,89 @@ a.rsswidget {
position: relative;
}
+.blank-slate .menu-name {
+ height: 2em;
+}
+
+.blank-slate .menu-settings {
+ border: none;
+ margin-top: 0;
+ padding-top: 0;
+ overflow: hidden;
+}
+
+.is-submenu {
+ font-style: italic;
+ font-weight: normal;
+ margin-left: 4px;
+}
+
+.manage-menus {
+ margin-top: 13px;
+ padding: 10px;
+ overflow: hidden;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+}
+
+.manage-menus select {
+ float: left;
+ margin-right: 6px;
+}
+
+.manage-menus .selected-menu {
+ float: left;
+ margin: 5px 6px 0 0;
+}
+
+.manage-menus .submit-btn {
+ float: left;
+ margin-top: 1px;
+}
+
+.menu-edit p {
+ margin: .3em 0 .6em;
+}
+
+.menu-settings {
+ margin-top: 2em;
+ padding-top: 16px;
+ overflow: hidden;
+}
+
+.menu-settings dl {
+ margin: 0 0 10px;
+ overflow: hidden;
+ position: relative;
+}
+
+.menu-settings dd {
+ float: left;
+ margin: 0;
+ width: 60%;
+ padding-left: 170px;
+}
+
+.menu-settings dt {
+ clear: both;
+ left: 0;
+ padding: 3px 0 0;
+ position: absolute;
+}
+
+.menu-edit .checkbox-input {
+ margin-top: 4px;
+}
+
+.theme-location-set {
+ font-size: 11px;
+}
+
/* Menu Container */
#menu-management-liquid {
float: left;
min-width: 100%;
+ margin-top: 3px;
}
#menu-management {
@@ -6757,7 +6841,7 @@ a.rsswidget {
}
.nav-menus-php #post-body {
- padding: 10px;
+ padding: 0 10px;
border-width: 1px 0;
border-style: solid;
}
@@ -6769,6 +6853,11 @@ a.rsswidget {
#nav-menu-header {
border-bottom: 1px solid;
+ margin-bottom: 13px;
+}
+
+#nav-menu-header .menu-name-label {
+ margin-top: 2px;
}
#nav-menu-footer {
@@ -6789,54 +6878,6 @@ a.rsswidget {
font-weight:bold;
}
-/* Menu Tabs */
-
-#menu-management .nav-tabs-nav {
- margin: 0 20px;
-}
-
-#menu-management .nav-tabs-arrow {
- width: 10px;
- padding: 0 5px 4px;
- cursor: pointer;
- position: absolute;
- top: 0;
- line-height: 22px;
- font-size: 18px;
-}
-
-#menu-management .nav-tabs-arrow-left {
- left: 0;
-}
-
-#menu-management .nav-tabs-arrow-right {
- right: 0;
- text-align: right;
-}
-
-#menu-management .nav-tabs-wrapper {
- width: 100%;
- height: 28px;
- margin-bottom: -1px;
- overflow: hidden;
-}
-
-#menu-management .nav-tabs {
- padding-left: 20px;
- padding-right: 10px;
-}
-
-.js #menu-management .nav-tabs {
- float: left;
- margin-left: 0px;
- margin-right: -400px;
-}
-
-#menu-management .nav-tab {
- margin-bottom: 0;
- font-size: 14px;
-}
-
#select-nav-menu-container {
text-align: right;
padding: 0 10px 3px 10px;
@@ -7033,7 +7074,8 @@ a.rsswidget {
}
#menu-to-edit {
- padding: 1em 0;
+ margin: 0;
+ padding: 0.1em 0;
}
.menu ul {
@@ -7049,7 +7091,7 @@ a.rsswidget {
clear:both;
line-height:1.5em;
position:relative;
- margin: 13px 0 0 0;
+ margin: 9px 0 0;
}
.menu-item-handle {
@@ -7269,34 +7311,16 @@ body.menu-max-depth-11 { min-width: 1280px !important; }
text-align: right;
float: right;
line-height: 23px;
- margin: 5px 0 1px;
+ margin: 2px 0 1px;
}
-.nav-menus-php .major-publishing-actions .delete-action {
- vertical-align: middle;
- text-align: left;
- float: left;
- padding-right: 15px;
- margin-top: 5px;
-}
-
-.menu-name-label span,
-.auto-add-pages label {
- font-size: 12px;
- font-style: normal;
-}
-
-.menu-name-label {
- margin-right: 15px;
-}
-
-.auto-add-pages input {
- margin-top: 0;
-}
-
-.auto-add-pages {
- margin-top: 4px;
+.nav-menus-php .blank-slate .menu-settings {
+ display: none;
+}
+
+.nav-menus-php .delete-action {
float: left;
+ margin-top: 2px;
}
.nav-menus-php .submitbox .submitcancel {
diff --git a/wp-admin/includes/nav-menu.php b/wp-admin/includes/nav-menu.php
index 60644628d9..c28ea891cf 100644
--- a/wp-admin/includes/nav-menu.php
+++ b/wp-admin/includes/nav-menu.php
@@ -81,11 +81,15 @@ class Walker_Nav_Menu_Edit extends Walker_Nav_Menu {
$title = empty( $item->label ) ? $title : $item->label;
+ $submenu_text = '';
+ if ( 0 == $depth )
+ $submenu_text = 'style="display: none;"';
+
?>
@@ -768,18 +790,18 @@ function wp_nav_menu_item_post_type_meta_box( $object, $post_type ) {
@@ -955,7 +977,7 @@ function wp_nav_menu_item_taxonomy_meta_box( $object, $taxonomy ) {
@@ -1086,7 +1108,7 @@ function wp_get_nav_menu_to_edit( $menu_id = 0 ) {
$menu_items = wp_get_nav_menu_items( $menu->term_id, array('post_status' => 'any') );
$result = '' : '">';
- $result .= '
' . __('Select menu items (pages, categories, links) from the boxes at left to begin building your custom menu.') . '
';
+ $result .= '
' . __( 'Next, add menu items (i.e. pages, links, categories) from the column on the left.' ) . '
';
$result .= '
';
if( empty($menu_items) )
@@ -1158,5 +1180,118 @@ function _wp_delete_orphaned_draft_menu_items() {
foreach( (array) $menu_items_to_delete as $menu_item_id )
wp_delete_post( $menu_item_id, true );
}
-
add_action('admin_head-nav-menus.php', '_wp_delete_orphaned_draft_menu_items');
+
+/**
+ * Delete nav menus from the nav menu management screen
+ *
+ * @since 3.6.0
+ * @access private
+ *
+ * @param int|string $nav_menu_id The menu to delete (id, slug, or name)
+ * @return false if Error, otherwise true
+ */
+function _wp_delete_nav_menu( $nav_menu_id ) {
+
+ if ( ! is_nav_menu( $nav_menu_id ) )
+ return;
+
+ $deleted_nav_menu = wp_get_nav_menu_object( $nav_menu_id );
+ $delete_nav_menu = wp_delete_nav_menu( $nav_menu_id );
+
+ if ( is_wp_error( $delete_nav_menu ) )
+ return $delete_nav_menu;
+
+ // Remove this menu from any locations.
+ $locations = get_theme_mod( 'nav_menu_locations' );
+ foreach ( (array) $locations as $location => $menu_id ) {
+ if ( $menu_id == $nav_menu_id )
+ $locations[ $location ] = 0;
+ }
+ set_theme_mod( 'nav_menu_locations', $locations );
+ return true;
+}
+
+/**
+ * Saves nav menu items
+ *
+ * @since 3.6.0
+ *
+ * @uses wp_get_nav_menu_items() to retrieve the nav menu's menu items
+ * @uses wp_defer_term_counter() to enable then disable term counting
+ *
+ * @param int|string $nav_menu_selected_id (id, slug, or name ) of the currently-selected menu
+ * @param string $nav_menu_selected_title Title of the currently-selected menu
+ * @return array $messages The menu updated message
+ */
+function wp_nav_menu_update_menu_items ( $nav_menu_selected_id, $nav_menu_selected_title ) {
+ $unsorted_menu_items = wp_get_nav_menu_items( $nav_menu_selected_id, array( 'orderby' => 'ID', 'output' => ARRAY_A, 'output_key' => 'ID', 'post_status' => 'draft,publish' ) );
+
+ $menu_items = array();
+ // Index menu items by db ID
+ foreach ( $unsorted_menu_items as $_item )
+ $menu_items[$_item->db_id] = $_item;
+
+ $post_fields = array(
+ 'menu-item-db-id', 'menu-item-object-id', 'menu-item-object',
+ 'menu-item-parent-id', 'menu-item-position', 'menu-item-type',
+ 'menu-item-title', 'menu-item-url', 'menu-item-description',
+ 'menu-item-attr-title', 'menu-item-target', 'menu-item-classes', 'menu-item-xfn'
+ );
+
+ wp_defer_term_counting( true );
+ // Loop through all the menu items' POST variables
+ if ( ! empty( $_POST['menu-item-db-id'] ) ) {
+ foreach( (array) $_POST['menu-item-db-id'] as $_key => $k ) {
+
+ // Menu item title can't be blank
+ if ( empty( $_POST['menu-item-title'][$_key] ) )
+ continue;
+
+ $args = array();
+ foreach ( $post_fields as $field )
+ $args[$field] = isset( $_POST[$field][$_key] ) ? $_POST[$field][$_key] : '';
+
+ $menu_item_db_id = wp_update_nav_menu_item( $nav_menu_selected_id, ( $_POST['menu-item-db-id'][$_key] != $_key ? 0 : $_key ), $args );
+
+ if ( is_wp_error( $menu_item_db_id ) )
+ $messages[] = '' . $menu_item_db_id->get_error_message() . '
';
+ elseif ( isset( $menu_items[$menu_item_db_id] ) )
+ unset( $menu_items[$menu_item_db_id] );
+ }
+ }
+
+ // Remove menu items from the menu that weren't in $_POST
+ if ( ! empty( $menu_items ) ) {
+ foreach ( array_keys( $menu_items ) as $menu_item_id ) {
+ if ( is_nav_menu_item( $menu_item_id ) ) {
+ wp_delete_post( $menu_item_id );
+ }
+ }
+ }
+
+ // Store 'auto-add' pages.
+ $auto_add = ! empty( $_POST['auto-add-pages'] );
+ $nav_menu_option = (array) get_option( 'nav_menu_options' );
+ if ( ! isset( $nav_menu_option['auto_add'] ) )
+ $nav_menu_option['auto_add'] = array();
+ if ( $auto_add ) {
+ if ( ! in_array( $nav_menu_selected_id, $nav_menu_option['auto_add'] ) )
+ $nav_menu_option['auto_add'][] = $nav_menu_selected_id;
+ } else {
+ if ( false !== ( $key = array_search( $nav_menu_selected_id, $nav_menu_option['auto_add'] ) ) )
+ unset( $nav_menu_option['auto_add'][$key] );
+ }
+ // Remove nonexistent/deleted menus
+ $nav_menu_option['auto_add'] = array_intersect( $nav_menu_option['auto_add'], wp_get_nav_menus( array( 'fields' => 'ids' ) ) );
+ update_option( 'nav_menu_options', $nav_menu_option );
+
+ wp_defer_term_counting( false );
+
+ do_action( 'wp_update_nav_menu', $nav_menu_selected_id );
+
+ $messages[] = '' . sprintf( __( '%1$s has been updated.' ), $nav_menu_selected_title ) . '
';
+ unset( $menu_items, $unsorted_menu_items );
+
+ return $messages;
+}
diff --git a/wp-admin/js/nav-menu.js b/wp-admin/js/nav-menu.js
index 20f0fc3bec..5380c98e3d 100644
--- a/wp-admin/js/nav-menu.js
+++ b/wp-admin/js/nav-menu.js
@@ -45,9 +45,14 @@ var wpNavMenu;
if( api.menuList.length ) // If no menu, we're in the + tab.
this.initSortables();
- this.initToggles();
+ if( oneThemeLocationNoMenus )
+ $( '#posttype-page' ).addSelectedToMenu( api.addMenuItemToBottom );
- this.initTabManager();
+ this.messageFadeIn();
+
+ this.initAccessibility();
+
+ this.initToggles();
},
jQueryExtensions : function() {
@@ -84,19 +89,55 @@ var wpNavMenu;
});
return result;
},
+ shiftHorizontally : function( dir ) {
+ return this.each(function(){
+ var t = $(this),
+ depth = t.menuItemDepth(),
+ newDepth = depth + dir;
+
+ // Change .menu-item-depth-n class
+ t.moveHorizontally( newDepth, depth );
+ });
+ },
+ moveHorizontally : function( newDepth, depth ) {
+ return this.each(function(){
+ var t = $(this),
+ children = t.childMenuItems(),
+ diff = newDepth - depth,
+ subItemText = t.find('.is-submenu');
+
+ // Change .menu-item-depth-n class
+ t.updateDepthClass( newDepth, depth ).updateParentMenuItemDBId();
+
+ // If it has children, move those too
+ if ( children ) {
+ children.each(function( index ) {
+ var t = $(this),
+ thisDepth = t.menuItemDepth(),
+ newDepth = thisDepth + diff;
+ t.updateDepthClass(newDepth, thisDepth).updateParentMenuItemDBId();
+ });
+ }
+
+ // Show "Sub item" helper text
+ if (0 === newDepth)
+ subItemText.hide();
+ else
+ subItemText.show();
+ });
+ },
updateParentMenuItemDBId : function() {
return this.each(function(){
var item = $(this),
- input = item.find('.menu-item-data-parent-id'),
- depth = item.menuItemDepth(),
- parent = item.prev();
+ input = item.find( '.menu-item-data-parent-id' ),
+ depth = parseInt( item.menuItemDepth() ),
+ parentDepth = depth - 1,
+ parent = item.prevAll( '.menu-item-depth-' + parentDepth ).first();
- if( depth == 0 ) { // Item is on the top level, has no parent
+ if ( 0 == depth ) { // Item is on the top level, has no parent
input.val(0);
} else { // Find the parent item, and retrieve its object id.
- while( ! parent[0] || ! parent[0].className || -1 == parent[0].className.indexOf('menu-item') || ( parent.menuItemDepth() != depth - 1 ) )
- parent = parent.prev();
- input.val( parent.find('.menu-item-data-db-id').val() );
+ input.val( parent.find( '.menu-item-data-db-id' ).val() );
}
});
},
@@ -120,7 +161,7 @@ var wpNavMenu;
return this.each(function() {
var t = $(this), menuItems = {},
- checkboxes = t.find('.tabs-panel-active .categorychecklist li input:checked'),
+ checkboxes = ( oneThemeLocationNoMenus && 0 == t.find('.tabs-panel-active .categorychecklist li input:checked').length ) ? t.find('#page-all li input[type="checkbox"]') : t.find('.tabs-panel-active .categorychecklist li input:checked'),
re = new RegExp('menu-item\\[(\[^\\]\]*)');
processMethod = processMethod || api.addMenuItemToBottom;
@@ -223,6 +264,128 @@ var wpNavMenu;
});
},
+ initAccessibility : function() {
+ $( '.item-edit' ).off( 'focus' ).on( 'focus', function(){
+ $(this).on( 'keydown', function(e){
+
+ var $this = $(this);
+
+ // Bail if it's not an arrow key
+ if ( 37 != e.which && 38 != e.which && 39 != e.which && 40 != e.which )
+ return;
+
+ // Avoid multiple keydown events
+ $this.off('keydown');
+
+ var menuItems = $('#menu-to-edit li');
+ menuItemsCount = menuItems.length,
+ thisItem = $this.parents( 'li.menu-item' ),
+ thisItemChildren = thisItem.childMenuItems(),
+ thisItemData = thisItem.getItemData(),
+ thisItemDepth = parseInt( thisItem.menuItemDepth() ),
+ thisItemPosition = parseInt( thisItem.index() ),
+ nextItem = thisItem.next(),
+ nextItemChildren = nextItem.childMenuItems(),
+ nextItemDepth = parseInt( nextItem.menuItemDepth() ) + 1,
+ prevItem = thisItem.prev(),
+ prevItemDepth = parseInt( prevItem.menuItemDepth() ),
+ prevItemId = prevItem.getItemData()['menu-item-db-id'];
+
+ // Bail if there is only one menu item
+ if ( 1 === menuItemsCount )
+ return;
+
+ // If RTL, swap left/right arrows
+ var arrows = { '38' : 'up', '40' : 'down', '37' : 'left', '39' : 'right' };
+ if ( $('body').hasClass('rtl') )
+ arrows = { '38' : 'up', '40' : 'down', '39' : 'left', '37' : 'right' };
+
+ switch ( arrows[e.which] ) {
+ case 'up':
+ var newItemPosition = thisItemPosition - 1;
+
+ // Already at top
+ if ( 0 === thisItemPosition )
+ break;
+
+ // If a sub item is moved to top, shift it to 0 depth
+ if ( 0 === newItemPosition && 0 !== thisItemDepth )
+ thisItem.moveHorizontally( 0, thisItemDepth );
+
+ // If prev item is sub item, shift to match depth
+ if ( 0 !== prevItemDepth )
+ thisItem.moveHorizontally( prevItemDepth, thisItemDepth );
+
+ // Does this item have sub items?
+ if ( thisItemChildren ) {
+ var items = thisItem.add( thisItemChildren );
+ // Move the entire block
+ items.detach().insertBefore( menuItems.eq( newItemPosition ) );
+ } else {
+ thisItem.detach().insertBefore( menuItems.eq( newItemPosition ) );
+ }
+ break;
+ case 'down':
+ // Does this item have sub items?
+ if ( thisItemChildren ) {
+ var items = thisItem.add( thisItemChildren ),
+ nextItem = menuItems.eq( items.length + thisItemPosition ),
+ nextItemChildren = 0 !== nextItem.childMenuItems().length;
+
+ if ( nextItemChildren ) {
+ var newDepth = parseInt( nextItem.menuItemDepth() ) + 1;
+ thisItem.moveHorizontally( newDepth, thisItemDepth );
+ }
+
+ // Have we reached the bottom?
+ if ( menuItemsCount === thisItemPosition + items.length )
+ break;
+
+ items.detach().insertAfter( menuItems.eq( thisItemPosition + items.length ) );
+ } else {
+ // If next item has sub items, shift depth
+ if ( 0 !== nextItemChildren.length )
+ thisItem.moveHorizontally( nextItemDepth, thisItemDepth );
+
+ // Have we reached the bottom
+ if ( menuItemsCount === thisItemPosition + 1 )
+ break;
+ thisItem.detach().insertAfter( menuItems.eq( thisItemPosition + 1 ) );
+ }
+ break;
+ case 'left':
+ // As far left as possible
+ if ( 0 === thisItemDepth )
+ break;
+ thisItem.shiftHorizontally( -1 );
+ break;
+ case 'right':
+ // Can't be sub item at top
+ if ( 0 === thisItemPosition )
+ break;
+ // Already sub item of prevItem
+ if ( thisItemData['menu-item-parent-id'] === prevItemId )
+ break;
+ thisItem.shiftHorizontally( 1 );
+ break;
+ }
+ api.registerChange();
+ // Put focus back on same menu item
+ $( '#edit-' + thisItemData['menu-item-db-id'] ).focus();
+ return false;
+ });
+ }).blur(function () {
+ $(this).off( 'keydown' );
+ });
+ },
+
+ messageFadeIn : function() {
+ var messages = $( '#message' );
+
+ // Visual change when users save menus multiple times in a row
+ messages.slideDown( 'slow' );
+ },
+
initToggles : function() {
// init postboxes
postboxes.add_postbox_toggles('nav-menus');
@@ -246,6 +409,9 @@ var wpNavMenu;
body = $('body'), maxChildDepth,
menuMaxDepth = initialMenuMaxDepth();
+ if( 0 != $( '#menu-to-edit li' ).length )
+ $( '.drag-instructions' ).show();
+
// Use the right edge if RTL.
menuEdge += api.isRTL ? api.menuList.width() : 0;
@@ -308,6 +474,13 @@ var wpNavMenu;
// Return child elements to the list
children = transport.children().insertAfter(ui.item);
+ // Add "sub menu" description
+ var subMenuTitle = ui.item.find( '.item-title .is-submenu' );
+ if ( 0 < currentDepth )
+ subMenuTitle.show();
+ else
+ subMenuTitle.hide();
+
// Update depth classes
if( depthChange != 0 ) {
ui.item.updateDepthClass( currentDepth );
@@ -327,9 +500,6 @@ var wpNavMenu;
ui.item[0].style.left = 'auto';
ui.item[0].style.right = 0;
}
-
- // The width of the tab bar might have changed. Just in case.
- api.refreshMenuTabs( true );
},
change: function(e, ui) {
// Make sure the placeholder is inside the menu.
@@ -461,6 +631,8 @@ var wpNavMenu;
if( '' == $t.val() )
$t.addClass( name ).val( $t.data(name) );
});
+
+ $( '.blank-slate .input-with-default-title' ).focus();
},
attachThemeLocationsListeners : function() {
@@ -572,8 +744,11 @@ var wpNavMenu;
$.post( ajaxurl, params, function(menuMarkup) {
var ins = $('#menu-instructions');
processMethod(menuMarkup, params);
- if( ! ins.hasClass('menu-instructions-inactive') && ins.siblings().length )
- ins.addClass('menu-instructions-inactive');
+ // Make it stand out a bit more visually, by adding a fadeIn
+ $( 'li.pending' ).hide().fadeIn('slow');
+ $( '.drag-instructions' ).show();
+ if( ! ins.hasClass( 'menu-instructions-inactive' ) && ins.siblings().length )
+ ins.addClass( 'menu-instructions-inactive' );
callback();
});
},
@@ -586,10 +761,12 @@ var wpNavMenu;
*/
addMenuItemToBottom : function( menuMarkup, req ) {
$(menuMarkup).hideAdvancedMenuItemFields().appendTo( api.targetList );
+ api.initAccessibility();
},
addMenuItemToTop : function( menuMarkup, req ) {
$(menuMarkup).hideAdvancedMenuItemFields().prependTo( api.targetList );
+ api.initAccessibility();
},
attachUnsavedChangesListener : function() {
@@ -604,7 +781,7 @@ var wpNavMenu;
};
} else {
// Make the post boxes read-only, as they can't be used yet
- $('#menu-settings-column').find('input,select').prop('disabled', true).end().find('a').attr('href', '#').unbind('click');
+ $( '#menu-settings-column' ).find( 'input,select' ).end().find( 'a' ).attr( 'href', '#' ).unbind( 'click' );
}
},
@@ -688,139 +865,6 @@ var wpNavMenu;
});
},
- initTabManager : function() {
- var fixed = $('.nav-tabs-wrapper'),
- fluid = fixed.children('.nav-tabs'),
- active = fluid.children('.nav-tab-active'),
- tabs = fluid.children('.nav-tab'),
- tabsWidth = 0,
- fixedRight, fixedLeft,
- arrowLeft, arrowRight, resizeTimer, css = {},
- marginFluid = api.isRTL ? 'margin-right' : 'margin-left',
- marginFixed = api.isRTL ? 'margin-left' : 'margin-right',
- msPerPx = 2;
-
- /**
- * Refreshes the menu tabs.
- * Will show and hide arrows where necessary.
- * Scrolls to the active tab by default.
- *
- * @param savePosition {boolean} Optional. Prevents scrolling so
- * that the current position is maintained. Default false.
- **/
- api.refreshMenuTabs = function( savePosition ) {
- var fixedWidth = fixed.width(),
- margin = 0, css = {};
- fixedLeft = fixed.offset().left;
- fixedRight = fixedLeft + fixedWidth;
-
- if( !savePosition )
- active.makeTabVisible();
-
- // Prevent space from building up next to the last tab if there's more to show
- if( tabs.last().isTabVisible() ) {
- margin = fixed.width() - tabsWidth;
- margin = margin > 0 ? 0 : margin;
- css[marginFluid] = margin + 'px';
- fluid.animate( css, 100, "linear" );
- }
-
- // Show the arrows only when necessary
- if( fixedWidth > tabsWidth )
- arrowLeft.add( arrowRight ).hide();
- else
- arrowLeft.add( arrowRight ).show();
- }
-
- $.fn.extend({
- makeTabVisible : function() {
- var t = this.eq(0), left, right, css = {}, shift = 0;
-
- if( ! t.length ) return this;
-
- left = t.offset().left;
- right = left + t.outerWidth();
-
- if( right > fixedRight )
- shift = fixedRight - right;
- else if ( left < fixedLeft )
- shift = fixedLeft - left;
-
- if( ! shift ) return this;
-
- css[marginFluid] = "+=" + api.negateIfRTL * shift + 'px';
- fluid.animate( css, Math.abs( shift ) * msPerPx, "linear" );
- return this;
- },
- isTabVisible : function() {
- var t = this.eq(0),
- left = t.offset().left,
- right = left + t.outerWidth();
- return ( right <= fixedRight && left >= fixedLeft ) ? true : false;
- }
- });
-
- // Find the width of all tabs
- tabs.each(function(){
- tabsWidth += $(this).outerWidth(true);
- });
-
- // Set up fixed margin for overflow, unset padding
- css['padding'] = 0;
- css[marginFixed] = (-1 * tabsWidth) + 'px';
- fluid.css( css );
-
- // Build tab navigation
- arrowLeft = $('');
- arrowRight = $('');
- // Attach to the document
- fixed.wrap('').parent().prepend( arrowLeft ).append( arrowRight );
-
- // Set the menu tabs
- api.refreshMenuTabs();
- // Make sure the tabs reset on resize
- $(window).resize(function() {
- if( resizeTimer ) clearTimeout(resizeTimer);
- resizeTimer = setTimeout( api.refreshMenuTabs, 200);
- });
-
- // Build arrow functions
- $.each([{
- arrow : arrowLeft,
- next : "next",
- last : "first",
- operator : "+="
- },{
- arrow : arrowRight,
- next : "prev",
- last : "last",
- operator : "-="
- }], function(){
- var that = this;
- this.arrow.mousedown(function(){
- var marginFluidVal = Math.abs( parseInt( fluid.css(marginFluid) ) ),
- shift = marginFluidVal,
- css = {};
-
- if( "-=" == that.operator )
- shift = Math.abs( tabsWidth - fixed.width() ) - marginFluidVal;
-
- if( ! shift ) return;
-
- css[marginFluid] = that.operator + shift + 'px';
- fluid.animate( css, shift * msPerPx, "linear" );
- }).mouseup(function(){
- var tab, next;
- fluid.stop(true);
- tab = tabs[that.last]();
- while( (next = tab[that.next]()) && next.length && ! next.isTabVisible() ) {
- tab = next;
- }
- tab.makeTabVisible();
- });
- });
- },
-
eventOnClickEditLink : function(clickedEl) {
var settings, item,
matchedSection = /#(.*)$/.exec(clickedEl.href);
@@ -846,8 +890,10 @@ var wpNavMenu;
},
eventOnClickCancelLink : function(clickedEl) {
- var settings = $(clickedEl).closest('.menu-item-settings');
- settings.setItemData( settings.data('menu-item-data') );
+ var settings = $( clickedEl ).closest( '.menu-item-settings' ),
+ thisMenuItem = $( clickedEl ).closest( '.menu-item' );
+ thisMenuItem.removeClass('menu-item-edit-active').addClass('menu-item-edit-inactive');
+ settings.setItemData( settings.data('menu-item-data') ).hide();
return false;
},
@@ -944,9 +990,11 @@ var wpNavMenu;
}, 350, function() {
var ins = $('#menu-instructions');
el.remove();
- children.shiftDepthClass(-1).updateParentMenuItemDBId();
- if( ! ins.siblings().length )
- ins.removeClass('menu-instructions-inactive');
+ children.shiftDepthClass( -1 ).updateParentMenuItemDBId();
+ if( 0 == $( '#menu-to-edit li' ).length ) {
+ $( '.drag-instructions' ).hide();
+ ins.removeClass( 'menu-instructions-inactive' );
+ }
});
},
diff --git a/wp-admin/nav-menus.php b/wp-admin/nav-menus.php
index 98f95a0e0d..9ac0d76881 100644
--- a/wp-admin/nav-menus.php
+++ b/wp-admin/nav-menus.php
@@ -221,49 +221,64 @@ switch ( $action ) {
if ( is_nav_menu_item( $menu_item_id ) && wp_delete_post( $menu_item_id, true ) )
$messages[] = '' . __('The menu item has been successfully deleted.') . '
';
break;
+
case 'delete':
check_admin_referer( 'delete-nav_menu-' . $nav_menu_selected_id );
-
if ( is_nav_menu( $nav_menu_selected_id ) ) {
- $deleted_nav_menu = wp_get_nav_menu_object( $nav_menu_selected_id );
- $delete_nav_menu = wp_delete_nav_menu( $nav_menu_selected_id );
-
- if ( is_wp_error($delete_nav_menu) ) {
- $messages[] = '' . $delete_nav_menu->get_error_message() . '
';
- } else {
- // Remove this menu from any locations.
- $locations = get_theme_mod( 'nav_menu_locations' );
- foreach ( (array) $locations as $location => $menu_id ) {
- if ( $menu_id == $nav_menu_selected_id )
- $locations[ $location ] = 0;
- }
- set_theme_mod( 'nav_menu_locations', $locations );
- $messages[] = '' . __('The menu has been successfully deleted.') . '
';
- // Select the next available menu
- $nav_menu_selected_id = 0;
- $_nav_menus = wp_get_nav_menus( array('orderby' => 'name') );
- foreach( $_nav_menus as $index => $_nav_menu ) {
- if ( strcmp( $_nav_menu->name, $deleted_nav_menu->name ) >= 0
- || $index == count( $_nav_menus ) - 1 ) {
- $nav_menu_selected_id = $_nav_menu->term_id;
- break;
- }
- }
- }
- unset( $delete_nav_menu, $deleted_nav_menu, $_nav_menus );
+ $deletion = _wp_delete_nav_menu( $nav_menu_selected_id );
} else {
// Reset the selected menu
$nav_menu_selected_id = 0;
unset( $_REQUEST['menu'] );
}
+
+ if ( ! isset( $deletion ) )
+ break;
+
+ if ( is_wp_error( $deletion ) )
+ $messages[] = '' . $deletion->get_error_message() . '
';
+ else
+ $messages[] = '' . __( 'The menu has been successfully deleted.' ) . '
';
+ break;
+
+ case 'delete_menus':
+ check_admin_referer( 'nav_menus_bulk_actions' );
+ foreach ( $_REQUEST['delete_menus'] as $menu_id_to_delete ) {
+ if ( ! is_nav_menu( $menu_id_to_delete ) )
+ continue;
+
+ $deletion = _wp_delete_nav_menu( $menu_id_to_delete );
+ if ( is_wp_error( $deletion ) ) {
+ $messages[] = '' . $deletion->get_error_message() . '
';
+ $deletion_error = true;
+ }
+ }
+
+ if ( empty( $deletion_error ) )
+ $messages[] = '' . __( 'Selected menus have been successfully deleted.' ) . '
';
break;
case 'update':
check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' );
- // Update menu theme locations
- if ( isset( $_POST['menu-locations'] ) )
- set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) );
+ // Get existing menu locations assignments
+ $locations = get_registered_nav_menus();
+ $menu_locations = get_nav_menu_locations();
+
+ // Remove menu locations that have been unchecked
+ foreach ( $locations as $location => $description ) {
+ if ( ( empty( $_POST['menu-locations'] ) || empty( $_POST['menu-locations'][ $location ] ) ) && isset( $menu_locations[ $location ] ) && $menu_locations[ $location ] == $nav_menu_selected_id )
+ unset( $menu_locations[ $location ] );
+ }
+
+ // Merge new and existing menu locations if any new ones are set
+ if ( isset( $_POST['menu-locations'] ) ) {
+ $new_menu_locations = array_map( 'absint', $_POST['menu-locations'] );
+ $menu_locations = array_merge( $menu_locations, $new_menu_locations );
+ }
+
+ // Set menu locations
+ set_theme_mod( 'nav_menu_locations', $menu_locations );
// Add Menu
if ( 0 == $nav_menu_selected_id ) {
@@ -278,20 +293,33 @@ switch ( $action ) {
$_menu_object = wp_get_nav_menu_object( $_nav_menu_selected_id );
$nav_menu_selected_id = $_nav_menu_selected_id;
$nav_menu_selected_title = $_menu_object->name;
- $messages[] = '' . sprintf( __('The %s menu has been successfully created.'), $nav_menu_selected_title ) . '
';
+ if ( isset( $_REQUEST['menu-item'] ) )
+ wp_save_nav_menu_items( $nav_menu_selected_id, absint( $_REQUEST['menu-item'] ) );
+ if ( isset( $_REQUEST['zero-menu-state'] ) ) {
+ // If there are menu items, add them
+ wp_nav_menu_update_menu_items( $nav_menu_selected_id, $nav_menu_selected_title );
+ // Auto-save nav_menu_locations
+ $locations = get_theme_mod( 'nav_menu_locations' );
+ foreach ( (array) $locations as $location => $menu_id ) {
+ $locations[ $location ] = $nav_menu_selected_id;
+ break; // There should only be 1
+ }
+ set_theme_mod( 'nav_menu_locations', $locations );
+ }
+ $messages[] = '' . sprintf( __( '%s has been created.' ), $nav_menu_selected_title ) . '
';
}
} else {
- $messages[] = '' . __('Please enter a valid menu name.') . '
';
+ $messages[] = '' . __( 'Please enter a valid menu name.' ) . '
';
}
- // update existing menu
+ // Update existing menu
} else {
$_menu_object = wp_get_nav_menu_object( $nav_menu_selected_id );
$menu_title = trim( esc_html( $_POST['menu-name'] ) );
if ( ! $menu_title ) {
- $messages[] = '' . __('Please enter a valid menu name.') . '
';
+ $messages[] = '' . __( 'Please enter a valid menu name.' ) . '
';
$menu_title = $_menu_object->name;
}
@@ -307,68 +335,8 @@ switch ( $action ) {
}
// Update menu items
-
if ( ! is_wp_error( $_menu_object ) ) {
- $unsorted_menu_items = wp_get_nav_menu_items( $nav_menu_selected_id, array('orderby' => 'ID', 'output' => ARRAY_A, 'output_key' => 'ID', 'post_status' => 'draft,publish') );
- $menu_items = array();
- // Index menu items by db ID
- foreach( $unsorted_menu_items as $_item )
- $menu_items[$_item->db_id] = $_item;
-
- $post_fields = array( 'menu-item-db-id', 'menu-item-object-id', 'menu-item-object', 'menu-item-parent-id', 'menu-item-position', 'menu-item-type', 'menu-item-title', 'menu-item-url', 'menu-item-description', 'menu-item-attr-title', 'menu-item-target', 'menu-item-classes', 'menu-item-xfn' );
- wp_defer_term_counting(true);
- // Loop through all the menu items' POST variables
- if ( ! empty( $_POST['menu-item-db-id'] ) ) {
- foreach( (array) $_POST['menu-item-db-id'] as $_key => $k ) {
-
- // Menu item title can't be blank
- if ( empty( $_POST['menu-item-title'][$_key] ) )
- continue;
-
- $args = array();
- foreach ( $post_fields as $field )
- $args[$field] = isset( $_POST[$field][$_key] ) ? $_POST[$field][$_key] : '';
-
- $menu_item_db_id = wp_update_nav_menu_item( $nav_menu_selected_id, ( $_POST['menu-item-db-id'][$_key] != $_key ? 0 : $_key ), $args );
-
- if ( is_wp_error( $menu_item_db_id ) )
- $messages[] = '' . $menu_item_db_id->get_error_message() . '
';
- elseif ( isset( $menu_items[$menu_item_db_id] ) )
- unset( $menu_items[$menu_item_db_id] );
- }
- }
-
- // Remove menu items from the menu that weren't in $_POST
- if ( ! empty( $menu_items ) ) {
- foreach ( array_keys( $menu_items ) as $menu_item_id ) {
- if ( is_nav_menu_item( $menu_item_id ) ) {
- wp_delete_post( $menu_item_id );
- }
- }
- }
-
- // Store 'auto-add' pages.
- $auto_add = ! empty( $_POST['auto-add-pages'] );
- $nav_menu_option = (array) get_option( 'nav_menu_options' );
- if ( ! isset( $nav_menu_option['auto_add'] ) )
- $nav_menu_option['auto_add'] = array();
- if ( $auto_add ) {
- if ( ! in_array( $nav_menu_selected_id, $nav_menu_option['auto_add'] ) )
- $nav_menu_option['auto_add'][] = $nav_menu_selected_id;
- } else {
- if ( false !== ( $key = array_search( $nav_menu_selected_id, $nav_menu_option['auto_add'] ) ) )
- unset( $nav_menu_option['auto_add'][$key] );
- }
- // Remove nonexistent/deleted menus
- $nav_menu_option['auto_add'] = array_intersect( $nav_menu_option['auto_add'], wp_get_nav_menus( array( 'fields' => 'ids' ) ) );
- update_option( 'nav_menu_options', $nav_menu_option );
-
- wp_defer_term_counting(false);
-
- do_action( 'wp_update_nav_menu', $nav_menu_selected_id );
-
- $messages[] = '' . sprintf( __('The %s menu has been updated.'), $nav_menu_selected_title ) . '
';
- unset( $menu_items, $unsorted_menu_items );
+ $messages = array_merge( $messages, wp_nav_menu_update_menu_items( $nav_menu_selected_id, $nav_menu_selected_title ) );
}
}
break;
@@ -376,22 +344,35 @@ switch ( $action ) {
// Get all nav menus
$nav_menus = wp_get_nav_menus( array('orderby' => 'name') );
+$menu_count = count( $nav_menus );
+
+// Are we on the add new screen?
+$add_new_screen = ( isset( $_GET['menu'] ) && 0 == $_GET['menu'] ) ? true : false;
+
+// If we have one theme location, and zero menus, we take them right into editing their first menu
+$page_count = wp_count_posts( 'page' );
+$one_theme_location_no_menus = ( 1 == count( get_registered_nav_menus() ) && ! $add_new_screen && empty( $nav_menus ) && ! empty( $page_count->publish ) ) ? true : false;
+
+// Redirect to add screen if there are no menus and this users has either zero, or more than 1 theme locations
+if ( 0 == $menu_count && ! $add_new_screen && ! $one_theme_location_no_menus )
+ wp_redirect( admin_url( 'nav-menus.php?action=edit&menu=0' ) );
// Get recently edited nav menu
-$recently_edited = (int) get_user_option( 'nav_menu_recently_edited' );
-
-// If there was no recently edited menu, and $nav_menu_selected_id is a nav menu, update recently edited menu.
-if ( !$recently_edited && is_nav_menu( $nav_menu_selected_id ) ) {
+$recently_edited = absint( get_user_option( 'nav_menu_recently_edited' ) );
+if ( empty( $recently_edited ) && is_nav_menu( $nav_menu_selected_id ) )
$recently_edited = $nav_menu_selected_id;
-// Else if $nav_menu_selected_id is not a menu and not requesting that we create a new menu, but $recently_edited is a menu, grab that one.
-} elseif ( 0 == $nav_menu_selected_id && ! isset( $_REQUEST['menu'] ) && is_nav_menu( $recently_edited ) ) {
+// Use $recently_edited if none are selected
+if ( empty( $nav_menu_selected_id ) && ! isset( $_GET['menu'] ) && is_nav_menu( $recently_edited ) )
$nav_menu_selected_id = $recently_edited;
-// Else try to grab the first menu from the menus list
-} elseif ( 0 == $nav_menu_selected_id && ! isset( $_REQUEST['menu'] ) && ! empty($nav_menus) ) {
+// On deletion of menu, if another menu exists, show it
+if ( ! $add_new_screen && 0 < $menu_count && isset( $_GET['action'] ) && 'delete' == $_GET['action'] )
$nav_menu_selected_id = $nav_menus[0]->term_id;
-}
+
+// Set $nav_menu_selected_id to 0 if no menus
+if ( $one_theme_location_no_menus )
+ $nav_menu_selected_id = 0;
// Update the user's setting
if ( $nav_menu_selected_id != $recently_edited && is_nav_menu( $nav_menu_selected_id ) )
@@ -412,14 +393,22 @@ foreach( (array) $nav_menus as $key => $_nav_menu ) {
$nav_menus[$key]->truncated_name = $_nav_menu->truncated_name;
}
+// Retrieve menu locations
+if ( current_theme_supports( 'menus' ) ) {
+ $locations = get_registered_nav_menus();
+ $menu_locations = get_nav_menu_locations();
+}
+
// Ensure the user will be able to scroll horizontally
// by adding a class for the max menu depth.
global $_wp_nav_menu_max_depth;
$_wp_nav_menu_max_depth = 0;
// Calling wp_get_nav_menu_to_edit generates $_wp_nav_menu_max_depth
-if ( is_nav_menu( $nav_menu_selected_id ) )
+if ( is_nav_menu( $nav_menu_selected_id ) ) {
+ $menu_items = wp_get_nav_menu_items( $nav_menu_selected_id, array( 'post_status' => 'any' ) );
$edit_markup = wp_get_nav_menu_to_edit( $nav_menu_selected_id );
+}
function wp_nav_menu_max_depth($classes) {
global $_wp_nav_menu_max_depth;
@@ -460,14 +449,51 @@ require_once( './admin-header.php' );
?>
-
+