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