Admin Menu Editor - Version 1.1

Version Description

  • WordPress 3.1 compatibility.
  • Added the ability to drag & drop a menu item to a different menu.
  • Added a drop-down list of Dashboard pages to the "File" box.
  • When the menu editor is opened, the first top-level menu is now automatically selected and it's submenu displayed. Hopefully, this will make the UI slightly easier to understand for first-time users.
  • All corners rounded on the "expand" link when not expanded.
  • By popular request, the "Menu Editor" menu entry can be hidden again.
Download this release

Release Info

Developer whiteshadow
Plugin Icon 128x128 Admin Menu Editor
Version 1.1
Comparing to
See all releases

Code changes from version 1.0.1 to 1.1

css/menu-editor.css CHANGED
@@ -112,14 +112,9 @@
112
  background-repeat: no-repeat;
113
  background-position: center;
114
 
115
- border-top-right-radius: 3px;
116
- border-top-left-radius: 3px;
117
-
118
- -moz-border-radius-topright: 3px;
119
- -moz-border-radius-topleft: 3px;
120
-
121
- -webkit-border-top-right-radius: 3px;
122
- -webkit-border-top-left-radius: 3px;
123
  }
124
 
125
  a.ws_edit_link:hover {
@@ -135,6 +130,24 @@ a.ws_edit_link:hover {
135
  background-image: url('../images/bullet_arrow_down2.png');
136
  padding-bottom: 1px;
137
  background-position: center 3px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  }
139
 
140
  /****************************************
@@ -223,7 +236,7 @@ a.ws_edit_link:hover {
223
  margin-top: 0px;
224
  }
225
 
226
- /* Dropdown button for the capability field */
227
  #ws_menu_editor .ws_dropdown_button {
228
  display : block;
229
  float: left;
@@ -257,10 +270,10 @@ a.ws_edit_link:hover {
257
  }
258
 
259
  /*
260
- The capability field's appearance and size need to be changed
261
  to accomodate the dropdown button.
262
  */
263
- #ws_menu_editor .ws_edit_field-access_level input.ws_field_value {
264
  width: 230px;
265
  margin-right: 0;
266
  border-right: 0;
@@ -390,7 +403,7 @@ a.ws_button:hover {
390
  Capability selector
391
  *************************************/
392
 
393
- select#ws_cap_selector {
394
  width: 252px;
395
  height: 20em;
396
 
@@ -402,13 +415,13 @@ select#ws_cap_selector {
402
  font-size: 12px;
403
  }
404
 
405
- select#ws_cap_selector option {
406
  font-family : "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
407
  font-size: 12px;
408
  padding: 3px;
409
  }
410
 
411
- select#ws_cap_selector optgroup option {
412
  padding-left: 10px;
413
  }
414
 
112
  background-repeat: no-repeat;
113
  background-position: center;
114
 
115
+ border-radius: 3px;
116
+ -moz-border-radius: 3px;
117
+ -webkit-border-radius: 3px;
 
 
 
 
 
118
  }
119
 
120
  a.ws_edit_link:hover {
130
  background-image: url('../images/bullet_arrow_down2.png');
131
  padding-bottom: 1px;
132
  background-position: center 3px;
133
+
134
+ border-bottom-right-radius: 0;
135
+ border-bottom-left-radius: 0;
136
+
137
+ -moz-border-radius-bottomright: 0;
138
+ -moz-border-radius-bottomleft: 0;
139
+
140
+ -webkit-border-bottom-right-radius: 0;
141
+ -webkit-border-bottom-left-radius: 0;
142
+ }
143
+
144
+
145
+ .ws_menu_drop_hover {
146
+ background-color: #43b529 !important;
147
+ }
148
+
149
+ .ws_container.ui-sortable-helper * {
150
+ cursor: move !important;
151
  }
152
 
153
  /****************************************
236
  margin-top: 0px;
237
  }
238
 
239
+ /* Dropdown button for combo-box fields */
240
  #ws_menu_editor .ws_dropdown_button {
241
  display : block;
242
  float: left;
270
  }
271
 
272
  /*
273
+ The appearance and size of combobox fields need to be changed
274
  to accomodate the dropdown button.
275
  */
276
+ #ws_menu_editor .ws_has_dropdown input.ws_field_value {
277
  width: 230px;
278
  margin-right: 0;
279
  border-right: 0;
403
  Capability selector
404
  *************************************/
405
 
406
+ #wpbody select.ws_dropdown {
407
  width: 252px;
408
  height: 20em;
409
 
415
  font-size: 12px;
416
  }
417
 
418
+ #wpbody select.ws_dropdown option {
419
  font-family : "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
420
  font-size: 12px;
421
  padding: 3px;
422
  }
423
 
424
+ #wpbody select.ws_dropdown optgroup option {
425
  padding-left: 10px;
426
  }
427
 
includes/menu-editor-core.php CHANGED
@@ -134,13 +134,15 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
134
  wp_enqueue_script('jquery-json', $this->plugin_dir_url.'/js/jquery.json-1.3.js', array('jquery'), '1.3');
135
  //jQuery sort plugin
136
  wp_enqueue_script('jquery-sort', $this->plugin_dir_url.'/js/jquery.sort.js', array('jquery'));
 
 
137
 
138
  //Editor's scipts
139
  wp_enqueue_script(
140
  'menu-editor',
141
  $this->plugin_dir_url.'/js/menu-editor.js',
142
  array('jquery', 'jquery-ui-sortable', 'jquery-ui-dialog', 'jquery-form'),
143
- '1.0'
144
  );
145
  }
146
 
@@ -150,7 +152,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
150
  * @return void
151
  */
152
  function enqueue_styles(){
153
- wp_enqueue_style('menu-editor-style', $this->plugin_dir_url . '/css/menu-editor.css', array(), '1.0');
154
  }
155
 
156
  /**
@@ -244,6 +246,33 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
244
  * @return void
245
  */
246
  function hook_admin_xml_ns(){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  global $title;
248
  global $pagenow;
249
  global $plugin_page;
@@ -253,8 +282,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
253
  if ( isset($this->title_lookups[$file]) ){
254
  $title = esc_html( strip_tags( $this->title_lookups[$file] ) );
255
  }
256
- }
257
- }
 
 
 
258
 
259
  /**
260
  * Loop over the Dashboard submenus and remove pages for which the current user does not have privs.
@@ -762,39 +794,6 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
762
  return array($menu, $submenu, $title_lookup);
763
  }
764
 
765
- /**
766
- * Prevent the "Menu Editor" menu item from becoming inaccessible.
767
- *
768
- * @param array $item
769
- * @return array
770
- */
771
- function hook_custom_admin_submenu($item){
772
- //To stop the user from accidentally shooting themselves in the foot, we make
773
- //any dangerous changes made to the "Menu Editor" menu have no effect.
774
- if ( !empty($item['file']) && ($item['file'] == 'menu_editor') ){
775
- //Reset the important fields back to the default values.
776
- $item['access_level'] = null;
777
- $item['page_title'] = null;
778
- $item['window_title'] = null;
779
- $item = $this->apply_defaults($item);
780
- $item['hidden'] = false; //Can't hide me!
781
- }
782
- return $item;
783
- }
784
-
785
- /**
786
- * Prevent the menu that contains the "Menu Editor" entry from being hidden.
787
- *
788
- * @param array $menu
789
- * @return array
790
- */
791
- function hook_custom_admin_menu($menu){
792
- if ( !empty($menu['items']) && array_key_exists('menu_editor', $menu['items']) ){
793
- $menu['hidden'] = false;
794
- }
795
- return $menu;
796
- }
797
-
798
  /**
799
  * Upgrade a menu tree to the currently used structure
800
  * Does nothing if the menu is already up to date.
@@ -849,6 +848,15 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
849
  $url = remove_query_arg('noheader');
850
 
851
  if ($data){
 
 
 
 
 
 
 
 
 
852
  //Save the custom menu
853
  $this->options['custom_menu'] = $data;
854
  $this->save_options();
@@ -984,7 +992,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
984
 
985
  <?php
986
  //Createa a pop-up capability selector
987
- $capSelector = array('<select id="ws_cap_selector" size="10">');
988
 
989
  $capSelector[] = '<optgroup label="Roles">';
990
  foreach($all_roles as $role_id => $role_name){
@@ -1008,6 +1016,39 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1008
  $capSelector[] = '</select>';
1009
 
1010
  echo implode("\n", $capSelector);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1011
  ?>
1012
 
1013
  <span id="ws-ame-screen-meta-contents" style="display:none;">
134
  wp_enqueue_script('jquery-json', $this->plugin_dir_url.'/js/jquery.json-1.3.js', array('jquery'), '1.3');
135
  //jQuery sort plugin
136
  wp_enqueue_script('jquery-sort', $this->plugin_dir_url.'/js/jquery.sort.js', array('jquery'));
137
+ //jQuery UI Droppable
138
+ wp_enqueue_script('jquery-ui-droppable');
139
 
140
  //Editor's scipts
141
  wp_enqueue_script(
142
  'menu-editor',
143
  $this->plugin_dir_url.'/js/menu-editor.js',
144
  array('jquery', 'jquery-ui-sortable', 'jquery-ui-dialog', 'jquery-form'),
145
+ '1.1'
146
  );
147
  }
148
 
152
  * @return void
153
  */
154
  function enqueue_styles(){
155
+ wp_enqueue_style('menu-editor-style', $this->plugin_dir_url . '/css/menu-editor.css', array(), '1.1');
156
  }
157
 
158
  /**
246
  * @return void
247
  */
248
  function hook_admin_xml_ns(){
249
+ global $title;
250
+ if ( empty($title) ){
251
+ $title = $this->get_real_page_title();
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Fix the page title for move plugin pages.
257
+ * The 'admin_title' filter is only available in WP 3.1+
258
+ *
259
+ * @param string $admin_title The current admin title.
260
+ * @param string $title The current page title.
261
+ * @return string New admin title.
262
+ */
263
+ function hook_admin_title($admin_title, $title){
264
+ if ( empty($title) ){
265
+ $admin_title = $this->get_real_page_title() . $admin_title;
266
+ }
267
+ return $admin_title;
268
+ }
269
+
270
+ /**
271
+ * Get the correct page title for a plugin page that's been moved to a different menu.
272
+ *
273
+ * @return string
274
+ */
275
+ function get_real_page_title(){
276
  global $title;
277
  global $pagenow;
278
  global $plugin_page;
282
  if ( isset($this->title_lookups[$file]) ){
283
  $title = esc_html( strip_tags( $this->title_lookups[$file] ) );
284
  }
285
+ }
286
+
287
+ return $title;
288
+ }
289
+
290
 
291
  /**
292
  * Loop over the Dashboard submenus and remove pages for which the current user does not have privs.
794
  return array($menu, $submenu, $title_lookup);
795
  }
796
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
797
  /**
798
  * Upgrade a menu tree to the currently used structure
799
  * Does nothing if the menu is already up to date.
848
  $url = remove_query_arg('noheader');
849
 
850
  if ($data){
851
+ //Ensure the user doesn't change the required capability to something they themselves don't have.
852
+ if ( isset($data['options-general.php']['items']['menu_editor']) ){
853
+ $item = $data['options-general.php']['items']['menu_editor'];
854
+ if ( !empty($item['access_level']) && !current_user_can($item['access_level']) ){
855
+ $item['access_level'] = null;
856
+ $data['options-general.php']['items']['menu_editor'] = $item;
857
+ }
858
+ }
859
+
860
  //Save the custom menu
861
  $this->options['custom_menu'] = $data;
862
  $this->save_options();
992
 
993
  <?php
994
  //Createa a pop-up capability selector
995
+ $capSelector = array('<select id="ws_cap_selector" class="ws_dropdown" size="10">');
996
 
997
  $capSelector[] = '<optgroup label="Roles">';
998
  foreach($all_roles as $role_id => $role_name){
1016
  $capSelector[] = '</select>';
1017
 
1018
  echo implode("\n", $capSelector);
1019
+
1020
+ //Create a pop-up page selector
1021
+ $pageSelector = array('<select id="ws_page_selector" class="ws_dropdown" size="10">');
1022
+ foreach($default_menu as $toplevel){
1023
+ if ( $toplevel['separator'] ) continue;
1024
+
1025
+ $top_title = strip_tags( preg_replace('@<span[^>]*>.*</span>@i', '', $this->get_menu_field($toplevel, 'menu_title')) );
1026
+
1027
+ if ( empty($toplevel['items'])) {
1028
+ //This menu has no items, so it can only link to itself
1029
+ $pageSelector[] = sprintf(
1030
+ '<option value="%s">%s -&gt; %s</option>',
1031
+ esc_attr($this->get_menu_field($toplevel, 'file')),
1032
+ $top_title,
1033
+ $top_title
1034
+ );
1035
+ } else {
1036
+ //When a menu has some items, it's own URL is ignored by WP and the first item is used instead.
1037
+ foreach($toplevel['items'] as $subitem){
1038
+ $sub_title = strip_tags( preg_replace('@<span[^>]*>.*</span>@i', '', $this->get_menu_field($subitem, 'menu_title')) );
1039
+
1040
+ $pageSelector[] = sprintf(
1041
+ '<option value="%s">%s -&gt; %s</option>',
1042
+ esc_attr($this->get_menu_field($subitem, 'file')),
1043
+ $top_title,
1044
+ $sub_title
1045
+ );
1046
+ }
1047
+ }
1048
+ }
1049
+
1050
+ $pageSelector[] = '</select>';
1051
+ echo implode("\n", $pageSelector);
1052
  ?>
1053
 
1054
  <span id="ws-ame-screen-meta-contents" style="display:none;">
js/menu-editor.js CHANGED
@@ -35,6 +35,9 @@ function outputWpMenu(menu){
35
  outputTopMenu(menu[filename]);
36
  i++;
37
  }
 
 
 
38
  }
39
 
40
  /*
@@ -115,6 +118,25 @@ function buildTopMenu(menu){
115
  addMenuFlag(menu_obj, 'custom_item');
116
  }
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  return menu_obj;
119
  }
120
 
@@ -216,6 +238,7 @@ var knownMenuFields = {
216
  standardCaption : true,
217
  type : 'text',
218
  defaultValue: '',
 
219
  visible: true
220
  },
221
  'page_title' : {
@@ -350,14 +373,28 @@ function buildEditboxField(entry, field_name, field_settings){
350
  if(entry[field_name]==null){
351
  className += ' ws_input_default';
352
  }
 
 
 
 
 
353
 
354
  var editField = $('<div>' + (field_settings.standardCaption?(field_settings.caption+'<br>'):'') + '</div>')
355
  .attr('class', className)
356
  .append(inputBox);
357
 
358
- if ( (typeof(field_settings['addDropdown']) != 'undefined') && field_settings.addDropdown ){
359
  //Add a dropdown button
360
- editField.append('<input type="button" class="button ws_dropdown_button" value="&#9660;" tabindex="-1">');
 
 
 
 
 
 
 
 
 
361
  }
362
 
363
  editField
@@ -625,9 +662,6 @@ $(document).ready(function(){
625
  knownMenuFields['open_in'].visible = true;
626
  };
627
 
628
- //Show the menu
629
- outputWpMenu(customMenu);
630
-
631
  //Make the top menu box sortable (we only need to do this once)
632
  var mainMenuBox = $('#ws_menu_box');
633
  makeBoxSortable(mainMenuBox);
@@ -740,6 +774,7 @@ $(document).ready(function(){
740
  $('#ws_menu_editor .ws_field_value').live('click', fieldValueChange);
741
  //jQuery 1.3.x can't catch 'change' events with live(),
742
  //so we handle that by using event delegation instead.
 
743
  $('#ws_menu_editor').change(function(event){
744
  var target = $(event.target);
745
  if ( target.is('.ws_field_value') ){
@@ -764,120 +799,141 @@ $(document).ready(function(){
764
  });
765
 
766
 
767
- //Event handlers for the capability dropdown
768
- var capList = $('#ws_cap_selector');
769
- var currentCapListOwner = null;
770
- var timeoutForgetOwner = 0;
771
-
 
 
 
 
 
 
 
 
 
 
 
 
772
  //Show/hide the capability dropdown list when the button is clicked
773
  $('#ws_menu_editor input.ws_dropdown_button').live('click',function(event){
774
- var list = capList;
775
-
776
  var button = $(this);
777
  var inputBox = button.parent().find('input.ws_field_value');
778
 
779
- clearTimeout(timeoutForgetOwner);
780
- timeoutForgetOwner = 0;
 
 
781
 
782
  //If we already own the list, hide it and rescind ownership.
783
- if ( currentCapListOwner == this ){
784
- list.hide();
785
 
786
- currentCapListOwner = null;
787
  inputBox.focus();
788
 
789
  return;
790
  }
791
- currentCapListOwner = this; //Got ye now!
792
 
793
  //Move the dropdown near to the button
794
  var inputPos = inputBox.offset();
795
- list.css({
796
  position: 'absolute',
797
  left: inputPos.left,
798
  top: inputPos.top + inputBox.outerHeight()
799
  });
800
 
801
  //Pre-select the current capability (will clear selection if there's no match)
802
- list.val(inputBox.val());
803
 
804
- list.show();
805
- list.focus();
806
  });
807
 
808
  //Also show it when the user presses the down arrow in the input field
809
- $('#ws_menu_editor .ws_edit_field-access_level input.ws_field_value').live('keyup', function(event){
810
  if ( event.which == 40 ){
811
  $(this).parent().find('input.ws_dropdown_button').click();
812
  }
813
  });
814
 
 
 
 
815
  //Hide capability dropdown when it loses focus
816
- capList.blur(function(event){
817
- capList.hide();
 
 
818
  /*
819
  * Hackiness : make sure the list doesn't disappear & immediately reappear
820
  * when the event that caused it to lose focus was the user clicking on the
821
  * dropdown button.
822
  */
823
- timeoutForgetOwner = setTimeout(
824
  (function(){
825
- currentCapListOwner = null;
826
  }),
827
  200
828
  );
829
  });
830
 
831
- capList.keydown(function(event){
 
 
832
  //Also hide it when the user presses Esc
833
  if ( event.which == 27 ){
834
- var inputBox = $(currentCapListOwner).parent().find('input.ws_field_value');
835
 
836
- capList.hide();
837
- if ( currentCapListOwner ){
838
- $(currentCapListOwner).parent().find('input.ws_field_value').focus();
839
  }
840
- currentCapListOwner = null;
841
 
842
  //Select an item & hide the list when the user presses Enter or Tab
843
  } else if ( (event.which == 13) || (event.which == 9) ){
844
- capList.hide();
845
 
846
- var inputBox = $(currentCapListOwner).parent().find('input.ws_field_value');
847
- if ( capList.val() ){
848
- inputBox.val(capList.val());
849
  inputBox.change();
850
  }
851
 
852
  inputBox.focus();
853
- currentCapListOwner = null;
854
 
855
  event.preventDefault();
856
  }
857
  });
858
 
859
  //Eat Tab keys to prevent focus theft. Required to make the "select item on Tab" thing work.
860
- capList.keyup(function(event){
861
  if ( event.which == 9 ){
862
  event.preventDefault();
863
  }
864
  })
865
 
866
 
867
- //Update the input & hide the list when an option is clicked
868
- capList.click(function(){
869
- if ( !currentCapListOwner || !capList.val() ){
 
 
870
  return;
871
  }
872
- capList.hide();
873
 
874
- var inputBox = $(currentCapListOwner).parent().find('input.ws_field_value');
875
- inputBox.val(capList.val()).change().focus();
876
- currentCapListOwner = null;
877
  });
878
 
879
  //Highlight an option when the user mouses over it (doesn't work in IE)
880
- capList.mousemove(function(event){
881
  if ( !event.target ){
882
  return;
883
  }
@@ -1351,6 +1407,9 @@ $(document).ready(function(){
1351
  }
1352
  });
1353
 
 
 
 
1354
  });
1355
 
1356
  })(jQuery);
35
  outputTopMenu(menu[filename]);
36
  i++;
37
  }
38
+
39
+ //Automatically select the first top-level menu
40
+ $('#ws_menu_box .ws_menu:first').click();
41
  }
42
 
43
  /*
118
  addMenuFlag(menu_obj, 'custom_item');
119
  }
120
 
121
+ if ( !menu.separator ){
122
+ //Allow the user to drag menu items to top-level menus
123
+ menu_obj.droppable({
124
+ 'hoverClass' : 'ws_menu_drop_hover',
125
+
126
+ 'accept' : (function(thing){
127
+ return thing.hasClass('ws_item');
128
+ }),
129
+
130
+ 'drop' : (function(event, ui){
131
+ var droppedItemData = readItemState(ui.draggable);
132
+ var new_item = buildMenuItem(droppedItemData);
133
+ var submenu = $('#' + menu_obj.data('submenu_id'));
134
+ submenu.append(new_item);
135
+ ui.draggable.remove();
136
+ })
137
+ });
138
+ }
139
+
140
  return menu_obj;
141
  }
142
 
238
  standardCaption : true,
239
  type : 'text',
240
  defaultValue: '',
241
+ addDropdown : 'ws_page_selector',
242
  visible: true
243
  },
244
  'page_title' : {
373
  if(entry[field_name]==null){
374
  className += ' ws_input_default';
375
  }
376
+
377
+ var hasDropdown = (typeof(field_settings['addDropdown']) != 'undefined') && field_settings.addDropdown;
378
+ if ( hasDropdown ){
379
+ className += ' ws_has_dropdown';
380
+ }
381
 
382
  var editField = $('<div>' + (field_settings.standardCaption?(field_settings.caption+'<br>'):'') + '</div>')
383
  .attr('class', className)
384
  .append(inputBox);
385
 
386
+ if ( hasDropdown ){
387
  //Add a dropdown button
388
+ var dropdownId = 'ws_cap_selector';
389
+ if ( typeof(field_settings.addDropdown) == 'string' ){
390
+ dropdownId = field_settings.addDropdown;
391
+ }
392
+ editField.append(
393
+ $('<input type="button" value="&#9660;">')
394
+ .addClass('button ws_dropdown_button')
395
+ .attr('tabindex', '-1')
396
+ .data('dropdownId', dropdownId)
397
+ );
398
  }
399
 
400
  editField
662
  knownMenuFields['open_in'].visible = true;
663
  };
664
 
 
 
 
665
  //Make the top menu box sortable (we only need to do this once)
666
  var mainMenuBox = $('#ws_menu_box');
667
  makeBoxSortable(mainMenuBox);
774
  $('#ws_menu_editor .ws_field_value').live('click', fieldValueChange);
775
  //jQuery 1.3.x can't catch 'change' events with live(),
776
  //so we handle that by using event delegation instead.
777
+ //TODO: Update for jQuery 1.4.2 in WP 3.0
778
  $('#ws_menu_editor').change(function(event){
779
  var target = $(event.target);
780
  if ( target.is('.ws_field_value') ){
799
  });
800
 
801
 
802
+ /***************************************************************************
803
+ Dropdown list for combobox fields
804
+ ***************************************************************************/
805
+
806
+ var availableDropdowns = {
807
+ 'ws_cap_selector' : {
808
+ list : $('#ws_cap_selector'),
809
+ currentOwner : null,
810
+ timeoutForgetOwner : 0
811
+ },
812
+ 'ws_page_selector' : {
813
+ list : $('#ws_page_selector'),
814
+ currentOwner : null,
815
+ timeoutForgetOwner : 0
816
+ }
817
+ }
818
+
819
  //Show/hide the capability dropdown list when the button is clicked
820
  $('#ws_menu_editor input.ws_dropdown_button').live('click',function(event){
 
 
821
  var button = $(this);
822
  var inputBox = button.parent().find('input.ws_field_value');
823
 
824
+ var dropdown = availableDropdowns[button.data('dropdownId')];
825
+
826
+ clearTimeout(dropdown.timeoutForgetOwner);
827
+ dropdown.timeoutForgetOwner = 0;
828
 
829
  //If we already own the list, hide it and rescind ownership.
830
+ if ( dropdown.currentOwner == this ){
831
+ dropdown.list.hide();
832
 
833
+ dropdown.currentOwner = null;
834
  inputBox.focus();
835
 
836
  return;
837
  }
838
+ dropdown.currentOwner = this; //Got ye now!
839
 
840
  //Move the dropdown near to the button
841
  var inputPos = inputBox.offset();
842
+ dropdown.list.css({
843
  position: 'absolute',
844
  left: inputPos.left,
845
  top: inputPos.top + inputBox.outerHeight()
846
  });
847
 
848
  //Pre-select the current capability (will clear selection if there's no match)
849
+ dropdown.list.val(inputBox.val());
850
 
851
+ dropdown.list.show();
852
+ dropdown.list.focus();
853
  });
854
 
855
  //Also show it when the user presses the down arrow in the input field
856
+ $('#ws_menu_editor .ws_has_dropdown input.ws_field_value').live('keyup', function(event){
857
  if ( event.which == 40 ){
858
  $(this).parent().find('input.ws_dropdown_button').click();
859
  }
860
  });
861
 
862
+ //Event handlers for the dropdowns themselves
863
+ var dropdownNodes = $('.ws_dropdown');
864
+
865
  //Hide capability dropdown when it loses focus
866
+ dropdownNodes.blur(function(event){
867
+ var dropdown = availableDropdowns[$(this).attr('id')];
868
+
869
+ dropdown.list.hide();
870
  /*
871
  * Hackiness : make sure the list doesn't disappear & immediately reappear
872
  * when the event that caused it to lose focus was the user clicking on the
873
  * dropdown button.
874
  */
875
+ dropdown.timeoutForgetOwner = setTimeout(
876
  (function(){
877
+ dropdown.currentOwner = null;
878
  }),
879
  200
880
  );
881
  });
882
 
883
+ dropdownNodes.keydown(function(event){
884
+ var dropdown = availableDropdowns[$(this).attr('id')];
885
+
886
  //Also hide it when the user presses Esc
887
  if ( event.which == 27 ){
888
+ var inputBox = $(dropdown.currentOwner).parent().find('input.ws_field_value');
889
 
890
+ dropdown.list.hide();
891
+ if ( dropdown.currentOwner ){
892
+ $(dropdown.currentOwner).parent().find('input.ws_field_value').focus();
893
  }
894
+ dropdown.currentOwner = null;
895
 
896
  //Select an item & hide the list when the user presses Enter or Tab
897
  } else if ( (event.which == 13) || (event.which == 9) ){
898
+ dropdown.list.hide();
899
 
900
+ var inputBox = $(dropdown.currentOwner).parent().find('input.ws_field_value');
901
+ if ( dropdown.list.val() ){
902
+ inputBox.val(dropdown.list.val());
903
  inputBox.change();
904
  }
905
 
906
  inputBox.focus();
907
+ dropdown.currentOwner = null;
908
 
909
  event.preventDefault();
910
  }
911
  });
912
 
913
  //Eat Tab keys to prevent focus theft. Required to make the "select item on Tab" thing work.
914
+ dropdownNodes.keyup(function(event){
915
  if ( event.which == 9 ){
916
  event.preventDefault();
917
  }
918
  })
919
 
920
 
921
+ //Update the input & hide the list when an option is clicked
922
+ dropdownNodes.click(function(){
923
+ var dropdown = availableDropdowns[$(this).attr('id')];
924
+
925
+ if ( !dropdown.currentOwner || !dropdown.list.val() ){
926
  return;
927
  }
928
+ dropdown.list.hide();
929
 
930
+ var inputBox = $(dropdown.currentOwner).parent().find('input.ws_field_value');
931
+ inputBox.val(dropdown.list.val()).change().focus();
932
+ dropdown.currentOwner = null;
933
  });
934
 
935
  //Highlight an option when the user mouses over it (doesn't work in IE)
936
+ dropdownNodes.mousemove(function(event){
937
  if ( !event.target ){
938
  return;
939
  }
1407
  }
1408
  });
1409
 
1410
+
1411
+ //Finally, show the menu
1412
+ outputWpMenu(customMenu);
1413
  });
1414
 
1415
  })(jQuery);
menu-editor.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Admin Menu Editor
4
  Plugin URI: http://w-shadow.com/blog/2008/12/20/admin-menu-editor-for-wordpress/
5
  Description: Lets you directly edit the WordPress admin menu. You can re-order, hide or rename existing menus, add custom menus and more.
6
- Version: 1.0.1
7
  Author: Janis Elsts
8
  Author URI: http://w-shadow.com/blog/
9
  */
3
  Plugin Name: Admin Menu Editor
4
  Plugin URI: http://w-shadow.com/blog/2008/12/20/admin-menu-editor-for-wordpress/
5
  Description: Lets you directly edit the WordPress admin menu. You can re-order, hide or rename existing menus, add custom menus and more.
6
+ Version: 1.1
7
  Author: Janis Elsts
8
  Author URI: http://w-shadow.com/blog/
9
  */
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: whiteshadow
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A6P9S6CE3SRSW
4
  Tags: admin, dashboard, menu, security, wpmu
5
  Requires at least: 3.0
6
- Tested up to: 3.1-alpha
7
- Stable tag: 1.0.1
8
 
9
  Lets you directly edit the WordPress admin menu. You can re-order, hide or rename existing menus, add custom menus and more.
10
 
@@ -61,6 +61,14 @@ Plugins installed in the `mu-plugins` directory are treated as "always on", so y
61
 
62
  == Changelog ==
63
 
 
 
 
 
 
 
 
 
64
  = 1.0.1 =
65
  * Added "Super Admin" to the "Required capability" dropdown.
66
  * Prevent users from accidentally making the menu editor inaccessible.
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A6P9S6CE3SRSW
4
  Tags: admin, dashboard, menu, security, wpmu
5
  Requires at least: 3.0
6
+ Tested up to: 3.1
7
+ Stable tag: 1.1
8
 
9
  Lets you directly edit the WordPress admin menu. You can re-order, hide or rename existing menus, add custom menus and more.
10
 
61
 
62
  == Changelog ==
63
 
64
+ = 1.1 =
65
+ * WordPress 3.1 compatibility.
66
+ * Added the ability to drag & drop a menu item to a different menu.
67
+ * Added a drop-down list of Dashboard pages to the "File" box.
68
+ * When the menu editor is opened, the first top-level menu is now automatically selected and it's submenu displayed. Hopefully, this will make the UI slightly easier to understand for first-time users.
69
+ * All corners rounded on the "expand" link when not expanded.
70
+ * By popular request, the "Menu Editor" menu entry can be hidden again.
71
+
72
  = 1.0.1 =
73
  * Added "Super Admin" to the "Required capability" dropdown.
74
  * Prevent users from accidentally making the menu editor inaccessible.