Admin Menu Editor - Version 1.4

Version Description

  • Added a special target page option: "< None >". It makes the selected menu item unclickable. This could be useful for creating menu headers and so on.
  • Added a new menu editor colour scheme that's similar to the default WordPress admin colour scheme. Click the "Settings" button next to the menu editor page title to switch colour schemes.
  • Fixed strange boxes showing up in the icon selector in Internet Explorer.
  • Fixed duplicate top level menus mysteriously disappearing. Now the plugin will properly warn the user that all top level menus must have unique URLs.
  • Fixed an obscure bug where changing the "Target page" from the default setting to "Custom" and back would occasionally make some menu properties suddenly show up as modified for no apparent reason.
  • Fixed incorrect submenu item height and margins in WP 4.0-beta.
  • Fixed a minor layout bug where items with no title would be smaller than other items.
  • Fixed combo-box dropdown button height for WP 3.9.x.
  • Added a workaround for a bug in WordPress Mu Domain Mapping 0.5.4.3.
  • Added a workaround for the very unusual situation where the "user_has_cap" filter is called without a capability.
  • Fixed duplicates of bbPress menu items showing up.
  • Changed the default custom menu icon to the generic "cogwheel" icon from Dashicons.
  • Other small UI changes.
  • Raised minimum requirements to WordPress 3.8 or later. This is mainly due to the increased reliance on Dashicons as menu icons.
Download this release

Release Info

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

Code changes from version 1.3.2 to 1.4

css/admin.css CHANGED
@@ -38,7 +38,66 @@ hr.ws-submenu-separator {
38
  }
39
  */
40
 
 
 
 
 
 
 
 
41
  /* No pointer/hand on separators. */
42
  #adminmenu li.ws-submenu-separator-wrap a {
43
  cursor: default;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
38
  }
39
  */
40
 
41
+ /* Override .wp-menu-separator styles as they don't work too well in submenus. */
42
+ #adminmenu .wp-submenu li.ws-submenu-separator-wrap {
43
+ margin: 0 0 0 0;
44
+ padding: 0;
45
+ height: inherit;
46
+ }
47
+
48
  /* No pointer/hand on separators. */
49
  #adminmenu li.ws-submenu-separator-wrap a {
50
  cursor: default;
51
+ }
52
+
53
+ /* No extra margin in submenus with icons. The selector uses the URL prefix because we can't control the link class.
54
+ * li.ws-submenu-separator-wrap would also work, but it's added via JS so there's an undesirable delay (FOUC).
55
+ */
56
+ #adminmenu .ame-has-submenu-icons ul.wp-submenu li a[href^="#submenu-separator-"] {
57
+ margin-left: 0;
58
+ }
59
+
60
+
61
+ /*
62
+ * Submenu icons.
63
+ */
64
+ .ame-submenu-icon {
65
+ display: inline-block;
66
+ padding-right: 8px;
67
+ min-width: 20px;
68
+
69
+ /*
70
+ Dashicons are 20x20 by default and some of them look pretty bad at smaller sizes. Submenu item titles are 16px high
71
+ by default. So lets hack some negative margins to make a 20px icon fit in 16px. With the current admin UI styles
72
+ it looks okay - submenu items are ~28px high when including padding/margins, so there's no visual overlap.
73
+ */
74
+ height: 20px;
75
+ margin-top: -2px;
76
+ margin-bottom: -2px;
77
+
78
+ vertical-align: top;
79
+
80
+ margin-left: -28px;
81
+ float: left;
82
+
83
+ /* Center image-based icons. Doesn't matter for dashicons. */
84
+ text-align: center;
85
+ }
86
+
87
+ #adminmenu .ame-has-submenu-icons ul.wp-submenu li a {
88
+ /* Push all submenus to the right to ensure that items with and without icons line up nicely. */
89
+ margin-left: 24px;
90
+ }
91
+
92
+ #adminmenu .ame-submenu-icon img {
93
+ padding-top: 2px;
94
+
95
+ opacity: 0.6;
96
+ filter: alpha(opacity=60);
97
+ }
98
+
99
+ #adminmenu .wp-submenu li:hover .ame-submenu-icon img,
100
+ #adminmenu .wp-submenu li.current .ame-submenu-icon img {
101
+ opacity: 1;
102
+ filter: alpha(opacity=100);
103
  }
css/menu-editor.css CHANGED
@@ -9,8 +9,10 @@
9
  width: 310px;
10
  float: left;
11
  display:block;
 
12
  border: 1px solid #cdd5d5;
13
-
 
14
  border-radius: 3px;
15
  -moz-border-radius: 3px;
16
  -webkit-border-radius: 3px;
@@ -42,7 +44,7 @@
42
  }
43
 
44
  .ws_dropzone_hover {
45
- border: 1px dotted silver;
46
  background: yellow;
47
  height: 30px;
48
  }
@@ -286,18 +288,22 @@
286
  margin-top: 0;
287
  }
288
 
 
 
 
 
289
  /* Dropdown button for combo-box fields */
290
  #ws_menu_editor .ws_dropdown_button,
291
  #ws_menu_access_editor .ws_dropdown_button
292
  {
 
293
  width: 20px;
294
- height: 23px;
295
 
296
  margin: 1px 1px 1px 0;
297
  padding: 0;
298
 
299
  text-align: center;
300
- vertical-align: middle;
301
  font-size: 9px !important;
302
 
303
  border-color: #dfdfdf;
@@ -320,8 +326,7 @@
320
 
321
  #ws_menu_access_editor .ws_dropdown_button {
322
  display: inline-block;
323
- height: 22px;
324
- margin-bottom: 2px;
325
  }
326
 
327
  #ws_menu_editor .ws_dropdown_button {
@@ -351,6 +356,8 @@ to accommodate the drop-down button.
351
 
352
  #ws_menu_access_editor input.ws_has_dropdown {
353
  width: 90%;
 
 
354
  }
355
 
356
  #ws_menu_editor .ws_has_dropdown input.ws_field_value {
@@ -419,6 +426,19 @@ to accommodate the drop-down button.
419
  background-image: url('../images/icon-extension-grey.png');
420
  }
421
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
  /* These classes could be used to apply different styles to items depending on their flags */
423
  .ws_custom { }
424
  .ws_hidden { }
@@ -557,6 +577,12 @@ select.ws_dropdown optgroup option {
557
  margin: 0;
558
  }
559
 
 
 
 
 
 
 
560
  #ws_icon_selector .ws_icon_option img {
561
  display: inline-block;
562
  margin: 0;
@@ -715,18 +741,18 @@ select.ws_dropdown optgroup option {
715
  }
716
 
717
  .ui-front {
718
- z-index: 100;
719
  }
720
 
721
  .ui-dialog {
722
  background: white;
723
  border: 1px solid #c0c0c0;
 
 
724
 
725
- padding: 2px;
726
-
727
- border-radius: 5px;
728
  -moz-border-radius: 5px;
729
- -webkit-border-radius: 5px;
 
730
  }
731
 
732
  .ui-dialog-titlebar {
@@ -738,10 +764,17 @@ select.ws_dropdown optgroup option {
738
  background-color: #86A7E3;
739
  font-size: 1.0em;
740
  line-height: 22px;
741
-
742
- border-radius: 5px;
743
- -moz-border-radius: 5px;
744
- -webkit-border-radius: 5px;
 
 
 
 
 
 
 
745
  }
746
 
747
  .ui-dialog-title {
@@ -772,7 +805,7 @@ select.ws_dropdown optgroup option {
772
  }
773
 
774
  .ui-dialog-content {
775
- padding: 6px;
776
  font-size: 1.1em;
777
  }
778
 
@@ -802,6 +835,24 @@ select.ws_dropdown optgroup option {
802
  margin-top: 0;
803
  }
804
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
805
  #import_file_selector {
806
  display: block;
807
  width: 286px;
@@ -935,6 +986,18 @@ select.ws_dropdown optgroup option {
935
  display: none;
936
  }
937
 
 
 
 
 
 
 
 
 
 
 
 
 
938
  /************************************
939
  Tooltips and hints
940
  *************************************/
@@ -1001,4 +1064,33 @@ select.ws_dropdown optgroup option {
1001
  right: 20px;
1002
  bottom: 40px;
1003
  z-index: 100;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1004
  }
9
  width: 310px;
10
  float: left;
11
  display:block;
12
+
13
  border: 1px solid #cdd5d5;
14
+ background-color: #FFFFFF;
15
+
16
  border-radius: 3px;
17
  -moz-border-radius: 3px;
18
  -webkit-border-radius: 3px;
44
  }
45
 
46
  .ws_dropzone_hover {
47
+ border: 1px dashed silver;
48
  background: yellow;
49
  height: 30px;
50
  }
288
  margin-top: 0;
289
  }
290
 
291
+ #ws_menu_editor input[type="text"].ws_field_value {
292
+ min-height: 25px;
293
+ }
294
+
295
  /* Dropdown button for combo-box fields */
296
  #ws_menu_editor .ws_dropdown_button,
297
  #ws_menu_access_editor .ws_dropdown_button
298
  {
299
+ box-sizing: border-box;
300
  width: 20px;
301
+ height: 25px;
302
 
303
  margin: 1px 1px 1px 0;
304
  padding: 0;
305
 
306
  text-align: center;
 
307
  font-size: 9px !important;
308
 
309
  border-color: #dfdfdf;
326
 
327
  #ws_menu_access_editor .ws_dropdown_button {
328
  display: inline-block;
329
+ height: 27px;
 
330
  }
331
 
332
  #ws_menu_editor .ws_dropdown_button {
356
 
357
  #ws_menu_access_editor input.ws_has_dropdown {
358
  width: 90%;
359
+ box-sizing: border-box;
360
+ height: 27px;
361
  }
362
 
363
  #ws_menu_editor .ws_has_dropdown input.ws_field_value {
426
  background-image: url('../images/icon-extension-grey.png');
427
  }
428
 
429
+ /* items with custom permissions for the selected actor */
430
+ .ws_custom_actor_permissions_flag {
431
+ font: 16px/1 'dashicons';
432
+ }
433
+ .ws_custom_actor_permissions_flag::before {
434
+ /*content: "\f160";*/ /* padlock */
435
+ content: "\f110"; /* human silhouette */
436
+ color: black;
437
+
438
+ filter: alpha(opacity=25); /*IE 5-7*/
439
+ opacity: 0.25;
440
+ }
441
+
442
  /* These classes could be used to apply different styles to items depending on their flags */
443
  .ws_custom { }
444
  .ws_hidden { }
577
  margin: 0;
578
  }
579
 
580
+ #ws_icon_selector .ws_icon_option .ws_icon_image.dashicons {
581
+ width: 20px;
582
+ height: 20px;
583
+ padding: 5px;
584
+ }
585
+
586
  #ws_icon_selector .ws_icon_option img {
587
  display: inline-block;
588
  margin: 0;
741
  }
742
 
743
  .ui-front {
744
+ z-index: 10000;
745
  }
746
 
747
  .ui-dialog {
748
  background: white;
749
  border: 1px solid #c0c0c0;
750
+
751
+ padding: 0;
752
 
 
 
 
753
  -moz-border-radius: 5px;
754
+ -webkit-border-radius: 5px;
755
+ border-radius: 5px;
756
  }
757
 
758
  .ui-dialog-titlebar {
764
  background-color: #86A7E3;
765
  font-size: 1.0em;
766
  line-height: 22px;
767
+
768
+ -webkit-border-top-left-radius: 4px;
769
+ -webkit-border-top-right-radius: 4px;
770
+
771
+ -moz-border-radius-topleft: 4px;
772
+ -moz-border-radius-topright: 4px;
773
+
774
+ border-top-left-radius: 4px;
775
+ border-top-right-radius: 4px;
776
+
777
+ border-bottom: 1px solid #809fd9;
778
  }
779
 
780
  .ui-dialog-title {
805
  }
806
 
807
  .ui-dialog-content {
808
+ padding: 8px 8px 8px 8px;
809
  font-size: 1.1em;
810
  }
811
 
835
  margin-top: 0;
836
  }
837
 
838
+ .ws_dialog_buttons.ame-vertical-button-list {
839
+ text-align: left;
840
+ }
841
+
842
+ .ws_dialog_buttons.ame-vertical-button-list .button-primary {
843
+ float: none;
844
+ }
845
+
846
+ .ws_dialog_buttons.ame-vertical-button-list .button {
847
+ width: 100%;
848
+ text-align: left;
849
+ margin-bottom: 10px;
850
+ }
851
+
852
+ .ws_dialog_buttons.ame-vertical-button-list .button:last-child {
853
+ margin-bottom: 0;
854
+ }
855
+
856
  #import_file_selector {
857
  display: block;
858
  width: 286px;
986
  display: none;
987
  }
988
 
989
+
990
+ /************************************
991
+ Menu deletion error
992
+ *************************************/
993
+
994
+ #ws-ame-menu-deletion-error {
995
+ max-width: 400px;
996
+ }
997
+
998
+
999
+
1000
+
1001
  /************************************
1002
  Tooltips and hints
1003
  *************************************/
1064
  right: 20px;
1065
  bottom: 40px;
1066
  z-index: 100;
1067
+ }
1068
+
1069
+
1070
+ .test-wrap {
1071
+ background-color: #444444;
1072
+ padding: 30px;
1073
+ }
1074
+
1075
+ .test-container {
1076
+ width: 400px;
1077
+ height: 200px;
1078
+ background-color: white;
1079
+
1080
+ border: 1px solid black;
1081
+ border-radius: 10px;
1082
+
1083
+ overflow: hidden;
1084
+ }
1085
+
1086
+ .test-header {
1087
+ background-color: #67d6ff;
1088
+ padding: 6px;
1089
+
1090
+ border-top-left-radius: 8px;
1091
+ border-top-right-radius: 8px;
1092
+ }
1093
+
1094
+ .test-content {
1095
+ padding: 8px;
1096
  }
css/{style-wp-gray.css → style-wp-grey.css} RENAMED
@@ -10,8 +10,8 @@
10
  -webkit-box-shadow: inset 0 1px 0 #fff;
11
  box-shadow: inset 0 1px 0 #fff;
12
 
13
- -webkit-border-radius: 3px;
14
- border-radius: 3px;
15
  }
16
 
17
  /**
@@ -19,31 +19,19 @@
19
  */
20
 
21
  .ws_item_head {
22
- padding: 0;
23
- border-top-left-radius: 3px;
24
- border-top-right-radius: 3px;
25
 
26
  background-color: #d9d9d9;
27
- background-image: -o-linear-gradient(top, #efefef, #d9d9d9);
28
- background-image: -o-linear-gradient(top, #e9e9e9, #d9d9d9);
29
  background-image: -ms-linear-gradient(top, #e9e9e9, #d9d9d9);
30
  background-image: -moz-linear-gradient(top, #e9e9e9, #d9d9d9);
31
  background-image: -webkit-gradient(linear, left top, left bottom, from(#e9e9e9), to(#d9d9d9));
32
  background-image: -webkit-linear-gradient(top, #e9e9e9, #d9d9d9);
33
- background-image: linear-gradient(top, #e9e9e9, #d9d9d9);
34
- /*background-color: #c7c7c7;*/
35
  }
36
 
37
  .ws_item_title {
38
- padding: 6px 5px;
39
-
40
- /*font-weight: bold;*/
41
  color: #222;
42
- text-shadow: #FFFFFF 0px 1px 0px;
43
- }
44
-
45
- .ws_flag_container {
46
- padding-top: 6px;
47
  }
48
 
49
  /**
@@ -51,10 +39,7 @@
51
  */
52
 
53
  .ws_edit_link {
54
- width: 30px;
55
- height: 30px;
56
-
57
- background: transparent url(../images/arrows.png) no-repeat center 5px;
58
  overflow: hidden;
59
  text-indent:-999em;
60
  }
@@ -64,7 +49,7 @@ a.ws_edit_link:hover {
64
  }
65
 
66
  .ws_edit_link:active {
67
- background: transparent url(../images/arrows-dark.png) no-repeat center 5px;
68
  }
69
 
70
  .ws_edit_link_expanded {
@@ -92,19 +77,19 @@ a.ws_edit_link:hover {
92
  }
93
 
94
  .ws_menu_separator .ws_item_head {
95
- height: 29px;
96
- border-radius: 3px;
97
  background: #F9F9F9 url("../images/menu-arrows.png") no-repeat 4px 8px;
98
  }
99
 
100
- .ws_menu_separator .ws_item_title {
101
- display: none;
102
- }
103
-
104
  .ws_menu_separator.ws_active .ws_item_head {
105
  background: #999 url("../images/menu-arrows.png") no-repeat 4px 8px;
106
  }
107
 
 
 
 
 
 
108
  /**
109
  * Active item
110
  */
@@ -125,6 +110,33 @@ a.ws_edit_link:hover {
125
  border-bottom-color: #6d6d6d;
126
  }
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  /**
129
  * Dropping menus on other menus.
130
  */
@@ -190,22 +202,44 @@ a.ws_button:hover {
190
  .ui-dialog {
191
  background: white;
192
  border: 1px solid #c0c0c0;
 
 
193
  }
194
 
195
  .ui-dialog-titlebar {
196
- background-color: #86A7E3;
 
197
  }
198
 
199
  .ui-dialog-title {
200
- color: white;
201
  }
202
 
203
  .ui-dialog-titlebar-close {
204
- background: #86A7E3 url(../images/x.png) no-repeat center;
205
- color: white;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  }
207
 
208
  .ui-dialog-titlebar-close:hover {
209
- /*background-image: url(../images/x-light.png);*/
210
- background-color: #a6c2f5;
211
  }
10
  -webkit-box-shadow: inset 0 1px 0 #fff;
11
  box-shadow: inset 0 1px 0 #fff;
12
 
13
+ /*-webkit-border-radius: 2px;
14
+ border-radius: 2px;*/
15
  }
16
 
17
  /**
19
  */
20
 
21
  .ws_item_head {
22
+ padding: 3px;
 
 
23
 
24
  background-color: #d9d9d9;
 
 
25
  background-image: -ms-linear-gradient(top, #e9e9e9, #d9d9d9);
26
  background-image: -moz-linear-gradient(top, #e9e9e9, #d9d9d9);
27
  background-image: -webkit-gradient(linear, left top, left bottom, from(#e9e9e9), to(#d9d9d9));
28
  background-image: -webkit-linear-gradient(top, #e9e9e9, #d9d9d9);
29
+ background-image: linear-gradient(to bottom, #e9e9e9, #d9d9d9);
 
30
  }
31
 
32
  .ws_item_title {
 
 
 
33
  color: #222;
34
+ text-shadow: #FFFFFF 0 1px 0;
 
 
 
 
35
  }
36
 
37
  /**
39
  */
40
 
41
  .ws_edit_link {
42
+ background: transparent url(../images/arrows.png) no-repeat center 3px;
 
 
 
43
  overflow: hidden;
44
  text-indent:-999em;
45
  }
49
  }
50
 
51
  .ws_edit_link:active {
52
+ background-image: url(../images/arrows-dark.png);
53
  }
54
 
55
  .ws_edit_link_expanded {
77
  }
78
 
79
  .ws_menu_separator .ws_item_head {
80
+ min-height: 22px;
 
81
  background: #F9F9F9 url("../images/menu-arrows.png") no-repeat 4px 8px;
82
  }
83
 
 
 
 
 
84
  .ws_menu_separator.ws_active .ws_item_head {
85
  background: #999 url("../images/menu-arrows.png") no-repeat 4px 8px;
86
  }
87
 
88
+ /* Offset the separator image in actor view to prevent it from overlapping the checkbox. */
89
+ .ws_is_actor_view .ws_menu_separator .ws_item_head {
90
+ background-position: 25px 8px;
91
+ }
92
+
93
  /**
94
  * Active item
95
  */
110
  border-bottom-color: #6d6d6d;
111
  }
112
 
113
+ /**
114
+ * Hidden items.
115
+ */
116
+ .ws_is_actor_view .ws_container.ws_is_hidden_for_actor .ws_item_head {
117
+ background: #F9F9F9 linear-gradient(to top, #F3F3F3, #FFFFFF);
118
+ }
119
+
120
+ /* selected hidden items */
121
+ .ws_is_actor_view .ws_is_hidden_for_actor.ws_active .ws_item_head {
122
+ background: #dedede linear-gradient(to top, #8f8f8f, #a2a2a2);
123
+ }
124
+ .ws_is_actor_view .ws_is_hidden_for_actor.ws_active .ws_item_title {
125
+ color: #fff;
126
+ text-shadow: none;
127
+ }
128
+
129
+ /* hidden separators */
130
+ .ws_is_actor_view .ws_menu_separator.ws_is_hidden_for_actor .ws_item_head {
131
+ /* Override gradient with the separator image. */
132
+ background: url("../images/menu-arrows.png") no-repeat 25px 8px;
133
+ }
134
+ /* selected hidden separators */
135
+ .ws_menu_separator.ws_is_hidden_for_actor.ws_active .ws_item_head {
136
+ background: #aaa url("../images/menu-arrows.png") no-repeat 25px 8px;
137
+ }
138
+
139
+
140
  /**
141
  * Dropping menus on other menus.
142
  */
202
  .ui-dialog {
203
  background: white;
204
  border: 1px solid #c0c0c0;
205
+
206
+ border-radius: 0;
207
  }
208
 
209
  .ui-dialog-titlebar {
210
+ background-color: #fcfcfc;;
211
+ border-bottom-color: #dfdfdf;
212
  }
213
 
214
  .ui-dialog-title {
215
+ color: #444444;
216
  }
217
 
218
  .ui-dialog-titlebar-close {
219
+ background-color: transparent;
220
+ border-style: none;
221
+
222
+ color: #666;
223
+ cursor: pointer;
224
+ padding: 0;
225
+ position: absolute;
226
+ top: 0;
227
+ right: 0;
228
+ width: 36px;
229
+ height: 30px;
230
+ text-align: center;
231
+ }
232
+
233
+ .ui-dialog-titlebar-close::before {
234
+ font: normal 20px/30px 'dashicons';
235
+ content: '\f158';
236
+
237
+ vertical-align: top;
238
+ width: 36px;
239
+ height: 30px;
240
  }
241
 
242
  .ui-dialog-titlebar-close:hover {
243
+ background: transparent none;
244
+ color: #2ea2cc;
245
  }
includes/editor-page.php CHANGED
@@ -2,6 +2,7 @@
2
  /**
3
  * @var array $editor_data Various pieces of data passed by the plugin.
4
  */
 
5
  $images_url = $editor_data['images_url'];
6
 
7
  $icons = array(
@@ -126,13 +127,15 @@ if ( apply_filters('admin_menu_editor_is_pro', false) ) {
126
  <a id='ws_toggle_all_menus' class='ws_button' href='javascript:void(0)'
127
  title='Toggle all menus for the selected role'><img src='<?php echo $icons['toggle-all']; ?>' alt="Toggle all" /></a>
128
  <?php endif; ?>
 
 
129
  </div>
130
  </div>
131
 
132
  <div id='ws_menu_box' class="ws_box">
133
  </div>
134
 
135
- <?php do_action('admin_menu_editor_container', 'menu'); ?>
136
  </div>
137
 
138
  <div class='ws_main_container'>
@@ -163,13 +166,15 @@ if ( apply_filters('admin_menu_editor_is_pro', false) ) {
163
  <a id='ws_sort_descending' class='ws_button' href='javascript:void(0)' title='Sort descending'>
164
  <img src='<?php echo $images_url; ?>/sort_descending.png' alt="Sort descending" />
165
  </a>
 
 
166
  </div>
167
  </div>
168
 
169
  <div id='ws_submenu_box' class="ws_box">
170
  </div>
171
 
172
- <?php do_action('admin_menu_editor_container', 'submenu'); ?>
173
  </div>
174
 
175
  <div class="ws_basic_container">
@@ -188,7 +193,7 @@ if ( apply_filters('admin_menu_editor_is_pro', false) ) {
188
  <input type="button" id='ws_load_menu' value="Load default menu" class="button ws_main_button" />
189
 
190
  <?php
191
- do_action('admin_menu_editor_sidebar');
192
  ?>
193
  </div>
194
 
@@ -342,7 +347,7 @@ if ( apply_filters('admin_menu_editor_is_pro', false) ) {
342
  function ws_ame_print_dashicon_option($icon, $isExtraIcon = false) {
343
  printf(
344
  '<div class="ws_icon_option%3$s" title="%1$s" data-icon-url="dashicons-%2$s">
345
- <div class="ws_icon_image icon16 dashicons dashicons-%2$s"><br></div>
346
  </div>',
347
  esc_attr(ucwords(str_replace('-', ' ', $icon))),
348
  $icon,
@@ -441,6 +446,28 @@ if ( apply_filters('admin_menu_editor_is_pro', false) ) {
441
  </label>
442
  </div>
443
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
444
 
445
  <script type='text/javascript'>
446
  var defaultMenu = <?php echo $editor_data['default_menu_js']; ?>;
@@ -450,4 +477,4 @@ var customMenu = <?php echo $editor_data['custom_menu_js']; ?>;
450
  <?php
451
 
452
  //Let the Pro version script output it's extra HTML & scripts.
453
- do_action('admin_menu_editor_footer');
2
  /**
3
  * @var array $editor_data Various pieces of data passed by the plugin.
4
  */
5
+ $current_user = wp_get_current_user();
6
  $images_url = $editor_data['images_url'];
7
 
8
  $icons = array(
127
  <a id='ws_toggle_all_menus' class='ws_button' href='javascript:void(0)'
128
  title='Toggle all menus for the selected role'><img src='<?php echo $icons['toggle-all']; ?>' alt="Toggle all" /></a>
129
  <?php endif; ?>
130
+
131
+ <div class="clear"></div>
132
  </div>
133
  </div>
134
 
135
  <div id='ws_menu_box' class="ws_box">
136
  </div>
137
 
138
+ <?php do_action('admin_menu_editor-container', 'menu'); ?>
139
  </div>
140
 
141
  <div class='ws_main_container'>
166
  <a id='ws_sort_descending' class='ws_button' href='javascript:void(0)' title='Sort descending'>
167
  <img src='<?php echo $images_url; ?>/sort_descending.png' alt="Sort descending" />
168
  </a>
169
+
170
+ <div class="clear"></div>
171
  </div>
172
  </div>
173
 
174
  <div id='ws_submenu_box' class="ws_box">
175
  </div>
176
 
177
+ <?php do_action('admin_menu_editor-container', 'submenu'); ?>
178
  </div>
179
 
180
  <div class="ws_basic_container">
193
  <input type="button" id='ws_load_menu' value="Load default menu" class="button ws_main_button" />
194
 
195
  <?php
196
+ do_action('admin_menu_editor-sidebar');
197
  ?>
198
  </div>
199
 
347
  function ws_ame_print_dashicon_option($icon, $isExtraIcon = false) {
348
  printf(
349
  '<div class="ws_icon_option%3$s" title="%1$s" data-icon-url="dashicons-%2$s">
350
+ <div class="ws_icon_image icon16 dashicons dashicons-%2$s"></div>
351
  </div>',
352
  esc_attr(ucwords(str_replace('-', ' ', $icon))),
353
  $icon,
446
  </label>
447
  </div>
448
 
449
+ <!-- Confirmation dialog when trying to delete a non-custom item. -->
450
+ <div id="ws-ame-menu-deletion-error" title="Error" style="display: none;">
451
+ <div class="ws_dialog_panel">
452
+ Sorry, it's not possible to permanently delete
453
+ <span id="ws-ame-menu-type-desc">{a built-in menu item|an item added by another plugin}</span>.
454
+ Would you like to hide it instead?
455
+ </div>
456
+
457
+ <div class="ws_dialog_buttons ame-vertical-button-list">
458
+ <?php
459
+ submit_button('Hide it from all users', 'secondary', 'ws_hide_menu_from_everyone', false);
460
+ submit_button(
461
+ sprintf('Hide it from everyone except "%s"', $current_user->get('user_login')),
462
+ 'secondary',
463
+ 'ws_hide_menu_except_current_user',
464
+ false
465
+ );
466
+ submit_button('Cancel', 'secondary', 'ws_cancel_menu_deletion', false);
467
+ ?>
468
+ </div>
469
+ </div>
470
+
471
 
472
  <script type='text/javascript'>
473
  var defaultMenu = <?php echo $editor_data['default_menu_js']; ?>;
477
  <?php
478
 
479
  //Let the Pro version script output it's extra HTML & scripts.
480
+ do_action('admin_menu_editor-footer');
includes/menu-editor-core.php CHANGED
@@ -104,6 +104,12 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
104
  'show_deprecated_hide_button' => null,
105
  'dashboard_hiding_confirmation_enabled' => true,
106
 
 
 
 
 
 
 
107
  //Enable/disable the admin notice that tells the user where the plugin settings menu is.
108
  'show_plugin_menu_notice' => true,
109
  );
@@ -234,13 +240,13 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
234
  $page = add_options_page(
235
  apply_filters('admin_menu_editor-self_page_title', 'Menu Editor'),
236
  apply_filters('admin_menu_editor-self_menu_title', 'Menu Editor'),
237
- apply_filters('admin_menu_editor_capability', 'manage_options'),
238
  'menu_editor',
239
  array(&$this, 'page_menu_editor')
240
  );
241
  //Output our JS & CSS on that page only
242
- add_action("admin_print_scripts-$page", array(&$this, 'enqueue_scripts'));
243
- add_action("admin_print_styles-$page", array(&$this, 'enqueue_styles'));
244
 
245
  //Compatibility fix for All In One Event Calendar; see the callback for details.
246
  add_action("admin_print_scripts-$page", array($this, 'dequeue_ai1ec_scripts'));
@@ -258,10 +264,18 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
258
  //Store the "original" menus for later use in the editor
259
  $this->default_wp_menu = $menu;
260
  $this->default_wp_submenu = $submenu;
261
-
 
 
 
 
 
262
  //Generate item templates from the default menu.
263
  $this->item_templates = $this->build_templates($this->default_wp_menu, $this->default_wp_submenu);
264
 
 
 
 
265
  //Is there a custom menu to use?
266
  $custom_menu = $this->load_custom_menu();
267
  if ( $custom_menu !== null ){
@@ -527,6 +541,8 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
527
  'adminAjaxUrl' => admin_url('admin-ajax.php'),
528
  'hideAdvancedSettings' => (boolean)$this->options['hide_advanced_settings'],
529
  'showExtraIcons' => $showExtraIcons,
 
 
530
  'hideAdvancedSettingsNonce' => wp_create_nonce('ws_ame_save_screen_options'),
531
  'dashiconsAvailable' => wp_style_is('dashicons', 'registered'),
532
  'captionShowAdvanced' => 'Show advanced options',
@@ -542,6 +558,9 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
542
  'defaults' => ameMenuItem::custom_item_defaults(),
543
  ),
544
 
 
 
 
545
  'actors' => $actors,
546
  'roles' => $roles,
547
  'users' => $users,
@@ -609,8 +628,8 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
609
  array('menu-editor-base-style')
610
  );
611
  wp_register_auto_versioned_style(
612
- 'menu-editor-colours-wp-gray',
613
- plugins_url('css/style-wp-gray.css', $this->plugin_file),
614
  array('menu-editor-base-style')
615
  );
616
 
@@ -625,7 +644,8 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
625
  wp_enqueue_style('menu-editor-screen-meta');
626
  }
627
 
628
- wp_enqueue_style('menu-editor-colours-classic');
 
629
  wp_enqueue_style('wp-color-picker');
630
  }
631
 
@@ -805,6 +825,52 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
805
  return strip_tags( preg_replace('@<span[^>]*>.*</span>@i', '', $title) );
806
  }
807
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
808
  /**
809
  * Merge a custom menu with the current default WordPress menu. Adds/replaces defaults,
810
  * inserts new items and removes missing items.
@@ -877,25 +943,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
877
 
878
  //Now we have some items marked as missing, and some items in lookup arrays
879
  //that are not marked as used. Lets remove the missing items from the tree.
880
- $filteredTree = array();
881
- foreach($tree as $file => $topmenu) {
882
- if ( $topmenu['missing'] ) {
883
- continue;
884
- }
885
- $filteredSubmenu = array();
886
- if (is_array($topmenu['items'])) {
887
- foreach($topmenu['items'] as $index => $item) {
888
- if ( !$item['missing'] ) {
889
- $filteredSubmenu[$index] = $item;
890
- }
891
- }
892
-
893
- }
894
- $topmenu['items'] = $filteredSubmenu;
895
- $filteredTree[$file] = $topmenu;
896
- }
897
-
898
- $tree = $filteredTree;
899
 
900
  //Lets merge in the unused items.
901
  foreach ($this->item_templates as $template_id => $template){
@@ -1035,6 +1083,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1035
 
1036
  //Prepare the submenu of this menu
1037
  $new_items = array();
 
1038
  if( !empty($topmenu['items']) ){
1039
  $items = $topmenu['items'];
1040
  //Sort by position
@@ -1051,9 +1100,18 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1051
 
1052
  //Make a note of the page's correct title so we can fix it later if necessary.
1053
  $this->title_lookups[$item['file']] = !empty($item['page_title']) ? $item['page_title'] : $item['menu_title'];
 
 
 
1054
  }
1055
  }
1056
 
 
 
 
 
 
 
1057
  $topmenu['items'] = $new_items;
1058
  $new_tree[] = $topmenu;
1059
  }
@@ -1084,8 +1142,6 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1084
  $this->reverse_item_lookup[$topmenu['url']] = $topmenu;
1085
  }
1086
 
1087
- $new_menu[] = $this->convert_to_wp_format($topmenu);
1088
-
1089
  foreach($topmenu['items'] as $item) {
1090
  $trueAccess = isset($this->page_access_lookup[$item['url']]) ? $this->page_access_lookup[$item['url']] : null;
1091
  if ( ($trueAccess === 'do_not_allow') && ($item['access_level'] !== $trueAccess) ) {
@@ -1104,6 +1160,8 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1104
  $this->reverse_item_lookup[$item['url']] = $item;
1105
  $new_submenu[$topmenu['file']][] = $this->convert_to_wp_format($item);
1106
  }
 
 
1107
  }
1108
 
1109
  $this->custom_wp_menu = $new_menu;
@@ -1164,12 +1222,28 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1164
  }
1165
  }
1166
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1167
  //Menus that have both a custom icon URL and a "menu-icon-*" class will get two overlapping icons.
1168
  //Fix this by automatically removing the class. The user can set a custom class attr. to override.
 
 
1169
  if (
1170
  ameMenuItem::is_default($item, 'css_class')
1171
- && !ameMenuItem::is_default($item, 'icon_url')
1172
- && !in_array($item['icon_url'], array('', 'none', 'div')) //Skip "no custom icon" icons.
1173
  ) {
1174
  $new_classes = preg_replace('@\bmenu-icon-[^\s]+\b@', '', $item['defaults']['css_class']);
1175
  if ( $new_classes !== $item['defaults']['css_class'] ) {
@@ -1193,6 +1267,20 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1193
  $item['icon_url'] = 'none';
1194
  }
1195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1196
  //Used later to determine the current page based on URL.
1197
  if ( empty($item['url']) ) {
1198
  $original_parent = !empty($item['defaults']['parent']) ? $item['defaults']['parent'] : $parent;
@@ -1314,7 +1402,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1314
  }
1315
 
1316
  $action = isset($this->post['action']) ? $this->post['action'] : (isset($this->get['action']) ? $this->get['action'] : '');
1317
- do_action('admin_menu_editor_header', $action);
1318
 
1319
  if ( !empty($action) ) {
1320
  $this->handle_form_submission($this->post, $action);
@@ -1365,7 +1453,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1365
  //Also, automatically select the last selected actor (convenience feature).
1366
  $query = array('message' => 1);
1367
  if ( isset($post['selected_actor']) && !empty($post['selected_actor']) ) {
1368
- $query['selected_actor'] = strval($post['selected_actor']);
1369
  }
1370
  wp_redirect( add_query_arg($query, $url) );
1371
  die();
@@ -1429,6 +1517,24 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1429
  $this->options['show_deprecated_hide_button'] = !empty($this->post['show_deprecated_hide_button']);
1430
  }
1431
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1432
  $this->save_options();
1433
  wp_redirect(add_query_arg('updated', 1, $this->get_settings_page_url()));
1434
  }
@@ -1617,11 +1723,14 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
1617
  * @param array $args The capability passed to current_user_can, the current user's ID, and other args.
1618
  * @return array Filtered version of $allcaps
1619
  */
1620
- function hook_user_has_cap($allcaps, $required_caps, $args){
1621
  //Be careful not to overwrite a super_admin cap added by other plugins
1622
  //For example, Advanced Access Manager also adds this capability.
1623
- if ( in_array('super_admin', $required_caps) && !isset($allcaps['super_admin']) ){
1624
- $allcaps['super_admin'] = is_multisite() && is_super_admin($args[1]);
 
 
 
1625
  }
1626
  return $allcaps;
1627
  }
@@ -2033,7 +2142,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
2033
  'ame-helper-style',
2034
  plugins_url('css/admin.css', $this->plugin_file),
2035
  array(),
2036
- '20140220-2'
2037
  );
2038
  }
2039
 
@@ -2085,7 +2194,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
2085
  * @return string
2086
  */
2087
  private function get_formatted_security_log() {
2088
- $log = '<div style="font: 12px/16.8px Consolas, monospace; margin-bottom: 1em;">';
2089
  $log .= implode("<br>\n", $this->security_log);
2090
  $log .= '</div>';
2091
  return $log;
@@ -2183,6 +2292,55 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
2183
  return $name;
2184
  }
2185
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2186
  /**
2187
  * Tell new users how to access the plugin settings page.
2188
  */
104
  'show_deprecated_hide_button' => null,
105
  'dashboard_hiding_confirmation_enabled' => true,
106
 
107
+ //When to show submenu icons.
108
+ 'submenu_icons_enabled' => 'if_custom', //"never", "if_custom" or "always".
109
+
110
+ //Menu editor UI colour scheme. "Classic" is the old blue/yellow scheme, and "wp-grey" is more WP-like.
111
+ 'ui_colour_scheme' => 'classic',
112
+
113
  //Enable/disable the admin notice that tells the user where the plugin settings menu is.
114
  'show_plugin_menu_notice' => true,
115
  );
240
  $page = add_options_page(
241
  apply_filters('admin_menu_editor-self_page_title', 'Menu Editor'),
242
  apply_filters('admin_menu_editor-self_menu_title', 'Menu Editor'),
243
+ apply_filters('admin_menu_editor-capability', 'manage_options'),
244
  'menu_editor',
245
  array(&$this, 'page_menu_editor')
246
  );
247
  //Output our JS & CSS on that page only
248
+ add_action("admin_print_scripts-$page", array($this, 'enqueue_scripts'));
249
+ add_action("admin_print_styles-$page", array($this, 'enqueue_styles'));
250
 
251
  //Compatibility fix for All In One Event Calendar; see the callback for details.
252
  add_action("admin_print_scripts-$page", array($this, 'dequeue_ai1ec_scripts'));
264
  //Store the "original" menus for later use in the editor
265
  $this->default_wp_menu = $menu;
266
  $this->default_wp_submenu = $submenu;
267
+
268
+ //Compatibility fix for bbPress.
269
+ $this->apply_bbpress_compat_fix();
270
+ //Compatibility fix for WordPress Mu Domain Mapping.
271
+ $this->apply_wpmu_domain_mapping_fix();
272
+
273
  //Generate item templates from the default menu.
274
  $this->item_templates = $this->build_templates($this->default_wp_menu, $this->default_wp_submenu);
275
 
276
+ //Add extra templates that are not part of the normal menu.
277
+ $this->item_templates = $this->add_special_templates($this->item_templates);
278
+
279
  //Is there a custom menu to use?
280
  $custom_menu = $this->load_custom_menu();
281
  if ( $custom_menu !== null ){
541
  'adminAjaxUrl' => admin_url('admin-ajax.php'),
542
  'hideAdvancedSettings' => (boolean)$this->options['hide_advanced_settings'],
543
  'showExtraIcons' => $showExtraIcons,
544
+ 'submenuIconsEnabled' => $this->options['submenu_icons_enabled'],
545
+
546
  'hideAdvancedSettingsNonce' => wp_create_nonce('ws_ame_save_screen_options'),
547
  'dashiconsAvailable' => wp_style_is('dashicons', 'registered'),
548
  'captionShowAdvanced' => 'Show advanced options',
558
  'defaults' => ameMenuItem::custom_item_defaults(),
559
  ),
560
 
561
+ 'unclickableTemplateId' => ameMenuItem::unclickableTemplateId,
562
+ 'unclickableTemplateClass' => ameMenuItem::unclickableTemplateClass,
563
+
564
  'actors' => $actors,
565
  'roles' => $roles,
566
  'users' => $users,
628
  array('menu-editor-base-style')
629
  );
630
  wp_register_auto_versioned_style(
631
+ 'menu-editor-colours-wp-grey',
632
+ plugins_url('css/style-wp-grey.css', $this->plugin_file),
633
  array('menu-editor-base-style')
634
  );
635
 
644
  wp_enqueue_style('menu-editor-screen-meta');
645
  }
646
 
647
+ $scheme = $this->options['ui_colour_scheme'];
648
+ wp_enqueue_style('menu-editor-colours-' . $scheme);
649
  wp_enqueue_style('wp-color-picker');
650
  }
651
 
825
  return strip_tags( preg_replace('@<span[^>]*>.*</span>@i', '', $title) );
826
  }
827
 
828
+ /**
829
+ * Generate special menu templates and add them to the input template list.
830
+ *
831
+ * @param array $templates Template list.
832
+ * @return array Modified template list.
833
+ */
834
+ private function add_special_templates($templates) {
835
+ //Add a special template for unclickable menu items. These can be used as headers and such.
836
+ $itemDefaults = ameMenuItem::custom_item_defaults();
837
+ $unclickableDefaults = array_merge(
838
+ $itemDefaults,
839
+ array(
840
+ 'file' => '#' . ameMenuItem::unclickableTemplateClass,
841
+ 'url' => '#' . ameMenuItem::unclickableTemplateClass,
842
+ 'css_class' => $itemDefaults['css_class'] . ' ' . ameMenuItem::unclickableTemplateClass,
843
+ 'menu_title' => 'Unclickable Menu',
844
+ )
845
+ );
846
+ $templates[ameMenuItem::unclickableTemplateId] = array(
847
+ 'name' => '< None >',
848
+ 'used' => true,
849
+ 'defaults' => $unclickableDefaults,
850
+ );
851
+
852
+ if ( $this->is_pro_version() ) {
853
+ //The Pro version has a [wp-logout-url] shortcode. Lets make it easier o use
854
+ //by adding it to the "Target page" dropdown.
855
+ $logoutDefaults = array_merge(
856
+ ameMenuItem::basic_defaults(),
857
+ array(
858
+ 'menu_title' => 'Logout',
859
+ 'file' => '[wp-logout-url]',
860
+ 'url' => '[wp-logout-url]',
861
+ 'icon_url' => 'dashicons-migrate',
862
+ )
863
+ );
864
+ $templates['>logout'] = array(
865
+ 'name' => 'Logout',
866
+ 'used' => true,
867
+ 'defaults' => $logoutDefaults,
868
+ );
869
+ }
870
+
871
+ return $templates;
872
+ }
873
+
874
  /**
875
  * Merge a custom menu with the current default WordPress menu. Adds/replaces defaults,
876
  * inserts new items and removes missing items.
943
 
944
  //Now we have some items marked as missing, and some items in lookup arrays
945
  //that are not marked as used. Lets remove the missing items from the tree.
946
+ $tree = ameMenu::remove_missing_items($tree);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
947
 
948
  //Lets merge in the unused items.
949
  foreach ($this->item_templates as $template_id => $template){
1083
 
1084
  //Prepare the submenu of this menu
1085
  $new_items = array();
1086
+ $has_submenu_icons = false;
1087
  if( !empty($topmenu['items']) ){
1088
  $items = $topmenu['items'];
1089
  //Sort by position
1100
 
1101
  //Make a note of the page's correct title so we can fix it later if necessary.
1102
  $this->title_lookups[$item['file']] = !empty($item['page_title']) ? $item['page_title'] : $item['menu_title'];
1103
+
1104
+ //Keep track of which menus have items with icons.
1105
+ $has_submenu_icons = $has_submenu_icons || !empty($item['has_submenu_icon']);
1106
  }
1107
  }
1108
 
1109
+ //The ame-has-submenu-icons class lets us change the appearance of all submenu items at once,
1110
+ //without having to add classes/styles to each item individually.
1111
+ if ( $has_submenu_icons && (strpos($topmenu['css_class'], 'ame-has-submenu-icons') === false) ) {
1112
+ $topmenu['css_class'] .= ' ame-has-submenu-icons';
1113
+ }
1114
+
1115
  $topmenu['items'] = $new_items;
1116
  $new_tree[] = $topmenu;
1117
  }
1142
  $this->reverse_item_lookup[$topmenu['url']] = $topmenu;
1143
  }
1144
 
 
 
1145
  foreach($topmenu['items'] as $item) {
1146
  $trueAccess = isset($this->page_access_lookup[$item['url']]) ? $this->page_access_lookup[$item['url']] : null;
1147
  if ( ($trueAccess === 'do_not_allow') && ($item['access_level'] !== $trueAccess) ) {
1160
  $this->reverse_item_lookup[$item['url']] = $item;
1161
  $new_submenu[$topmenu['file']][] = $this->convert_to_wp_format($item);
1162
  }
1163
+
1164
+ $new_menu[] = $this->convert_to_wp_format($topmenu);
1165
  }
1166
 
1167
  $this->custom_wp_menu = $new_menu;
1222
  }
1223
  }
1224
 
1225
+ //Give each unclickable item a unique URL.
1226
+ if ( $item['template_id'] === ameMenuItem::unclickableTemplateId ) {
1227
+ static $unclickableCounter = 0;
1228
+ $unclickableCounter++;
1229
+ $unclickableUrl = '#' . ameMenuItem::unclickableTemplateClass . '-' . $unclickableCounter;
1230
+ $item['file'] = $item['url'] = $unclickableUrl;
1231
+
1232
+ //The item must have the special "unclickable" class even if the user overrides the class.
1233
+ $cssClass = ameMenuItem::get($item, 'css_class', '');
1234
+ if ( strpos($cssClass, ameMenuItem::unclickableTemplateClass) === false ) {
1235
+ $item['css_class'] = ameMenuItem::unclickableTemplateClass . ' ' . $cssClass;
1236
+ }
1237
+ }
1238
+
1239
  //Menus that have both a custom icon URL and a "menu-icon-*" class will get two overlapping icons.
1240
  //Fix this by automatically removing the class. The user can set a custom class attr. to override.
1241
+ $hasCustomIconUrl = !ameMenuItem::is_default($item, 'icon_url');
1242
+ $hasIcon = !in_array(ameMenuItem::get($item, 'icon_url'), array('', 'none', 'div'));
1243
  if (
1244
  ameMenuItem::is_default($item, 'css_class')
1245
+ && $hasCustomIconUrl
1246
+ && $hasIcon //Skip "no icon" settings.
1247
  ) {
1248
  $new_classes = preg_replace('@\bmenu-icon-[^\s]+\b@', '', $item['defaults']['css_class']);
1249
  if ( $new_classes !== $item['defaults']['css_class'] ) {
1267
  $item['icon_url'] = 'none';
1268
  }
1269
 
1270
+ //Submenus must not have the "menu-top" class(-es). In WP versions that support submenu CSS classes,
1271
+ //it can break menu display.
1272
+ if ( !empty($item['css_class']) && ($item_type === 'submenu') ) {
1273
+ $item['css_class'] = preg_replace('@\bmenu-top(?:-[\w\-]+)?\b@', '', $item['css_class']);
1274
+ } elseif ( ($item_type === 'menu') && (!$item['separator']) && (!preg_match('@\bmenu-top\b@', $item['css_class'])) ) {
1275
+ //Top-level menus should always have the "menu-top" class.
1276
+ $item['css_class'] = 'menu-top ' . $item['css_class'];
1277
+ }
1278
+
1279
+ //Add submenu icons if necessary.
1280
+ if ( ($item_type === 'submenu') && $hasIcon ) {
1281
+ $item = apply_filters('admin_menu_editor-submenu_with_icon', $item, $hasCustomIconUrl);
1282
+ }
1283
+
1284
  //Used later to determine the current page based on URL.
1285
  if ( empty($item['url']) ) {
1286
  $original_parent = !empty($item['defaults']['parent']) ? $item['defaults']['parent'] : $parent;
1402
  }
1403
 
1404
  $action = isset($this->post['action']) ? $this->post['action'] : (isset($this->get['action']) ? $this->get['action'] : '');
1405
+ do_action('admin_menu_editor-header', $action);
1406
 
1407
  if ( !empty($action) ) {
1408
  $this->handle_form_submission($this->post, $action);
1453
  //Also, automatically select the last selected actor (convenience feature).
1454
  $query = array('message' => 1);
1455
  if ( isset($post['selected_actor']) && !empty($post['selected_actor']) ) {
1456
+ $query['selected_actor'] = rawurlencode(strval($post['selected_actor']));
1457
  }
1458
  wp_redirect( add_query_arg($query, $url) );
1459
  die();
1517
  $this->options['show_deprecated_hide_button'] = !empty($this->post['show_deprecated_hide_button']);
1518
  }
1519
 
1520
+ //Menu editor colour scheme.
1521
+ if ( !empty($this->post['ui_colour_scheme']) ) {
1522
+ $valid_colour_schemes = array('classic', 'wp-grey');
1523
+ $scheme = strval($this->post['ui_colour_scheme']);
1524
+ if ( in_array($scheme, $valid_colour_schemes) ) {
1525
+ $this->options['ui_colour_scheme'] = $scheme;
1526
+ }
1527
+ }
1528
+
1529
+ //Enable submenu icons.
1530
+ if ( !empty($this->post['submenu_icons_enabled']) ) {
1531
+ $submenu_icons_enabled = strval($this->post['submenu_icons_enabled']);
1532
+ $valid_icon_settings = array('never', 'if_custom', 'always');
1533
+ if ( in_array($submenu_icons_enabled, $valid_icon_settings, true) ) {
1534
+ $this->options['submenu_icons_enabled'] = $submenu_icons_enabled;
1535
+ }
1536
+ }
1537
+
1538
  $this->save_options();
1539
  wp_redirect(add_query_arg('updated', 1, $this->get_settings_page_url()));
1540
  }
1723
  * @param array $args The capability passed to current_user_can, the current user's ID, and other args.
1724
  * @return array Filtered version of $allcaps
1725
  */
1726
+ function hook_user_has_cap($allcaps, /** @noinspection PhpUnusedParameterInspection */ $required_caps, $args){
1727
  //Be careful not to overwrite a super_admin cap added by other plugins
1728
  //For example, Advanced Access Manager also adds this capability.
1729
+ if ( is_array($allcaps) && !isset($allcaps['super_admin']) ){
1730
+ $user_id = intval($args[1]);
1731
+ if ( $user_id != 0 ) {
1732
+ $allcaps['super_admin'] = is_multisite() && is_super_admin($user_id);
1733
+ }
1734
  }
1735
  return $allcaps;
1736
  }
2142
  'ame-helper-style',
2143
  plugins_url('css/admin.css', $this->plugin_file),
2144
  array(),
2145
+ '20140630-3'
2146
  );
2147
  }
2148
 
2194
  * @return string
2195
  */
2196
  private function get_formatted_security_log() {
2197
+ $log = '<div style="font: 12px/17px Consolas, monospace; margin-bottom: 1em;">';
2198
  $log .= implode("<br>\n", $this->security_log);
2199
  $log .= '</div>';
2200
  return $log;
2292
  return $name;
2293
  }
2294
 
2295
+ /**
2296
+ * Compatibility fix for bbPress 2.5.3.
2297
+ *
2298
+ * bbPress creates a bunch of "hidden" menu items in the admin_menu action only to remove them
2299
+ * later in an admin_head hook. This results in apparently duplicated menus showing up when AME is
2300
+ * active because AME processes the items before they get removed.
2301
+ *
2302
+ * This method works around the issue by explicitly removing those bbPress menus.
2303
+ *
2304
+ * @uses $this->default_wp_submenu
2305
+ */
2306
+ private function apply_bbpress_compat_fix() {
2307
+ if ( !isset($this->default_wp_submenu, $this->default_wp_submenu['index.php']) ) {
2308
+ return;
2309
+ }
2310
+
2311
+ //Note to self: This would be easier if we could rely on anonymous function support being available.
2312
+ //Then we could just array_filter() the submenu with a closure as the callback.
2313
+ $items_to_remove = array('bbp-about' => null, 'bbp-credits' => null);
2314
+ foreach($this->default_wp_submenu['index.php'] as $index => $menu) {
2315
+ if ( array_key_exists($menu[2], $items_to_remove) ) {
2316
+ $items_to_remove[$menu[2]] = $index;
2317
+ }
2318
+ }
2319
+
2320
+ foreach($items_to_remove as $index) {
2321
+ if ( isset($index, $this->default_wp_submenu['index.php'][$index]) ) {
2322
+ unset($this->default_wp_submenu['index.php'][$index]);
2323
+ }
2324
+ }
2325
+ }
2326
+
2327
+ /**
2328
+ * Compatibility fix for WordPress Mu Domain Mapping 0.5.4.3.
2329
+ *
2330
+ * The aforementioned domain mapping plugin has a bug that makes the plugins_url() function
2331
+ * return incorrect URLs for plugins installed in /mu-plugins. Fixed by removing the offending
2332
+ * filter callback.
2333
+ *
2334
+ * Note that this won't break domain mapping. Domain Mapping adds two 'plugins_url' filters.
2335
+ * The buggy one is completely redundant and can be removed with no ill effects.
2336
+ */
2337
+ private function apply_wpmu_domain_mapping_fix() {
2338
+ $priority = has_filter('plugins_url', 'domain_mapping_plugins_uri');
2339
+ if ( ($priority !== false) && (has_filter('plugins_url', 'domain_mapping_post_content') !== false) ) {
2340
+ remove_filter('plugins_url', 'domain_mapping_plugins_uri', $priority);
2341
+ }
2342
+ }
2343
+
2344
  /**
2345
  * Tell new users how to access the plugin settings page.
2346
  */
includes/menu-item.php CHANGED
@@ -8,6 +8,9 @@
8
  * currently registered hooks and the presence of specific files in admin/plugin folders.
9
  */
10
  abstract class ameMenuItem {
 
 
 
11
  /**
12
  * @var array A partial list of files in /wp-admin/. Correct as of WP 3.8-RC1, 2013.12.04.
13
  * When trying to determine if a menu links to one of the default WP admin pages, it's faster
@@ -34,14 +37,15 @@ abstract class ameMenuItem {
34
  */
35
  public static function fromWpItem($item, $position = 0, $parent = '') {
36
  static $separator_count = 0;
 
37
  $item = array(
38
  'menu_title' => $item[0],
39
  'access_level' => $item[1], //= required capability
40
  'file' => $item[2],
41
  'page_title' => (isset($item[3]) ? $item[3] : ''),
42
- 'css_class' => (isset($item[4]) ? $item[4] : 'menu-top'),
43
  'hookname' => (isset($item[5]) ? $item[5] : ''), //Used as the ID attr. of the generated HTML tag.
44
- 'icon_url' => (isset($item[6]) ? $item[6] : 'images/generic.png'),
45
  'position' => $position,
46
  'parent' => $parent,
47
  );
@@ -94,7 +98,7 @@ abstract class ameMenuItem {
94
  //Fields that apply only to top level menus.
95
  'css_class' => 'menu-top',
96
  'hookname' => '',
97
- 'icon_url' => 'images/generic.png',
98
  'separator' => false,
99
  'colors' => false,
100
 
@@ -137,10 +141,11 @@ abstract class ameMenuItem {
137
  return array(
138
  'menu_title' => 'Custom Menu',
139
  'access_level' => 'read',
 
140
  'page_title' => '',
141
  'css_class' => 'menu-top',
142
  'hookname' => '',
143
- 'icon_url' => 'images/generic.png',
144
  'open_in' => 'same_window',
145
  'is_plugin_page' => false,
146
  'page_heading' => '',
8
  * currently registered hooks and the presence of specific files in admin/plugin folders.
9
  */
10
  abstract class ameMenuItem {
11
+ const unclickableTemplateId = '>special:none';
12
+ const unclickableTemplateClass = 'ame-unclickable-menu-item';
13
+
14
  /**
15
  * @var array A partial list of files in /wp-admin/. Correct as of WP 3.8-RC1, 2013.12.04.
16
  * When trying to determine if a menu links to one of the default WP admin pages, it's faster
37
  */
38
  public static function fromWpItem($item, $position = 0, $parent = '') {
39
  static $separator_count = 0;
40
+ $default_css_class = empty($parent) ? 'menu-top' : '';
41
  $item = array(
42
  'menu_title' => $item[0],
43
  'access_level' => $item[1], //= required capability
44
  'file' => $item[2],
45
  'page_title' => (isset($item[3]) ? $item[3] : ''),
46
+ 'css_class' => (isset($item[4]) ? $item[4] : $default_css_class),
47
  'hookname' => (isset($item[5]) ? $item[5] : ''), //Used as the ID attr. of the generated HTML tag.
48
+ 'icon_url' => (isset($item[6]) ? $item[6] : 'dashicons-admin-generic'),
49
  'position' => $position,
50
  'parent' => $parent,
51
  );
98
  //Fields that apply only to top level menus.
99
  'css_class' => 'menu-top',
100
  'hookname' => '',
101
+ 'icon_url' => 'dashicons-admin-generic',
102
  'separator' => false,
103
  'colors' => false,
104
 
141
  return array(
142
  'menu_title' => 'Custom Menu',
143
  'access_level' => 'read',
144
+ 'extra_capability' => '',
145
  'page_title' => '',
146
  'css_class' => 'menu-top',
147
  'hookname' => '',
148
+ 'icon_url' => 'dashicons-admin-generic',
149
  'open_in' => 'same_window',
150
  'is_plugin_page' => false,
151
  'page_heading' => '',
includes/menu.php CHANGED
@@ -198,6 +198,28 @@ abstract class ameMenu {
198
 
199
  return false;
200
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  }
202
 
203
 
198
 
199
  return false;
200
  }
201
+
202
+ /**
203
+ * Recursively filter a list of menu items and remove items flagged as missing.
204
+ *
205
+ * @param array $items An array of menu items to filter.
206
+ * @return array
207
+ */
208
+ public static function remove_missing_items($items) {
209
+ $items = array_filter($items, array(__CLASS__, 'is_not_missing'));
210
+
211
+ foreach($items as &$item) {
212
+ if ( !empty($item['items']) ) {
213
+ $item['items'] = self::remove_missing_items($item['items']);
214
+ }
215
+ }
216
+
217
+ return $items;
218
+ }
219
+
220
+ protected static function is_not_missing($item) {
221
+ return empty($item['missing']);
222
+ }
223
  }
224
 
225
 
includes/settings-page.php CHANGED
@@ -148,6 +148,62 @@ $isProVersion = apply_filters('admin_menu_editor_is_pro', false);
148
  </td>
149
  </tr>
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  <tr>
152
  <th scope="row">Debugging</th>
153
  <td>
148
  </td>
149
  </tr>
150
 
151
+ <tr>
152
+ <th scope="row">Editor colour scheme</th>
153
+ <td>
154
+ <fieldset>
155
+ <p>
156
+ <label>
157
+ <input type="radio" name="ui_colour_scheme" value="classic"
158
+ <?php checked('classic', $settings['ui_colour_scheme']); ?>>
159
+ Blue and yellow
160
+ </label>
161
+ </p>
162
+
163
+ <p>
164
+ <label>
165
+ <input type="radio" name="ui_colour_scheme" value="wp-grey"
166
+ <?php checked('wp-grey', $settings['ui_colour_scheme']); ?>>
167
+ Grey
168
+ </label>
169
+ </p>
170
+ </fieldset>
171
+ </td>
172
+ </tr>
173
+
174
+ <?php if ($isProVersion): ?>
175
+ <tr>
176
+ <th scope="row">Show submenu icons</th>
177
+ <td>
178
+ <fieldset id="ame-submenu-icons-settings">
179
+ <p>
180
+ <label>
181
+ <input type="radio" name="submenu_icons_enabled" value="always"
182
+ <?php checked('always', $settings['submenu_icons_enabled']); ?>>
183
+ Always
184
+ </label>
185
+ </p>
186
+
187
+ <p>
188
+ <label>
189
+ <input type="radio" name="submenu_icons_enabled" value="if_custom"
190
+ <?php checked('if_custom', $settings['submenu_icons_enabled']); ?>>
191
+ Only when manually selected
192
+ </label>
193
+ </p>
194
+
195
+ <p>
196
+ <label>
197
+ <input type="radio" name="submenu_icons_enabled" value="never"
198
+ <?php checked('never', $settings['submenu_icons_enabled']); ?>>
199
+ Never
200
+ </label>
201
+ </p>
202
+ </fieldset>
203
+ </td>
204
+ </tr>
205
+ <?php endif; ?>
206
+
207
  <tr>
208
  <th scope="row">Debugging</th>
209
  <td>
js/admin-helpers.js CHANGED
@@ -14,14 +14,25 @@
14
  }
15
 
16
  jQuery(function($) {
 
 
17
  //Menu separators shouldn't be clickable and should have a custom class.
18
- $('#adminmenu')
19
  .find('.ws-submenu-separator')
20
  .closest('a').click(function() {
21
  return false;
22
  })
23
  .closest('li').addClass('ws-submenu-separator-wrap');
24
 
 
 
 
 
 
 
 
 
 
25
  //Replace the original page heading with the custom heading.
26
  if ( customPageHeading ) {
27
  function replaceAdminPageHeading(newText) {
14
  }
15
 
16
  jQuery(function($) {
17
+ var adminMenu = $('#adminmenu');
18
+
19
  //Menu separators shouldn't be clickable and should have a custom class.
20
+ adminMenu
21
  .find('.ws-submenu-separator')
22
  .closest('a').click(function() {
23
  return false;
24
  })
25
  .closest('li').addClass('ws-submenu-separator-wrap');
26
 
27
+ //Menus with the target "< None >" also shouldn't be clickable.
28
+ adminMenu
29
+ .find(
30
+ 'a.menu-top.ame-unclickable-menu-item, ul.wp-submenu > li > a[href^="#ame-unclickable-menu-item"]'
31
+ )
32
+ .click(function() {
33
+ return false;
34
+ });
35
+
36
  //Replace the original page heading with the custom heading.
37
  if ( customPageHeading ) {
38
  function replaceAdminPageHeading(newText) {
js/menu-editor.js CHANGED
@@ -355,12 +355,17 @@ function buildMenuItem(itemData, isTopLevel) {
355
  //the editors themselves are created later, when the user tries to access them
356
  //for the first time).
357
  var contents = [];
 
 
 
 
 
358
  contents.push(
359
  '<div class="ws_item_head">',
360
  itemData.separator ? '' : '<a class="ws_edit_link"> </a><div class="ws_flag_container"> </div>',
361
  '<input type="checkbox" class="ws_actor_access_checkbox">',
362
  '<span class="ws_item_title">',
363
- ((itemData.menu_title != null) ? itemData.menu_title : itemData.defaults.menu_title),
364
  '&nbsp;</span>',
365
 
366
  '</div>',
@@ -434,7 +439,11 @@ var knownMenuFields = {
434
  caption : 'Menu title',
435
  display: function(menuItem, displayValue, input, containerNode) {
436
  //Update the header as well.
437
- containerNode.find('.ws_item_title').html(displayValue);
 
 
 
 
438
  return displayValue;
439
  },
440
  write: function(menuItem, value, input, containerNode) {
@@ -448,17 +457,49 @@ var knownMenuFields = {
448
  type : 'select',
449
  options : (function(){
450
  //Generate name => id mappings for all item templates + the special "Custom" template.
451
- var itemTemplateIds = {};
452
- itemTemplateIds[wsEditorData.customItemTemplate.name] = '';
 
453
  for (var template_id in wsEditorData.itemTemplates) {
454
  if (wsEditorData.itemTemplates.hasOwnProperty(template_id)) {
455
- itemTemplateIds[wsEditorData.itemTemplates[template_id].name] = template_id;
456
  }
457
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
458
  return itemTemplateIds;
459
  })(),
460
 
461
  write: function(menuItem, value, input, containerNode) {
 
 
462
  menuItem.template_id = value;
463
  menuItem.defaults = itemTemplates.getDefaults(menuItem.template_id);
464
  menuItem.custom = (menuItem.template_id == '');
@@ -479,7 +520,10 @@ var knownMenuFields = {
479
  var hasDefaultValue = itemTemplates.hasDefaultValue(menuItem.template_id, fieldName);
480
 
481
  if (isSetToDefault && !hasDefaultValue) {
482
- menuItem[fieldName] = getInputValue(field.find('.ws_field_value'));
 
 
 
483
  }
484
  });
485
  }
@@ -592,11 +636,11 @@ var knownMenuFields = {
592
  caption: 'Open in',
593
  advanced : true,
594
  type : 'select',
595
- options : {
596
- 'Same window or tab' : 'same_window',
597
- 'New window' : 'new_window',
598
- 'Frame' : 'iframe'
599
- },
600
  defaultValue: 'same_window',
601
  visible: false
602
  }),
@@ -614,20 +658,29 @@ var knownMenuFields = {
614
  defaultValue: 'div',
615
  onlyForTopMenus: true,
616
 
617
- display: function(menuItem, displayValue, input) {
618
  //Display the current icon in the selector.
619
  var cssClass = getFieldValue(menuItem, 'css_class', '');
620
  var iconUrl = getFieldValue(menuItem, 'icon_url', '');
621
 
 
 
 
 
 
 
 
 
622
  var selectButton = input.closest('.ws_edit_field').find('.ws_select_icon');
623
  var cssIcon = selectButton.find('.icon16');
624
  var imageIcon = selectButton.find('img');
625
 
626
  var matches = cssClass.match(/\b(ame-)?menu-icon-([^\s]+)\b/);
627
- var dashiconMatches = iconUrl && iconUrl.match('^\s*(dashicons-[a-z0-9\-]+)');
628
 
629
- //Icon URL take precedence over icon class.
630
  if ( iconUrl && iconUrl !== 'none' && iconUrl !== 'div' && !dashiconMatches ) {
 
631
  cssIcon.hide();
632
  imageIcon.prop('src', iconUrl).show();
633
  } else if ( dashiconMatches ) {
@@ -760,12 +813,12 @@ function buildEditboxField(entry, field_name, field_settings){
760
  case 'select':
761
  inputBox = $('<select class="ws_field_value">');
762
  var option = null;
763
- for( var optionTitle in field_settings.options ){
764
- if (!field_settings.options.hasOwnProperty(optionTitle)) {
765
- continue;
766
- }
767
  option = $('<option>')
768
- .val(field_settings.options[optionTitle])
769
  .text(optionTitle);
770
  option.appendTo(inputBox);
771
  }
@@ -840,6 +893,7 @@ function updateActorAccessUi(containerNode) {
840
  if (selectedActor != null) {
841
  var menuItem = containerNode.data('menu_item');
842
  var hasAccess = actorCanAccessMenu(menuItem, selectedActor);
 
843
 
844
  var checkbox = containerNode.find('.ws_actor_access_checkbox');
845
  checkbox.prop('checked', hasAccess);
@@ -870,8 +924,11 @@ function updateActorAccessUi(containerNode) {
870
  }
871
 
872
  containerNode.toggleClass('ws_is_hidden_for_actor', !hasAccess);
 
 
873
  } else {
874
- containerNode.removeClass('ws_is_hidden_for_actor');
 
875
  }
876
  }
877
 
@@ -1001,15 +1058,36 @@ function encodeMenuAsJSON(tree){
1001
 
1002
  function readMenuTreeState(){
1003
  var tree = {};
1004
- var menu_position = 0;
 
1005
 
1006
  //Gather all menus and their items
1007
  $('#ws_menu_box').find('.ws_menu').each(function() {
1008
- var menu = readItemState(this, menu_position++);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1009
 
1010
- //Attach the current menu to the main struct
1011
- var filename = (menu.file !== null)?menu.file:menu.defaults.file;
1012
  tree[filename] = menu;
 
1013
  });
1014
 
1015
  return {
@@ -1108,8 +1186,9 @@ function readAllFields(container){
1108
 
1109
  var item_flags = {
1110
  'custom':'This is a custom menu item',
1111
- 'unused':'This item was automatically (re)inserted into your custom menu because it is present in the default WordPress menu',
1112
- 'hidden':'This item is hidden'
 
1113
  };
1114
 
1115
  function setMenuFlag(item, flag, state) {
@@ -1156,6 +1235,13 @@ function actorCanAccessMenu(menuItem, actor) {
1156
  return actorHasAccess;
1157
  }
1158
 
 
 
 
 
 
 
 
1159
  function setActorAccess(containerNode, actor, allowAccess) {
1160
  var menuItem = containerNode.data('menu_item');
1161
 
@@ -1195,13 +1281,9 @@ function setSelectedActor(actor) {
1195
  editorNode.toggleClass('ws_is_actor_view', (selectedActor != null));
1196
 
1197
  //Update the menu item states to indicate whether they're accessible.
1198
- if (selectedActor != null) {
1199
- editorNode.find('.ws_container').each(function() {
1200
- updateActorAccessUi($(this));
1201
- });
1202
- } else {
1203
- editorNode.find('.ws_is_hidden_for_actor').removeClass('ws_is_hidden_for_actor');
1204
- }
1205
  }
1206
 
1207
  /**
@@ -1224,11 +1306,14 @@ function denyAccessForAllExcept(menuItem, actor) {
1224
 
1225
  $.each(wsEditorData.actors, function(otherActor) {
1226
  //If the input actor is more or equally specific...
1227
- if (AmeCapabilityManager.compareActorSpecificity(actor, otherActor) >= 0) {
1228
  menuItem.grant_access[otherActor] = false;
1229
  }
1230
  });
1231
- menuItem.grant_access[actor] = true;
 
 
 
1232
  return menuItem;
1233
  }
1234
 
@@ -1248,6 +1333,10 @@ $(document).ready(function(){
1248
  knownMenuFields['page_heading'].visible = true;
1249
  knownMenuFields['colors'].visible = true;
1250
  knownMenuFields['extra_capability'].visible = false; //Superseded by the "access_level" field.
 
 
 
 
1251
  $('.ws_hide_if_pro').hide();
1252
  }
1253
 
@@ -2086,18 +2175,121 @@ $(document).ready(function(){
2086
  });
2087
  });
2088
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2089
  //Delete menu
2090
  $('#ws_delete_menu').click(function () {
2091
  //Get the selected menu
2092
  var selection = getSelectedMenu();
2093
  if (!selection.length) return;
2094
 
2095
- if (confirm('Delete this menu?')){
2096
- //Delete the submenu first
2097
- $('#' + selection.data('submenu_id')).remove();
2098
- //Delete the menu
2099
- selection.remove();
2100
- }
2101
  });
2102
 
2103
  //Copy menu
@@ -2137,7 +2329,7 @@ $(document).ready(function(){
2137
  menu.css_class = 'menu-top';
2138
  }
2139
  if (getFieldValue(menu, 'icon_url', '') == '') {
2140
- menu.icon_url = 'images/generic.png';
2141
  }
2142
  if (getFieldValue(menu, 'hookname', '') == '') {
2143
  menu.hookname = randomMenuId();
@@ -2258,16 +2450,10 @@ $(document).ready(function(){
2258
 
2259
  //Delete item
2260
  $('#ws_delete_item').click(function () {
2261
- //Get the selected menu
2262
  var selection = getSelectedSubmenuItem();
2263
  if (!selection.length) return;
2264
 
2265
- if (confirm('Delete this menu item?')){
2266
- var submenu = selection.parent();
2267
- //Delete the item
2268
- selection.remove();
2269
- updateParentAccessUi(submenu);
2270
- }
2271
  });
2272
 
2273
  //Copy item
@@ -2420,7 +2606,23 @@ $(document).ready(function(){
2420
 
2421
  //Save Changes - encode the current menu as JSON and save
2422
  $('#ws_save_menu').click(function () {
2423
- var tree = readMenuTreeState();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2424
 
2425
  function findItemByTemplateId(items, templateId) {
2426
  var foundItem = null;
@@ -2457,8 +2659,6 @@ $(document).ready(function(){
2457
  }
2458
  }
2459
 
2460
- console.log(tree);
2461
- //return;
2462
  var data = encodeMenuAsJSON(tree);
2463
  $('#ws_data').val(data);
2464
  $('#ws_data_length').val(data.length);
@@ -2490,16 +2690,27 @@ $(document).ready(function(){
2490
 
2491
  $('#ws_export_menu').click(function(){
2492
  var button = $(this);
2493
- button.attr('disabled', 'disabled');
2494
  button.val('Exporting...');
2495
 
2496
  $('#export_complete_notice, #download_menu_button').hide();
2497
  $('#export_progress_notice').show();
2498
- $('#export_dialog').dialog('open');
2499
-
2500
- //Encode and store the menu for download
2501
- var exportData = encodeMenuAsJSON();
 
 
 
 
 
 
 
 
 
 
2502
 
 
2503
  $.post(
2504
  wsEditorData.adminAjaxUrl,
2505
  {
@@ -2509,10 +2720,10 @@ $(document).ready(function(){
2509
  },
2510
  function(data){
2511
  button.val('Export');
2512
- button.removeAttr('disabled');
2513
 
2514
  if ( typeof data['error'] != 'undefined' ){
2515
- $('#export_dialog').dialog('close');
2516
  alert(data.error);
2517
  }
2518
 
355
  //the editors themselves are created later, when the user tries to access them
356
  //for the first time).
357
  var contents = [];
358
+ var menuTitle = ((itemData.menu_title != null) ? itemData.menu_title : itemData.defaults.menu_title);
359
+ if (menuTitle === '') {
360
+ menuTitle = '&nbsp;';
361
+ }
362
+
363
  contents.push(
364
  '<div class="ws_item_head">',
365
  itemData.separator ? '' : '<a class="ws_edit_link"> </a><div class="ws_flag_container"> </div>',
366
  '<input type="checkbox" class="ws_actor_access_checkbox">',
367
  '<span class="ws_item_title">',
368
+ menuTitle,
369
  '&nbsp;</span>',
370
 
371
  '</div>',
439
  caption : 'Menu title',
440
  display: function(menuItem, displayValue, input, containerNode) {
441
  //Update the header as well.
442
+ var itemTitle = displayValue;
443
+ if (itemTitle === '') {
444
+ itemTitle = '&nbsp;';
445
+ }
446
+ containerNode.find('.ws_item_title').html(itemTitle);
447
  return displayValue;
448
  },
449
  write: function(menuItem, value, input, containerNode) {
457
  type : 'select',
458
  options : (function(){
459
  //Generate name => id mappings for all item templates + the special "Custom" template.
460
+ var itemTemplateIds = [];
461
+ itemTemplateIds.push([wsEditorData.customItemTemplate.name, '']);
462
+
463
  for (var template_id in wsEditorData.itemTemplates) {
464
  if (wsEditorData.itemTemplates.hasOwnProperty(template_id)) {
465
+ itemTemplateIds.push([wsEditorData.itemTemplates[template_id].name, template_id]);
466
  }
467
  }
468
+
469
+ itemTemplateIds.sort(function(a, b) {
470
+ if (a[1] === b[1]) {
471
+ return 0;
472
+ }
473
+
474
+ //The "Custom" item is always first.
475
+ if (a[1] === '') {
476
+ return -1;
477
+ } else if (b[1] === '') {
478
+ return 1;
479
+ }
480
+
481
+ //Top-level items go before submenus.
482
+ var aIsTop = (a[1].charAt(0) === '>') ? 1 : 0;
483
+ var bIsTop = (b[1].charAt(0) === '>') ? 1 : 0;
484
+ if (aIsTop !== bIsTop) {
485
+ return bIsTop - aIsTop;
486
+ }
487
+
488
+ //Everything else is sorted by name, in alphabetical order.
489
+ if (a[0] > b[0]) {
490
+ return 1;
491
+ } else if (a[0] < b[0]) {
492
+ return -1;
493
+ }
494
+ return 0;
495
+ });
496
+
497
  return itemTemplateIds;
498
  })(),
499
 
500
  write: function(menuItem, value, input, containerNode) {
501
+ var oldTemplateId = menuItem.template_id;
502
+
503
  menuItem.template_id = value;
504
  menuItem.defaults = itemTemplates.getDefaults(menuItem.template_id);
505
  menuItem.custom = (menuItem.template_id == '');
520
  var hasDefaultValue = itemTemplates.hasDefaultValue(menuItem.template_id, fieldName);
521
 
522
  if (isSetToDefault && !hasDefaultValue) {
523
+ var oldDefaultValue = itemTemplates.getDefaultValue(oldTemplateId, fieldName);
524
+ if (oldDefaultValue !== null) {
525
+ menuItem[fieldName] = oldDefaultValue;
526
+ }
527
  }
528
  });
529
  }
636
  caption: 'Open in',
637
  advanced : true,
638
  type : 'select',
639
+ options : [
640
+ ['Same window or tab', 'same_window'],
641
+ ['New window', 'new_window'],
642
+ ['Frame', 'iframe']
643
+ ],
644
  defaultValue: 'same_window',
645
  visible: false
646
  }),
658
  defaultValue: 'div',
659
  onlyForTopMenus: true,
660
 
661
+ display: function(menuItem, displayValue, input, containerNode) {
662
  //Display the current icon in the selector.
663
  var cssClass = getFieldValue(menuItem, 'css_class', '');
664
  var iconUrl = getFieldValue(menuItem, 'icon_url', '');
665
 
666
+ //When submenu icon visibility is set to "only if manually selected",
667
+ //don't show the default submenu icons.
668
+ var isDefault = (typeof menuItem['icon_url'] === 'undefined') || (menuItem['icon_url'] === null);
669
+ if (isDefault && (wsEditorData.submenuIconsEnabled === 'if_custom') && containerNode.hasClass('ws_item')) {
670
+ iconUrl = 'none';
671
+ cssClass = '';
672
+ }
673
+
674
  var selectButton = input.closest('.ws_edit_field').find('.ws_select_icon');
675
  var cssIcon = selectButton.find('.icon16');
676
  var imageIcon = selectButton.find('img');
677
 
678
  var matches = cssClass.match(/\b(ame-)?menu-icon-([^\s]+)\b/);
679
+ var dashiconMatches = iconUrl && iconUrl.match(/^\s*(dashicons-[a-z0-9\-]+)/);
680
 
681
+ //Icon URL takes precedence over icon class.
682
  if ( iconUrl && iconUrl !== 'none' && iconUrl !== 'div' && !dashiconMatches ) {
683
+ //Regular image icon.
684
  cssIcon.hide();
685
  imageIcon.prop('src', iconUrl).show();
686
  } else if ( dashiconMatches ) {
813
  case 'select':
814
  inputBox = $('<select class="ws_field_value">');
815
  var option = null;
816
+ for( var index = 0; index < field_settings.options.length; index++ ){
817
+ var optionTitle = field_settings.options[index][0];
818
+ var optionValue = field_settings.options[index][1];
819
+
820
  option = $('<option>')
821
+ .val(optionValue)
822
  .text(optionTitle);
823
  option.appendTo(inputBox);
824
  }
893
  if (selectedActor != null) {
894
  var menuItem = containerNode.data('menu_item');
895
  var hasAccess = actorCanAccessMenu(menuItem, selectedActor);
896
+ var hasCustomPermissions = actorHasCustomPermissions(menuItem, selectedActor);
897
 
898
  var checkbox = containerNode.find('.ws_actor_access_checkbox');
899
  checkbox.prop('checked', hasAccess);
924
  }
925
 
926
  containerNode.toggleClass('ws_is_hidden_for_actor', !hasAccess);
927
+ containerNode.toggleClass('ws_has_custom_permissions_for_actor', hasCustomPermissions);
928
+ setMenuFlag(containerNode, 'custom_actor_permissions', hasCustomPermissions)
929
  } else {
930
+ containerNode.removeClass('ws_is_hidden_for_actor ws_has_custom_permissions_for_actor');
931
+ setMenuFlag(containerNode, 'custom_actor_permissions', false);
932
  }
933
  }
934
 
1058
 
1059
  function readMenuTreeState(){
1060
  var tree = {};
1061
+ var menuPosition = 0;
1062
+ var itemsByFilename = {};
1063
 
1064
  //Gather all menus and their items
1065
  $('#ws_menu_box').find('.ws_menu').each(function() {
1066
+ var containerNode = this;
1067
+ var menu = readItemState(containerNode, menuPosition++);
1068
+
1069
+ //Attach the current menu to the main structure.
1070
+ var filename = (menu.file !== null) ? menu.file : menu.defaults.file;
1071
+
1072
+ //Give unclickable items unique keys.
1073
+ if (menu.template_id === wsEditorData.unclickableTemplateId) {
1074
+ ws_paste_count++;
1075
+ filename = '#' + wsEditorData.unclickableTemplateClass + '-' + ws_paste_count;
1076
+ }
1077
+
1078
+ //Prevent the user from saving top level items with duplicate URLs.
1079
+ //WordPress indexes the submenu array by parent URL and AME uses a {url : menu_data} hashtable internally.
1080
+ //Duplicate URLs would cause problems for both.
1081
+ if (itemsByFilename.hasOwnProperty(filename)) {
1082
+ throw {
1083
+ code: 'duplicate_top_level_url',
1084
+ message: 'Error: Found a duplicate URL! All top level menus must have unique URLs.',
1085
+ duplicates: [itemsByFilename[filename], containerNode]
1086
+ }
1087
+ }
1088
 
 
 
1089
  tree[filename] = menu;
1090
+ itemsByFilename[filename] = containerNode;
1091
  });
1092
 
1093
  return {
1186
 
1187
  var item_flags = {
1188
  'custom':'This is a custom menu item',
1189
+ 'unused':'This item was automatically recreated. You cannot delete a non-custom item, but you could hide it.',
1190
+ 'hidden':'This item is hidden from ALL roles and users',
1191
+ 'custom_actor_permissions' : "The selected role has custom permissions for this item."
1192
  };
1193
 
1194
  function setMenuFlag(item, flag, state) {
1235
  return actorHasAccess;
1236
  }
1237
 
1238
+ function actorHasCustomPermissions(menuItem, actor) {
1239
+ if (menuItem.grant_access && menuItem.grant_access.hasOwnProperty && menuItem.grant_access.hasOwnProperty(actor)) {
1240
+ return (menuItem.grant_access[actor] !== null);
1241
+ }
1242
+ return false;
1243
+ }
1244
+
1245
  function setActorAccess(containerNode, actor, allowAccess) {
1246
  var menuItem = containerNode.data('menu_item');
1247
 
1281
  editorNode.toggleClass('ws_is_actor_view', (selectedActor != null));
1282
 
1283
  //Update the menu item states to indicate whether they're accessible.
1284
+ editorNode.find('.ws_container').each(function() {
1285
+ updateActorAccessUi($(this));
1286
+ });
 
 
 
 
1287
  }
1288
 
1289
  /**
1306
 
1307
  $.each(wsEditorData.actors, function(otherActor) {
1308
  //If the input actor is more or equally specific...
1309
+ if ((actor === null) || (AmeCapabilityManager.compareActorSpecificity(actor, otherActor) >= 0)) {
1310
  menuItem.grant_access[otherActor] = false;
1311
  }
1312
  });
1313
+
1314
+ if (actor !== null) {
1315
+ menuItem.grant_access[actor] = true;
1316
+ }
1317
  return menuItem;
1318
  }
1319
 
1333
  knownMenuFields['page_heading'].visible = true;
1334
  knownMenuFields['colors'].visible = true;
1335
  knownMenuFields['extra_capability'].visible = false; //Superseded by the "access_level" field.
1336
+
1337
+ //The Pro version supports submenu icons, but they can be disabled by the user.
1338
+ knownMenuFields['icon_url'].onlyForTopMenus = (wsEditorData.submenuIconsEnabled == 'never');
1339
+
1340
  $('.ws_hide_if_pro').hide();
1341
  }
1342
 
2175
  });
2176
  });
2177
 
2178
+ //Delete error dialog. It shows up when the user tries to delete one of the default menus.
2179
+ var menuDeletionDialog = $('#ws-ame-menu-deletion-error').dialog({
2180
+ autoOpen: false,
2181
+ modal: true,
2182
+ closeText: ' ',
2183
+ title: 'Error',
2184
+ draggable: false
2185
+ });
2186
+ var menuDeletionCallback = function(hide) {
2187
+ menuDeletionDialog.dialog('close');
2188
+ var selection = menuDeletionDialog.data('selected_menu');
2189
+
2190
+ function hideRecursively(containerNode, exceptActor) {
2191
+ denyAccessForAllExcept(containerNode.data('menu_item'), exceptActor);
2192
+
2193
+ var subMenuId = containerNode.data('submenu_id');
2194
+ if (subMenuId && containerNode.hasClass('ws_menu')) {
2195
+ $('.ws_item', '#' + subMenuId).each(function() {
2196
+ var node = $(this);
2197
+ denyAccessForAllExcept(node.data('menu_item'), exceptActor);
2198
+ updateItemEditor(node);
2199
+ });
2200
+ }
2201
+
2202
+ updateItemEditor(containerNode);
2203
+ updateParentAccessUi(containerNode);
2204
+ }
2205
+
2206
+ if (hide === 'all') {
2207
+ hideRecursively(selection, null);
2208
+ } else if (hide === 'except_current_user') {
2209
+ hideRecursively(selection, 'user:' + wsEditorData.currentUserLogin);
2210
+ }
2211
+ };
2212
+
2213
+ //Callbacks for each of the dialog buttons.
2214
+ $('#ws_cancel_menu_deletion').click(function() {
2215
+ menuDeletionCallback(false);
2216
+ });
2217
+ $('#ws_hide_menu_from_everyone').click(function() {
2218
+ menuDeletionCallback('all');
2219
+ });
2220
+ $('#ws_hide_menu_except_current_user').click(function() {
2221
+ menuDeletionCallback('except_current_user');
2222
+ });
2223
+
2224
+ /**
2225
+ * Attempt to delete a menu item. Will check if the item can actually be deleted and ask the user for confirmation.
2226
+ * UI callback.
2227
+ *
2228
+ * @param selection The selected menu item (DOM node).
2229
+ */
2230
+ function tryDeleteItem(selection) {
2231
+ var menuItem = selection.data('menu_item');
2232
+ var isDefaultItem =
2233
+ ( menuItem.template_id !== '')
2234
+ && ( menuItem.template_id !== wsEditorData.unclickableTemplateId)
2235
+ && (!menuItem.separator);
2236
+
2237
+ var otherCopiesExist = false;
2238
+ var shouldDelete = false;
2239
+
2240
+ if (isDefaultItem) {
2241
+ //Check if there are any other menus with the same template ID.
2242
+ $('#ws_menu_editor').find('.ws_container').each(function() {
2243
+ var otherItem = $(this).data('menu_item');
2244
+ if ((menuItem != otherItem) && (menuItem.template_id == otherItem.template_id)) {
2245
+ otherCopiesExist = true;
2246
+ return false;
2247
+ }
2248
+ return true;
2249
+ });
2250
+ }
2251
+
2252
+ if (!isDefaultItem || otherCopiesExist || !wsEditorData.wsMenuEditorPro) {
2253
+ //Custom and duplicate items can be deleted normally. The free version doesn't get the dialog
2254
+ //because it doesn't have role-specific permissions.
2255
+ shouldDelete = confirm('Delete this menu?');
2256
+ } else {
2257
+ //Non-custom items can not be deleted, but they can be hidden. Ask the user if they want to do that.
2258
+ menuDeletionDialog.find('#ws-ame-menu-type-desc').text(
2259
+ menuItem.defaults.is_plugin_page ? 'an item added by another plugin' : 'a built-in menu item'
2260
+ );
2261
+ menuDeletionDialog.data('selected_menu', selection);
2262
+ menuDeletionDialog.dialog('open');
2263
+
2264
+ //Select "Cancel" as the default button.
2265
+ menuDeletionDialog.find('#ws_cancel_menu_deletion').focus();
2266
+ }
2267
+
2268
+ if (shouldDelete) {
2269
+ //Delete this menu's submenu first, if any.
2270
+ var submenuId = selection.data('submenu_id');
2271
+ if (submenuId) {
2272
+ $('#' + submenuId).remove();
2273
+ }
2274
+ var parentSubmenu = selection.closest('.ws_submenu');
2275
+
2276
+ //Delete the menu.
2277
+ selection.remove();
2278
+
2279
+ if (parentSubmenu) {
2280
+ //Refresh permissions UI for this menu's parent (if any).
2281
+ updateParentAccessUi(parentSubmenu);
2282
+ }
2283
+ }
2284
+ }
2285
+
2286
  //Delete menu
2287
  $('#ws_delete_menu').click(function () {
2288
  //Get the selected menu
2289
  var selection = getSelectedMenu();
2290
  if (!selection.length) return;
2291
 
2292
+ tryDeleteItem(selection);
 
 
 
 
 
2293
  });
2294
 
2295
  //Copy menu
2329
  menu.css_class = 'menu-top';
2330
  }
2331
  if (getFieldValue(menu, 'icon_url', '') == '') {
2332
+ menu.icon_url = 'dashicons-admin-generic';
2333
  }
2334
  if (getFieldValue(menu, 'hookname', '') == '') {
2335
  menu.hookname = randomMenuId();
2450
 
2451
  //Delete item
2452
  $('#ws_delete_item').click(function () {
 
2453
  var selection = getSelectedSubmenuItem();
2454
  if (!selection.length) return;
2455
 
2456
+ tryDeleteItem(selection);
 
 
 
 
 
2457
  });
2458
 
2459
  //Copy item
2606
 
2607
  //Save Changes - encode the current menu as JSON and save
2608
  $('#ws_save_menu').click(function () {
2609
+ try {
2610
+ var tree = readMenuTreeState();
2611
+ } catch (error) {
2612
+ //Right now the only known error condition is duplicate top level URLs.
2613
+ if (error.hasOwnProperty('code') && (error.code === 'duplicate_top_level_url')) {
2614
+ var message = 'Error: Duplicate menu URLs. The following top level menus have the same URL:\n\n' ;
2615
+ for (var i = 0; i < error.duplicates.length; i++) {
2616
+ var containerNode = $(error.duplicates[i]);
2617
+ message += (i + 1) + '. ' + containerNode.find('.ws_item_title').first().text() + '\n';
2618
+ }
2619
+ message += '\nPlease change the URLs to be unique or delete the duplicates.';
2620
+ alert(message);
2621
+ } else {
2622
+ alert(error.message);
2623
+ }
2624
+ return;
2625
+ }
2626
 
2627
  function findItemByTemplateId(items, templateId) {
2628
  var foundItem = null;
2659
  }
2660
  }
2661
 
 
 
2662
  var data = encodeMenuAsJSON(tree);
2663
  $('#ws_data').val(data);
2664
  $('#ws_data_length').val(data.length);
2690
 
2691
  $('#ws_export_menu').click(function(){
2692
  var button = $(this);
2693
+ button.prop('disabled', true);
2694
  button.val('Exporting...');
2695
 
2696
  $('#export_complete_notice, #download_menu_button').hide();
2697
  $('#export_progress_notice').show();
2698
+ var exportDialog = $('#export_dialog');
2699
+ exportDialog.dialog('open');
2700
+
2701
+ //Encode the menu.
2702
+ try {
2703
+ var exportData = encodeMenuAsJSON();
2704
+ } catch (error) {
2705
+ exportDialog.dialog('close');
2706
+ alert(error.message);
2707
+
2708
+ button.val('Export');
2709
+ button.prop('disabled', false);
2710
+ return;
2711
+ }
2712
 
2713
+ //Store the menu for download.
2714
  $.post(
2715
  wsEditorData.adminAjaxUrl,
2716
  {
2720
  },
2721
  function(data){
2722
  button.val('Export');
2723
+ button.prop('disabled', false);
2724
 
2725
  if ( typeof data['error'] != 'undefined' ){
2726
+ exportDialog.dialog('close');
2727
  alert(data.error);
2728
  }
2729
 
js/menu-highlight-fix.js CHANGED
@@ -51,6 +51,14 @@ jQuery(function($) {
51
  currentUri.path = currentUri.path + 'index.php';
52
  }
53
 
 
 
 
 
 
 
 
 
54
  var adminMenu = $('#adminmenu');
55
  adminMenu.find('li > a').each(function(index, link) {
56
  var $link = $(link);
@@ -63,8 +71,7 @@ jQuery(function($) {
63
 
64
  var uri = parseUri(link.href);
65
 
66
- //Special case: if post_type is not specified for edit.php and post-new.php,
67
- //WordPress assumes it is "post". Here we make this explicit.
68
  if ( (uri.file === 'edit.php') || (uri.file === 'post-new.php') ) {
69
  if ( !uri.queryKey.hasOwnProperty('post_type') ) {
70
  uri.queryKey['post_type'] = 'post';
51
  currentUri.path = currentUri.path + 'index.php';
52
  }
53
 
54
+ //Special case: if post_type is not specified for edit.php and post-new.php,
55
+ //WordPress assumes it is "post". Here we make this explicit.
56
+ if ( (currentUri.file === 'edit.php') || (currentUri.file === 'post-new.php') ) {
57
+ if ( !currentUri.queryKey.hasOwnProperty('post_type') ) {
58
+ currentUri.queryKey['post_type'] = 'post';
59
+ }
60
+ }
61
+
62
  var adminMenu = $('#adminmenu');
63
  adminMenu.find('li > a').each(function(index, link) {
64
  var $link = $(link);
71
 
72
  var uri = parseUri(link.href);
73
 
74
+ //Same as above - use "post" as the default post type.
 
75
  if ( (uri.file === 'edit.php') || (uri.file === 'post-new.php') ) {
76
  if ( !uri.queryKey.hasOwnProperty('post_type') ) {
77
  uri.queryKey['post_type'] = 'post';
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.3.2
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.4
7
  Author: Janis Elsts
8
  Author URI: http://w-shadow.com/blog/
9
  */
readme.txt CHANGED
@@ -2,30 +2,31 @@
2
  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.5
6
- Tested up to: 4.0-alpha
7
- Stable tag: 1.3.2
8
 
9
  Lets you edit the WordPress admin menu. You can re-order, hide or rename menus, add custom menus and more.
10
 
11
  == Description ==
12
- Admin Menu Editor lets you manually edit the Dashboard menu. You can reorder the menus, show/hide specific items, change access rights, and more.
13
 
14
  **Features**
15
 
16
- * Edit menu title, access rights, URL, icon and so on.
17
- * Sort menu items via drag & drop.
18
- * Hide/show any menu or menu item. A hidden menu is invisible to all users, including administrators.
19
  * Move a menu item to a different submenu.
20
  * Create custom menus that point to any part of the Dashboard or an external URL.
 
21
 
22
  The [Pro version](http://w-shadow.com/AdminMenuEditor/) lets you set per-role menu permissions, hide a menu from everyone except a specific user, export your admin menu, drag items between menu levels, make menus open in a new window and more. [Try online demo](http://amedemo.com/wpdemo/demo.php).
23
 
24
  **Notes**
25
 
26
- * If you delete any of the default menus they will reappear after saving. This is by design. To get rid of a menu for good, either hide it or set it's access rights to a higher level.
27
- * In the free version, you can't lower a menu's required access rights, but you can change them to be more restrictive.
28
- * In case of emergency, you can reset the menu configuration back to the default by going to http://example.com/wp-admin/?reset\_admin\_menu=1
29
 
30
  == Installation ==
31
 
@@ -40,7 +41,7 @@ That's it. You can access the the menu editor by going to *Settings -> Menu Edit
40
 
41
  **WP MultiSite installation**
42
 
43
- If you have WordPress set up in multisite ("Network") mode, you can also install Admin Menu Editor as a global plugin. This will enable you to edit the Dashboard menu for all sites and users at once.
44
 
45
  1. Download the admin-menu-editor.zip file to your computer.
46
  1. Unzip the file.
@@ -53,7 +54,6 @@ Plugins installed in the `mu-plugins` directory are treated as "always on", so y
53
  *Notes*
54
  * Instead of installing Admin Menu Editor in `mu-plugins`, you can also install it normally and then activate it globally via "Network Activate". However, this will make the plugin visible to normal users when it is inactive (e.g. during upgrades).
55
  * When Admin Menu Editor is installed in `mu-plugins` or activated via "Network Activate", only the "super admin" user can access the menu editor page. Other users will see the customized Dashboard menu, but be unable to edit it.
56
- * It is currently not possible to install Admin Menu Editor as both a normal and global plugin on the same site.
57
 
58
  == Screenshots ==
59
 
@@ -63,6 +63,22 @@ Plugins installed in the `mu-plugins` directory are treated as "always on", so y
63
 
64
  == Changelog ==
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  = 1.3.2 =
67
  * Added a large number of menu icons based on the Dashicons icon font.
68
  * Fixed default menu icons not showing up in WP 3.9.
2
  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.8
6
+ Tested up to: 4.0-beta2
7
+ Stable tag: 1.4
8
 
9
  Lets you edit the WordPress admin menu. You can re-order, hide or rename menus, add custom menus and more.
10
 
11
  == Description ==
12
+ Admin Menu Editor lets you manually edit the Dashboard menu. You can reorder the menus, show/hide specific items, change premissions, and more.
13
 
14
  **Features**
15
 
16
+ * Change menu titles, URLs, icons, CSS classes and so on.
17
+ * Organize menu items via drag & drop.
18
+ * Change menu permissions by setting the required capability or role.
19
  * Move a menu item to a different submenu.
20
  * Create custom menus that point to any part of the Dashboard or an external URL.
21
+ * Hide/show any menu or menu item. A hidden menu is invisible to all users, including administrators.
22
 
23
  The [Pro version](http://w-shadow.com/AdminMenuEditor/) lets you set per-role menu permissions, hide a menu from everyone except a specific user, export your admin menu, drag items between menu levels, make menus open in a new window and more. [Try online demo](http://amedemo.com/wpdemo/demo.php).
24
 
25
  **Notes**
26
 
27
+ * If you delete any of the default menus they will reappear after saving. This is by design. To get rid of a menu for good, either hide it or change it's access permissions.
28
+ * In the free version, it's not possible to give a role access to a menu item that it couldn't see before. You can only restrict menu access further.
29
+ * In case of emergency, you can reset the menu configuration back to the default by going to http://example.com/wp-admin/?reset\_admin\_menu=1 (replace example.com with your site URL). You must be logged in as an Administrator to do this.
30
 
31
  == Installation ==
32
 
41
 
42
  **WP MultiSite installation**
43
 
44
+ If you have WordPress set up in Multisite ("Network") mode, you can also install Admin Menu Editor as a global plugin. This will enable you to edit the Dashboard menu for all sites and users at once.
45
 
46
  1. Download the admin-menu-editor.zip file to your computer.
47
  1. Unzip the file.
54
  *Notes*
55
  * Instead of installing Admin Menu Editor in `mu-plugins`, you can also install it normally and then activate it globally via "Network Activate". However, this will make the plugin visible to normal users when it is inactive (e.g. during upgrades).
56
  * When Admin Menu Editor is installed in `mu-plugins` or activated via "Network Activate", only the "super admin" user can access the menu editor page. Other users will see the customized Dashboard menu, but be unable to edit it.
 
57
 
58
  == Screenshots ==
59
 
63
 
64
  == Changelog ==
65
 
66
+ = 1.4 =
67
+ * Added a special target page option: "< None >". It makes the selected menu item unclickable. This could be useful for creating menu headers and so on.
68
+ * Added a new menu editor colour scheme that's similar to the default WordPress admin colour scheme. Click the "Settings" button next to the menu editor page title to switch colour schemes.
69
+ * Fixed strange boxes showing up in the icon selector in Internet Explorer.
70
+ * Fixed duplicate top level menus mysteriously disappearing. Now the plugin will properly warn the user that all top level menus must have unique URLs.
71
+ * Fixed an obscure bug where changing the "Target page" from the default setting to "Custom" and back would occasionally make some menu properties suddenly show up as modified for no apparent reason.
72
+ * Fixed incorrect submenu item height and margins in WP 4.0-beta.
73
+ * Fixed a minor layout bug where items with no title would be smaller than other items.
74
+ * Fixed combo-box dropdown button height for WP 3.9.x.
75
+ * Added a workaround for a bug in WordPress Mu Domain Mapping 0.5.4.3.
76
+ * Added a workaround for the very unusual situation where the "user_has_cap" filter is called without a capability.
77
+ * Fixed duplicates of bbPress menu items showing up.
78
+ * Changed the default custom menu icon to the generic "cogwheel" icon from Dashicons.
79
+ * Other small UI changes.
80
+ * Raised minimum requirements to WordPress 3.8 or later. This is mainly due to the increased reliance on Dashicons as menu icons.
81
+
82
  = 1.3.2 =
83
  * Added a large number of menu icons based on the Dashicons icon font.
84
  * Fixed default menu icons not showing up in WP 3.9.
screenshot-1.png CHANGED
Binary file
screenshot-2.png CHANGED
Binary file