<!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" /><style type="text/css"><!--
#msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer { 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 #fc0 solid; padding: 6px; }
#msg ul, pre { overflow: auto; }
#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>
<title>[15008] trunk: New pending menu item behavior.</title>
</head>
<body>

<div id="msg">
<dl>
<dt>Revision</dt> <dd><a href="http://trac.wordpress.org/changeset/15008">15008</a></dd>
<dt>Author</dt> <dd>nacin</dd>
<dt>Date</dt> <dd>2010-05-27 22:22:09 +0000 (Thu, 27 May 2010)</dd>
</dl>

<h3>Log Message</h3>
<pre>New pending menu item behavior. props filosofo, see <a href="http://trac.wordpress.org/ticket/13579">#13579</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkwpadminadminajaxphp">trunk/wp-admin/admin-ajax.php</a></li>
<li><a href="#trunkwpadminincludesnavmenuphp">trunk/wp-admin/includes/nav-menu.php</a></li>
<li><a href="#trunkwpadminnavmenusphp">trunk/wp-admin/nav-menus.php</a></li>
<li><a href="#trunkwpincludesnavmenuphp">trunk/wp-includes/nav-menu.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkwpadminadminajaxphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/admin-ajax.php (15007 => 15008)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/admin-ajax.php        2010-05-27 22:17:58 UTC (rev 15007)
+++ trunk/wp-admin/admin-ajax.php        2010-05-27 22:22:09 UTC (rev 15008)
</span><span class="lines">@@ -816,14 +816,9 @@
</span><span class="cx"> 
</span><span class="cx">         require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
</span><span class="cx"> 
</span><del>-        $menu_id = (int) $_POST['menu'];
-        if ( isset( $_POST['menu-item'] ) ) {
-                $item_ids = wp_save_nav_menu_items( $menu_id, $_POST['menu-item'] );
-                if ( is_wp_error( $item_ids ) )
-                        die('-1');
-        } else {
-                $item_ids = array();
-        }
</del><ins>+        $item_ids = wp_save_nav_menu_items( 0, $_POST['menu-item'] );
+        if ( is_wp_error( $item_ids ) )
+                die('-1');
</ins><span class="cx"> 
</span><span class="cx">         foreach ( (array) $item_ids as $menu_item_id ) {
</span><span class="cx">                 $menu_obj = get_post( $menu_item_id );
</span></span></pre></div>
<a id="trunkwpadminincludesnavmenuphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/includes/nav-menu.php (15007 => 15008)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/includes/nav-menu.php        2010-05-27 22:17:58 UTC (rev 15007)
+++ trunk/wp-admin/includes/nav-menu.php        2010-05-27 22:22:09 UTC (rev 15008)
</span><span class="lines">@@ -58,11 +58,26 @@
</span><span class="cx">                         $original_object = get_post( $item-&gt;object_id );
</span><span class="cx">                         $original_title = $original_object-&gt;post_title;
</span><span class="cx">                 }
</span><ins>+
+                $classes = array(
+                        'menu-item menu-item-depth-' . $depth,
+                        'menu-item-' . esc_attr( $item-&gt;object ),
+                        'menu-item-edit-' . ( ( isset( $_GET['edit-menu-item'] ) &amp;&amp; $item_id == $_GET['edit-menu-item'] ) ? 'active' : 'inactive'),
+                );
+
+                $title = $item-&gt;title;
+
+                if ( isset( $item-&gt;post_status ) &amp;&amp; 'draft' == $item-&gt;post_status ) {
+                        $classes[] = 'pending';
+                        /* translators: %s: title of menu item in draft status */
+                        $title = sprintf( __('%s (Pending)'), $item-&gt;title );
+                }
+
</ins><span class="cx">                 ?&gt;
</span><del>-                &lt;li id=&quot;menu-item-&lt;?php echo $item_id; ?&gt;&quot; class=&quot;menu-item menu-item-depth-&lt;?php echo $depth; ?&gt; menu-item-&lt;?php echo esc_attr( $item-&gt;object ); ?&gt; menu-item-edit-&lt;?php echo ( isset( $_GET['edit-menu-item'] ) &amp;&amp; $item_id == $_GET['edit-menu-item'] ) ? 'active' : 'inactive'; ?&gt;&quot;&gt;
</del><ins>+                &lt;li id=&quot;menu-item-&lt;?php echo $item_id; ?&gt;&quot; class=&quot;&lt;?php echo implode(' ', $classes ); ?&gt;&quot;&gt;
</ins><span class="cx">                         &lt;dl class=&quot;menu-item-bar&quot;&gt;
</span><span class="cx">                                 &lt;dt class=&quot;menu-item-handle&quot;&gt;
</span><del>-                                        &lt;span class=&quot;item-title&quot;&gt;&lt;?php echo esc_html( $item-&gt;title ); ?&gt;&lt;/span&gt;
</del><ins>+                                        &lt;span class=&quot;item-title&quot;&gt;&lt;?php echo esc_html( $title ); ?&gt;&lt;/span&gt;
</ins><span class="cx">                                         &lt;span class=&quot;item-controls&quot;&gt;
</span><span class="cx">                                                 &lt;span class=&quot;item-type&quot;&gt;&lt;?php echo esc_html( $item-&gt;type_label ); ?&gt;&lt;/span&gt;
</span><span class="cx">                                                 &lt;span class=&quot;item-order&quot;&gt;
</span><span class="lines">@@ -865,7 +880,7 @@
</span><span class="cx">  *
</span><span class="cx">  * @since 3.0.0
</span><span class="cx">  *
</span><del>- * @param int $menu_id The menu ID for which to save this item.
</del><ins>+ * @param int $menu_id The menu ID for which to save this item. $menu_id of 0 makes a draft, orphaned menu item.
</ins><span class="cx">  * @param array $menu_data The unsanitized posted menu item data.
</span><span class="cx">  * @return array The database IDs of the items saved
</span><span class="cx">  */
</span><span class="lines">@@ -873,7 +888,7 @@
</span><span class="cx">         $menu_id = (int) $menu_id;
</span><span class="cx">         $items_saved = array();
</span><span class="cx"> 
</span><del>-        if ( is_nav_menu( $menu_id ) ) {
</del><ins>+        if ( 0 == $menu_id || is_nav_menu( $menu_id ) ) {
</ins><span class="cx"> 
</span><span class="cx">                 // Loop through all the menu items' POST values
</span><span class="cx">                 foreach( (array) $menu_data as $_possible_db_id =&gt; $_item_object_data ) {
</span><span class="lines">@@ -882,7 +897,7 @@
</span><span class="cx">                                 (
</span><span class="cx">                                         ! isset( $_item_object_data['menu-item-type'] ) || // and item type either isn't set
</span><span class="cx">                                         in_array( $_item_object_data['menu-item-url'], array( 'http://', '' ) ) || // or URL is the default
</span><del>-                                        'custom' != $_item_object_data['menu-item-type'] ||  // or it's not a custom menu item
</del><ins>+                                        ! ( 'custom' == $_item_object_data['menu-item-type'] &amp;&amp; ! isset( $_item_object_data['menu-item-db-id'] ) ) ||  // or it's not a custom menu item (but not the custom home page)
</ins><span class="cx">                                         ! empty( $_item_object_data['menu-item-db-id'] ) // or it *is* a custom menu item that already exists
</span><span class="cx">                                 )
</span><span class="cx">                         ) {
</span><span class="lines">@@ -998,6 +1013,15 @@
</span><span class="cx">                 else
</span><span class="cx">                         return new WP_Error( 'menu_walker_not_exist', sprintf( __('The Walker class named &lt;strong&gt;%s&lt;/strong&gt; does not exist.'), $walker_class_name ) );
</span><span class="cx"> 
</span><ins>+                $some_pending_menu_items = false;
+                foreach( (array) $menu_items as $menu_item ) {
+                        if ( isset( $menu_item-&gt;post_status ) &amp;&amp; 'draft' == $menu_item-&gt;post_status )
+                                $some_pending_menu_items = true;
+                }
+
+                if ( $some_pending_menu_items )
+                        $result .= '&lt;div class=&quot;updated inline&quot;&gt;&lt;p&gt;' . __('Click &lt;em&gt;Save Menu&lt;/em&gt; to make pending menu items public.') . '&lt;/p&gt;&lt;/div&gt;';
+
</ins><span class="cx">                 $result .= walk_nav_menu_tree( array_map('wp_setup_nav_menu_item', $menu_items), 0, (object) array('walker' =&gt; $walker ) );
</span><span class="cx">                 return $result;
</span><span class="cx">         } elseif ( is_wp_error( $menu ) ) {
</span><span class="lines">@@ -1026,4 +1050,24 @@
</span><span class="cx">         );
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+/** 
+ * Deletes orphaned draft menu items
+ *
+ * @access private
+ * @since 3.0.0
+ *
+ */
+function _wp_delete_orphaned_draft_menu_items() {
+        global $wpdb;
+        $delete_timestamp = time() - (60*60*24*EMPTY_TRASH_DAYS);
+
+        // delete orphaned draft menu items
+        $menu_items_to_delete = $wpdb-&gt;get_col($wpdb-&gt;prepare(&quot;SELECT ID FROM $wpdb-&gt;posts AS p LEFT JOIN $wpdb-&gt;postmeta AS m ON p.ID = m.post_id WHERE post_type = 'nav_menu_item' AND post_status = 'draft' AND meta_key = '_menu_item_orphaned' AND meta_value &lt; '%d'&quot;, $delete_timestamp ) );
+
+        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');
+
</ins><span class="cx"> ?&gt;
</span></span></pre></div>
<a id="trunkwpadminnavmenusphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-admin/nav-menus.php (15007 => 15008)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-admin/nav-menus.php        2010-05-27 22:17:58 UTC (rev 15007)
+++ trunk/wp-admin/nav-menus.php        2010-05-27 22:22:09 UTC (rev 15008)
</span><span class="lines">@@ -543,15 +543,15 @@
</span><span class="cx">                                                                 &lt;br class=&quot;clear&quot; /&gt;
</span><span class="cx">                                                                 &lt;div class=&quot;publishing-action&quot;&gt;
</span><span class="cx">                                                                         &lt;input class=&quot;button-primary menu-save&quot; name=&quot;save_menu&quot; type=&quot;submit&quot; value=&quot;&lt;?php empty($nav_menu_selected_id) ? esc_attr_e('Create Menu') : esc_attr_e('Save Menu'); ?&gt;&quot; /&gt;
</span><del>-                                                                &lt;/div&gt;&lt;!--END .publishing-action--&gt;
</del><ins>+                                                                &lt;/div&gt;&lt;!-- END .publishing-action --&gt;
</ins><span class="cx"> 
</span><span class="cx">                                                                 &lt;?php if ( ! empty( $nav_menu_selected_id ) ) : ?&gt;
</span><span class="cx">                                                                 &lt;div class=&quot;delete-action&quot;&gt;
</span><span class="cx">                                                                         &lt;a class=&quot;submitdelete deletion menu-delete&quot; href=&quot;&lt;?php echo esc_url( wp_nonce_url( admin_url('nav-menus.php?action=delete&amp;amp;menu=' . $nav_menu_selected_id), 'delete-nav_menu-' . $nav_menu_selected_id ) ); ?&gt;&quot;&gt;&lt;?php _e('Delete Menu'); ?&gt;&lt;/a&gt;
</span><del>-                                                                &lt;/div&gt;&lt;!--END .delete-action--&gt;
</del><ins>+                                                                &lt;/div&gt;&lt;!-- END .delete-action --&gt;
</ins><span class="cx">                                                                 &lt;?php endif; ?&gt;
</span><del>-                                                        &lt;/div&gt;&lt;!--END .major-publishing-actions--&gt;
-                                                &lt;/div&gt;&lt;!--END #submitpost .submitbox--&gt;
</del><ins>+                                                        &lt;/div&gt;&lt;!-- END .major-publishing-actions --&gt;
+                                                &lt;/div&gt;&lt;!-- END #submitpost .submitbox --&gt;
</ins><span class="cx">                                                 &lt;?php
</span><span class="cx">                                                 wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
</span><span class="cx">                                                 wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
</span><span class="lines">@@ -559,7 +559,7 @@
</span><span class="cx">                                                 ?&gt;
</span><span class="cx">                                                 &lt;input type=&quot;hidden&quot; name=&quot;action&quot; value=&quot;update&quot; /&gt;
</span><span class="cx">                                                 &lt;input type=&quot;hidden&quot; name=&quot;menu&quot; id=&quot;menu&quot; value=&quot;&lt;?php echo esc_attr( $nav_menu_selected_id ); ?&gt;&quot; /&gt;
</span><del>-                                        &lt;/div&gt;&lt;!--END #nav-menu-header--&gt;
</del><ins>+                                        &lt;/div&gt;&lt;!-- END #nav-menu-header --&gt;
</ins><span class="cx">                                         &lt;div id=&quot;post-body&quot;&gt;
</span><span class="cx">                                                 &lt;div id=&quot;post-body-content&quot;&gt;
</span><span class="cx">                                                         &lt;?php if ( is_nav_menu( $nav_menu_selected_id ) ) : ?&gt;
</span><span class="lines">@@ -580,13 +580,13 @@
</span><span class="cx">                                                                 echo '&lt;p&gt;' . sprintf( __('For more information on this feature, see the &lt;a href=&quot;%s&quot;&gt;Custom Menus&lt;/a&gt; article in the Codex.'), _x('http://codex.wordpress.org/Custom_Menus', 'Custom Menus codex page') ) . '&lt;/p&gt;';
</span><span class="cx">                                                                 echo '&lt;/div&gt;';
</span><span class="cx">                                                         endif; ?&gt;
</span><del>-                                                &lt;/div&gt;&lt;!-- /#post-body-content--&gt;
-                                        &lt;/div&gt;&lt;!--- /#post-body --&gt;
-                                &lt;/form&gt;&lt;!--/#update-nav-menu--&gt;
</del><ins>+                                                &lt;/div&gt;&lt;!-- /#post-body-content --&gt;
+                                        &lt;/div&gt;&lt;!-- /#post-body --&gt;
+                                &lt;/form&gt;&lt;!-- /#update-nav-menu --&gt;
</ins><span class="cx">                         &lt;/div&gt;&lt;!-- /.menu-edit --&gt;
</span><span class="cx">                 &lt;/div&gt;&lt;!-- /#menu-management --&gt;
</span><span class="cx">         &lt;/div&gt;&lt;!-- /#menu-management-liquid --&gt;
</span><del>-        &lt;/div&gt;&lt;!-- /#nav-menus-frame--&gt;
</del><ins>+        &lt;/div&gt;&lt;!-- /#nav-menus-frame --&gt;
</ins><span class="cx"> &lt;/div&gt;&lt;!-- /.wrap--&gt;
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkwpincludesnavmenuphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/nav-menu.php (15007 => 15008)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/nav-menu.php        2010-05-27 22:17:58 UTC (rev 15007)
+++ trunk/wp-includes/nav-menu.php        2010-05-27 22:22:09 UTC (rev 15008)
</span><span class="lines">@@ -247,7 +247,7 @@
</span><span class="cx">  *
</span><span class="cx">  * @since 3.0.0
</span><span class="cx">  *
</span><del>- * @param int $menu_id The ID of the menu. Required.
</del><ins>+ * @param int $menu_id The ID of the menu. Required. If &quot;0&quot;, makes the menu item a draft orphan.
</ins><span class="cx">  * @param int $menu_item_db_id The ID of the menu item. If &quot;0&quot;, creates a new menu item.
</span><span class="cx">  * @param array $menu_item_data The menu item's data.
</span><span class="cx">  * @return int The menu item's database ID or WP_Error object on failure.
</span><span class="lines">@@ -257,17 +257,15 @@
</span><span class="cx">         $menu_item_db_id = (int) $menu_item_db_id;
</span><span class="cx"> 
</span><span class="cx">         // make sure that we don't convert non-nav_menu_item objects into nav_menu_item objects
</span><del>-        if ( ! empty( $menu_item_db_id ) &amp;&amp; ! is_nav_menu_item( $menu_item_db_id ) ) {
</del><ins>+        if ( ! empty( $menu_item_db_id ) &amp;&amp; ! is_nav_menu_item( $menu_item_db_id ) )
</ins><span class="cx">                 return new WP_Error('update_nav_menu_item_failed', __('The given object ID is not that of a menu item.'));
</span><del>-        }
</del><span class="cx"> 
</span><span class="cx">         $menu = wp_get_nav_menu_object( $menu_id );
</span><span class="cx"> 
</span><del>-        if ( ! $menu || is_wp_error( $menu ) ) {
</del><ins>+        if ( ( ! $menu &amp;&amp; 0 !== $menu_id ) || is_wp_error( $menu ) )
</ins><span class="cx">                 return $menu;
</span><del>-        }
</del><span class="cx"> 
</span><del>-        $menu_items = (array) wp_get_nav_menu_items( $menu_id, array( 'post_status' =&gt; 'publish,draft' ) );
</del><ins>+        $menu_items = 0 == $menu_id ? array() : (array) wp_get_nav_menu_items( $menu_id, array( 'post_status' =&gt; 'publish,draft' ) );
</ins><span class="cx"> 
</span><span class="cx">         $count = count( $menu_items );
</span><span class="cx"> 
</span><span class="lines">@@ -289,8 +287,10 @@
</span><span class="cx">         );
</span><span class="cx"> 
</span><span class="cx">         $args = wp_parse_args( $menu_item_data, $defaults );
</span><del>-
-        if ( 0 == (int) $args['menu-item-position'] ) {
</del><ins>+        
+        if ( 0 == $menu_id ) {
+                $args['menu-item-position'] = 1;
+        } elseif ( 0 == (int) $args['menu-item-position'] ) {
</ins><span class="cx">                 $last_item = array_pop( $menu_items );
</span><span class="cx">                 $args['menu-item-position'] = ( $last_item &amp;&amp; isset( $last_item-&gt;menu_order ) ) ? 1 + $last_item-&gt;menu_order : $count;
</span><span class="cx">         }
</span><span class="lines">@@ -339,9 +339,11 @@
</span><span class="cx">                 'post_parent' =&gt; $original_parent,
</span><span class="cx">                 'post_title' =&gt; $args['menu-item-title'],
</span><span class="cx">                 'post_type' =&gt; 'nav_menu_item',
</span><del>-                'tax_input' =&gt; array( 'nav_menu' =&gt; array( intval( $menu-&gt;term_id ) ) ),
</del><span class="cx">         );
</span><span class="cx"> 
</span><ins>+        if ( 0 != $menu_id )
+                $post['tax_input'] = array( 'nav_menu' =&gt; array( intval( $menu-&gt;term_id ) ) );
+
</ins><span class="cx">         // New menu item. Default is draft status
</span><span class="cx">         if ( 0 == $menu_item_db_id ) {
</span><span class="cx">                 $post['ID'] = 0;
</span><span class="lines">@@ -374,9 +376,12 @@
</span><span class="cx">                 $args['menu-item-xfn'] = implode( ' ', array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-xfn'] ) ) );
</span><span class="cx">                 update_post_meta( $menu_item_db_id, '_menu_item_classes', $args['menu-item-classes'] );
</span><span class="cx">                 update_post_meta( $menu_item_db_id, '_menu_item_xfn', $args['menu-item-xfn'] );
</span><del>-
-                // @todo: only save custom link urls.
</del><span class="cx">                 update_post_meta( $menu_item_db_id, '_menu_item_url', esc_url_raw($args['menu-item-url']) );
</span><ins>+                
+                if ( 0 == $menu_id )
+                        update_post_meta( $menu_item_db_id, '_menu_item_orphaned', time() );
+                else
+                        delete_post_meta( $menu_item_db_id, '_menu_item_orphaned' );
</ins><span class="cx"> 
</span><span class="cx">                 do_action('wp_update_nav_menu_item', $menu_id, $menu_item_db_id, $args );
</span><span class="cx">         }
</span><span class="lines">@@ -740,6 +745,17 @@
</span><span class="cx">         }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+/**
+ * Automatically add newly published page objects to menus with that as an option.
+ *
+ * @since 3.0.0
+ * @access private
+ *
+ * @param string $new_status The new status of the post object.
+ * @param string $old_status The old status of the post object.
+ * @param object $post The post object being transitioned from one status to another.
+ * @return void
+ */
</ins><span class="cx"> function _wp_auto_add_pages_to_menu( $new_status, $old_status, $post ) {
</span><span class="cx">         if ( 'publish' != $new_status || 'publish' == $old_status || 'page' != $post-&gt;post_type )
</span><span class="cx">                 return;
</span></span></pre>
</div>
</div>

</body>
</html>