Admin Menu Editor - Version 1.8.8

Version Description

  • Added the ability to edit more plugin details like author name, site URL and version number. Note that this feature only changes how plugins are displayed. It doesn't actually modify plugin files.
  • Fixed a PHP deprecation notice: "strpos(): Non-string needles will be interpreted as strings in the future". Hopefully this time it's been fixed for good.
  • Fixed a couple of HTML validation errors.
  • Fixed an inefficiency where the plugin would reinitialise the media frame every time the user tried to select an image from the media library.
  • Added a partial workaround for situations where menu icons that were more than 32 pixels wide would be displayed incorrectly.
  • Tested up to WP 5.1.1.
Download this release

Release Info

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

Code changes from version 1.8.7 to 1.8.8

css/admin.css CHANGED
@@ -91,6 +91,7 @@ hr.ws-submenu-separator {
91
92
#adminmenu .ame-submenu-icon img {
93
padding-top: 2px;
94
95
opacity: 0.6;
96
filter: alpha(opacity=60);
91
92
#adminmenu .ame-submenu-icon img {
93
padding-top: 2px;
94
+ max-width: 32px;
95
96
opacity: 0.6;
97
filter: alpha(opacity=60);
includes/shadow_plugin_framework.php CHANGED
@@ -318,10 +318,18 @@ class MenuEd_ShadowPluginFramework {
318
$normalizedFileName = realpath($filename);
319
320
//If realpath() fails, just normalize the syntax instead.
321
- if ( empty($normalizedFileName) || empty($normalizedFileName) ) {
322
$normalizedMuPluginDir = wp_normalize_path(WPMU_PLUGIN_DIR);
323
$normalizedFileName = wp_normalize_path($filename);
324
}
325
return (strpos( $normalizedFileName, $normalizedMuPluginDir ) !== false);
326
}
327
318
$normalizedFileName = realpath($filename);
319
320
//If realpath() fails, just normalize the syntax instead.
321
+ if ( empty($normalizedFileName) || empty($normalizedMuPluginDir) ) {
322
$normalizedMuPluginDir = wp_normalize_path(WPMU_PLUGIN_DIR);
323
$normalizedFileName = wp_normalize_path($filename);
324
}
325
+ //Yet another fallback if the above also fails.
326
+ if ( !is_string($normalizedMuPluginDir) || empty($normalizedMuPluginDir) ) {
327
+ if ( is_string(WPMU_PLUGIN_DIR) ) {
328
+ $normalizedMuPluginDir = WPMU_PLUGIN_DIR;
329
+ } else {
330
+ return false;
331
+ }
332
+ }
333
return (strpos( $normalizedFileName, $normalizedMuPluginDir ) !== false);
334
}
335
js/actor-manager.js CHANGED
@@ -6,7 +6,7 @@ var __extends = (this && this.__extends) || (function () {
6
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
7
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
8
return extendStatics(d, b);
9
- }
10
return function (d, b) {
11
extendStatics(d, b);
12
function __() { this.constructor = d; }
@@ -502,4 +502,3 @@ if (typeof wsAmeActorData !== 'undefined') {
502
AmeActors.generateCapabilitySuggestions(wsAmeActorData['capPower']);
503
}
504
}
505
- //# sourceMappingURL=actor-manager.js.map
6
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
7
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
8
return extendStatics(d, b);
9
+ };
10
return function (d, b) {
11
extendStatics(d, b);
12
function __() { this.constructor = d; }
502
AmeActors.generateCapabilitySuggestions(wsAmeActorData['capPower']);
503
}
504
}
js/menu-editor.js CHANGED
@@ -1074,7 +1074,7 @@ function buildEditboxField(entry, field_name, field_settings){
1074
case 'icon_selector':
1075
//noinspection HtmlUnknownTag
1076
inputBox = $(basicTextField)
1077
- .add('<button class="button ws_select_icon" title="Select icon"><div class="icon16 icon-settings"></div><img src="" style="display:none;"></button>');
1078
break;
1079
1080
case 'color_scheme_editor':
@@ -1129,7 +1129,7 @@ function buildEditboxField(entry, field_name, field_settings){
1129
1130
editField
1131
.append(
1132
- $('<img class="ws_reset_button" title="Reset to default value" src="">')
1133
.attr('src', wsEditorData.imagesUrl + '/transparent16.png')
1134
).data('field_name', field_name);
1135
@@ -2877,9 +2877,9 @@ function ameOnDomReady() {
2877
2878
//Alternatively, use the WordPress media uploader to select a custom icon.
2879
//This code is based on the header selection script in /wp-admin/js/custom-header.js.
2880
$('#ws_choose_icon_from_media').click(function(event) {
2881
event.preventDefault();
2882
- var frame = null;
2883
2884
//This option is not usable on the demo site since the filesystem is usually read-only.
2885
if (wsEditorData.isDemoMode) {
@@ -2888,13 +2888,13 @@ function ameOnDomReady() {
2888
}
2889
2890
//If the media frame already exists, reopen it.
2891
- if ( frame ) {
2892
- frame.open();
2893
return;
2894
}
2895
2896
//Create a custom media frame.
2897
- frame = wp.media.frames.customAdminMenuIcon = wp.media({
2898
//Set the title of the modal.
2899
title: 'Choose a Custom Icon (20x20)',
2900
@@ -2911,9 +2911,9 @@ function ameOnDomReady() {
2911
});
2912
2913
//When an image is selected, set it as the menu icon.
2914
- frame.on( 'select', function() {
2915
//Grab the selected attachment.
2916
- var attachment = frame.state().get('selection').first();
2917
//TODO: Warn the user if the image exceeds 20x20 pixels.
2918
2919
//Set the menu icon to the attachment URL.
@@ -2935,11 +2935,11 @@ function ameOnDomReady() {
2935
});
2936
2937
//If the user closes the frame by via Esc or the "X" button, clear up state.
2938
- frame.on('escape', function(){
2939
currentIconButton = null;
2940
});
2941
2942
- frame.open();
2943
iconSelector.hide();
2944
});
2945
@@ -4321,6 +4321,13 @@ function ameOnDomReady() {
4321
}
4322
});
4323
4324
$('#ws_toggle_editor_layout').click(function () {
4325
var isCompactLayoutEnabled = menuEditorNode.toggleClass('ws_compact_layout').hasClass('ws_compact_layout');
4326
$.cookie('ame-compact-layout', isCompactLayoutEnabled ? '1' : '0', {expires: 90});
@@ -4938,7 +4945,7 @@ function ameOnDomReady() {
4938
},
4939
method: 'post',
4940
dataType: 'json',
4941
- success: function(response, textStatus) {
4942
if (!response) {
4943
alert('Error: Could not parse the server response.');
4944
testAccessButton.prop('disabled', false);
1074
case 'icon_selector':
1075
//noinspection HtmlUnknownTag
1076
inputBox = $(basicTextField)
1077
+ .add('<button class="button ws_select_icon" title="Select icon"><div class="icon16 icon-settings"></div><img src="" style="display:none;" alt="Icon"></button>');
1078
break;
1079
1080
case 'color_scheme_editor':
1129
1130
editField
1131
.append(
1132
+ $('<img class="ws_reset_button" title="Reset to default value" src="" alt="Reset">')
1133
.attr('src', wsEditorData.imagesUrl + '/transparent16.png')
1134
).data('field_name', field_name);
1135
2877
2878
//Alternatively, use the WordPress media uploader to select a custom icon.
2879
//This code is based on the header selection script in /wp-admin/js/custom-header.js.
2880
+ var mediaFrame = null;
2881
$('#ws_choose_icon_from_media').click(function(event) {
2882
event.preventDefault();
2883
2884
//This option is not usable on the demo site since the filesystem is usually read-only.
2885
if (wsEditorData.isDemoMode) {
2888
}
2889
2890
//If the media frame already exists, reopen it.
2891
+ if ( mediaFrame !== null ) {
2892
+ mediaFrame.open();
2893
return;
2894
}
2895
2896
//Create a custom media frame.
2897
+ mediaFrame = wp.media.frames.customAdminMenuIcon = wp.media({
2898
//Set the title of the modal.
2899
title: 'Choose a Custom Icon (20x20)',
2900
2911
});
2912
2913
//When an image is selected, set it as the menu icon.
2914
+ mediaFrame.on( 'select', function() {
2915
//Grab the selected attachment.
2916
+ var attachment = mediaFrame.state().get('selection').first();
2917
//TODO: Warn the user if the image exceeds 20x20 pixels.
2918
2919
//Set the menu icon to the attachment URL.
2935
});
2936
2937
//If the user closes the frame by via Esc or the "X" button, clear up state.
2938
+ mediaFrame.on('escape', function(){
2939
currentIconButton = null;
2940
});
2941
2942
+ mediaFrame.open();
2943
iconSelector.hide();
2944
});
2945
4321
}
4322
});
4323
4324
+ //Enable the "load default menu" and "undo changes" buttons only when "All" is selected.
4325
+ //Otherwise some users incorrectly assume these buttons only affect the currently selected role or user.
4326
+ actorSelectorWidget.onChange(function (newSelectedActor) {
4327
+ $('#ws_load_menu, #ws_reset_menu').prop('disabled', newSelectedActor !== null);
4328
+ });
4329
+ $('#ws_load_menu, #ws_reset_menu').prop('disabled', actorSelectorWidget.selectedActor !== null);
4330
+
4331
$('#ws_toggle_editor_layout').click(function () {
4332
var isCompactLayoutEnabled = menuEditorNode.toggleClass('ws_compact_layout').hasClass('ws_compact_layout');
4333
$.cookie('ame-compact-layout', isCompactLayoutEnabled ? '1' : '0', {expires: 90});
4945
},
4946
method: 'post',
4947
dataType: 'json',
4948
+ success: function(response) {
4949
if (!response) {
4950
alert('Error: Could not parse the server response.');
4951
testAccessButton.prop('disabled', false);
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.8.7
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.8.8
7
Author: Janis Elsts
8
Author URI: http://w-shadow.com/blog/
9
*/
modules/actor-selector/actor-selector.js CHANGED
@@ -208,4 +208,3 @@ var AmeActorSelector = /** @class */ (function () {
208
AmeActorSelector._ = wsAmeLodash;
209
return AmeActorSelector;
210
}());
211
- //# sourceMappingURL=actor-selector.js.map
208
AmeActorSelector._ = wsAmeLodash;
209
return AmeActorSelector;
210
}());
modules/plugin-visibility/plugin-visibility-template.php CHANGED
@@ -75,10 +75,33 @@
75
<tr class="inline-edit-row" data-bind="if: isBeingEdited">
76
<td class="colspanchange" colspan="3">
77
<fieldset class="ame-pv-inline-edit-left">
78
<div class="inline-edit-col">
79
<label>
80
<span class="title">Name</span>
81
- <input type="text" data-bind="value: editableName" class="ame-pv-custom-name">
82
</label>
83
</div>
84
</fieldset>
@@ -88,7 +111,7 @@
88
<span class="title">Description</span>
89
<textarea name="plugin-description" cols="30" rows="5"
90
class="ame-pv-custom-description"
91
- data-bind="value: editableDescription"></textarea>
92
</label>
93
</div>
94
</fieldset>
75
<tr class="inline-edit-row" data-bind="if: isBeingEdited">
76
<td class="colspanchange" colspan="3">
77
<fieldset class="ame-pv-inline-edit-left">
78
+ <legend class="inline-edit-legend" data-bind="text: defaultProperties['name']">
79
+ Edit Plugin Properties
80
+ </legend>
81
<div class="inline-edit-col">
82
<label>
83
<span class="title">Name</span>
84
+ <span class="input-text-wrap">
85
+ <input type="text" data-bind="value: editableProperties['name']" class="ame-pv-custom-name">
86
+ </span>
87
+ </label>
88
+ <label>
89
+ <span class="title">Author</span>
90
+ <span class="input-text-wrap">
91
+ <input type="text" data-bind="value: editableProperties['author']" class="ame-pv-custom-author">
92
+ </span>
93
+ </label>
94
+ <label>
95
+ <span class="title">Site URL</span>
96
+ <span class="input-text-wrap">
97
+ <input type="text" data-bind="value: editableProperties['siteUrl']" class="ame-pv-custom-site-url">
98
+ </span>
99
+ </label>
100
+ <label>
101
+ <span class="title">Version</span>
102
+ <span class="input-text-wrap">
103
+ <input type="text" data-bind="value: editableProperties['version']" class="ame-pv-custom-version-number">
104
+ </span>
105
</label>
106
</div>
107
</fieldset>
111
<span class="title">Description</span>
112
<textarea name="plugin-description" cols="30" rows="5"
113
class="ame-pv-custom-description"
114
+ data-bind="value: editableProperties['description']"></textarea>
115
</label>
116
</div>
117
</fieldset>
modules/plugin-visibility/plugin-visibility.css CHANGED
@@ -45,13 +45,15 @@
45
Inline editor
46
*/
47
#ame-plugin-visibility-editor .inline-edit-row .ame-pv-inline-edit-left {
48
- width: 30%; }
49
#ame-plugin-visibility-editor .inline-edit-row .ame-pv-inline-edit-right {
50
- width: 70%; }
51
#ame-plugin-visibility-editor .inline-edit-row span.title {
52
width: auto; }
53
#ame-plugin-visibility-editor .inline-edit-row input[type="text"] {
54
width: 100%; }
55
#ame-plugin-visibility-editor .inline-edit-row .ame-pv-inline-reset {
56
line-height: 28px;
57
margin-left: 1em; }
@@ -59,5 +61,10 @@
59
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1); }
60
#ame-plugin-visibility-editor .inline-edit-row p.submit {
61
margin-bottom: 0.2em; }
62
63
/*# sourceMappingURL=plugin-visibility.css.map */
45
Inline editor
46
*/
47
#ame-plugin-visibility-editor .inline-edit-row .ame-pv-inline-edit-left {
48
+ width: 35%; }
49
#ame-plugin-visibility-editor .inline-edit-row .ame-pv-inline-edit-right {
50
+ width: 65%; }
51
#ame-plugin-visibility-editor .inline-edit-row span.title {
52
width: auto; }
53
#ame-plugin-visibility-editor .inline-edit-row input[type="text"] {
54
width: 100%; }
55
+ #ame-plugin-visibility-editor .inline-edit-row .ame-pv-custom-description {
56
+ height: 9em; }
57
#ame-plugin-visibility-editor .inline-edit-row .ame-pv-inline-reset {
58
line-height: 28px;
59
margin-left: 1em; }
61
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1); }
62
#ame-plugin-visibility-editor .inline-edit-row p.submit {
63
margin-bottom: 0.2em; }
64
+ #ame-plugin-visibility-editor .inline-edit-row .inline-edit-legend {
65
+ margin: 0;
66
+ padding: 0.2em 0.5em 0;
67
+ line-height: 2.5;
68
+ font-weight: 600; }
69
70
/*# sourceMappingURL=plugin-visibility.css.map */
modules/plugin-visibility/plugin-visibility.js CHANGED
@@ -137,10 +137,12 @@ var AmePluginVisibilityModule = /** @class */ (function () {
137
isVisibleByDefault: plugin.isVisibleByDefault(),
138
grantAccess: _.mapValues(plugin.grantAccess, function (allow) {
139
return allow();
140
- }),
141
- customName: plugin.customName(),
142
- customDescription: plugin.customDescription()
143
};
144
});
145
return result;
146
};
@@ -162,30 +164,33 @@ var AmePluginVisibilityModule = /** @class */ (function () {
162
var AmePlugin = /** @class */ (function () {
163
function AmePlugin(details, settings, module) {
164
var _this = this;
165
var _ = AmePluginVisibilityModule._;
166
- this.defaultName = ko.observable(details.name);
167
- this.defaultDescription = ko.observable(details.description);
168
- this.customName = ko.observable(_.get(settings, 'customName', ''));
169
- this.customDescription = ko.observable(_.get(settings, 'customDescription', ''));
170
this.name = ko.computed(function () {
171
- var value = _this.customName();
172
if (value === '') {
173
- value = _this.defaultName();
174
}
175
return AmePlugin.stripAllTags(value);
176
});
177
this.description = ko.computed(function () {
178
- var value = _this.customDescription();
179
if (value === '') {
180
- value = _this.defaultDescription();
181
}
182
return AmePlugin.stripAllTags(value);
183
});
184
this.fileName = details.fileName;
185
this.isActive = details.isActive;
186
this.isBeingEdited = ko.observable(false);
187
- this.editableName = ko.observable(this.defaultName());
188
- this.editableDescription = ko.observable(this.defaultDescription());
189
this.isVisibleByDefault = ko.observable(_.get(settings, 'isVisibleByDefault', true));
190
var emptyGrant = {};
191
this.grantAccess = _.mapValues(_.get(settings, 'grantAccess', emptyGrant), function (hasAccess) {
@@ -209,8 +214,10 @@ var AmePlugin = /** @class */ (function () {
209
};
210
//noinspection JSUnusedGlobalSymbols Used in KO template.
211
AmePlugin.prototype.openInlineEditor = function () {
212
- this.editableName(this.customName() === '' ? this.defaultName() : this.customName());
213
- this.editableDescription(this.customDescription() === '' ? this.defaultDescription() : this.customDescription());
214
this.isBeingEdited(true);
215
};
216
//noinspection JSUnusedGlobalSymbols Used in KO template.
@@ -219,20 +226,21 @@ var AmePlugin = /** @class */ (function () {
219
};
220
//noinspection JSUnusedGlobalSymbols Used in KO template.
221
AmePlugin.prototype.confirmEdit = function () {
222
- this.customName(this.editableName());
223
- this.customDescription(this.editableDescription());
224
- if (this.customName() === this.defaultName()) {
225
- this.customName('');
226
- }
227
- if (this.customDescription() === this.defaultDescription()) {
228
- this.customDescription('');
229
}
230
this.isBeingEdited(false);
231
};
232
//noinspection JSUnusedGlobalSymbols Used in KO template.
233
AmePlugin.prototype.resetNameAndDescription = function () {
234
- this.customName('');
235
- this.customDescription('');
236
this.isBeingEdited(false);
237
};
238
AmePlugin.stripAllTags = function (input) {
@@ -240,6 +248,7 @@ var AmePlugin = /** @class */ (function () {
240
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
241
return input.replace(commentsAndPhpTags, '').replace(tags, '');
242
};
243
return AmePlugin;
244
}());
245
jQuery(function ($) {
@@ -250,4 +259,3 @@ jQuery(function ($) {
250
AjawV1.getAction('ws_ame_dismiss_pv_usage_notice').request();
251
});
252
});
253
- //# sourceMappingURL=plugin-visibility.js.map
137
isVisibleByDefault: plugin.isVisibleByDefault(),
138
grantAccess: _.mapValues(plugin.grantAccess, function (allow) {
139
return allow();
140
+ })
141
};
142
+ for (var i = 0; i < AmePlugin.editablePropertyNames.length; i++) {
143
+ var key = AmePlugin.editablePropertyNames[i], upperKey = key.substring(0, 1).toUpperCase() + key.substring(1);
144
+ result.plugins[plugin.fileName]['custom' + upperKey] = plugin.customProperties[key]();
145
+ }
146
});
147
return result;
148
};
164
var AmePlugin = /** @class */ (function () {
165
function AmePlugin(details, settings, module) {
166
var _this = this;
167
+ this.defaultProperties = {};
168
+ this.customProperties = {};
169
+ this.editableProperties = {};
170
var _ = AmePluginVisibilityModule._;
171
+ for (var i = 0; i < AmePlugin.editablePropertyNames.length; i++) {
172
+ var key = AmePlugin.editablePropertyNames[i], upperKey = key.substring(0, 1).toUpperCase() + key.substring(1);
173
+ this.defaultProperties[key] = ko.observable(_.get(details, key, ''));
174
+ this.customProperties[key] = ko.observable(_.get(settings, 'custom' + upperKey, ''));
175
+ this.editableProperties[key] = ko.observable(this.defaultProperties[key]());
176
+ }
177
this.name = ko.computed(function () {
178
+ var value = _this.customProperties['name']();
179
if (value === '') {
180
+ value = _this.defaultProperties['name']();
181
}
182
return AmePlugin.stripAllTags(value);
183
});
184
this.description = ko.computed(function () {
185
+ var value = _this.customProperties['description']();
186
if (value === '') {
187
+ value = _this.defaultProperties['description']();
188
}
189
return AmePlugin.stripAllTags(value);
190
});
191
this.fileName = details.fileName;
192
this.isActive = details.isActive;
193
this.isBeingEdited = ko.observable(false);
194
this.isVisibleByDefault = ko.observable(_.get(settings, 'isVisibleByDefault', true));
195
var emptyGrant = {};
196
this.grantAccess = _.mapValues(_.get(settings, 'grantAccess', emptyGrant), function (hasAccess) {
214
};
215
//noinspection JSUnusedGlobalSymbols Used in KO template.
216
AmePlugin.prototype.openInlineEditor = function () {
217
+ for (var i = 0; i < AmePlugin.editablePropertyNames.length; i++) {
218
+ var key = AmePlugin.editablePropertyNames[i], customValue = this.customProperties[key]();
219
+ this.editableProperties[key](customValue === '' ? this.defaultProperties[key]() : customValue);
220
+ }
221
this.isBeingEdited(true);
222
};
223
//noinspection JSUnusedGlobalSymbols Used in KO template.
226
};
227
//noinspection JSUnusedGlobalSymbols Used in KO template.
228
AmePlugin.prototype.confirmEdit = function () {
229
+ for (var i = 0; i < AmePlugin.editablePropertyNames.length; i++) {
230
+ var key = AmePlugin.editablePropertyNames[i], customValue = this.editableProperties[key]();
231
+ if (customValue === this.defaultProperties[key]()) {
232
+ customValue = '';
233
+ }
234
+ this.customProperties[key](customValue);
235
}
236
this.isBeingEdited(false);
237
};
238
//noinspection JSUnusedGlobalSymbols Used in KO template.
239
AmePlugin.prototype.resetNameAndDescription = function () {
240
+ for (var i = 0; i < AmePlugin.editablePropertyNames.length; i++) {
241
+ var key = AmePlugin.editablePropertyNames[i];
242
+ this.customProperties[key]('');
243
+ }
244
this.isBeingEdited(false);
245
};
246
AmePlugin.stripAllTags = function (input) {
248
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
249
return input.replace(commentsAndPhpTags, '').replace(tags, '');
250
};
251
+ AmePlugin.editablePropertyNames = ['name', 'description', 'author', 'siteUrl', 'version'];
252
return AmePlugin;
253
}());
254
jQuery(function ($) {
259
AjawV1.getAction('ws_ame_dismiss_pv_usage_notice').request();
260
});
261
});
modules/plugin-visibility/plugin-visibility.php CHANGED
@@ -162,6 +162,15 @@ class amePluginVisibility extends amePersistentModule {
162
return $plugins;
163
}
164
165
$pluginFileNames = array_keys($plugins);
166
foreach($pluginFileNames as $fileName) {
167
//Remove all hidden plugins.
@@ -170,14 +179,12 @@ class amePluginVisibility extends amePersistentModule {
170
continue;
171
}
172
173
- //Set custom names and descriptions.
174
- $customName = ameUtils::get($settings, array('plugins', $fileName, 'customName'), '');
175
- $customDescription = ameUtils::get($settings, array('plugins', $fileName, 'customDescription'), '');
176
- if ( $customName !== '' ) {
177
- $plugins[$fileName]['Name'] = $customName;
178
- }
179
- if ( $customDescription !== '' ) {
180
- $plugins[$fileName]['Description'] = $customDescription;
181
}
182
}
183
@@ -324,12 +331,13 @@ class amePluginVisibility extends amePersistentModule {
324
325
$plugins[] = array(
326
'fileName' => $pluginFile,
327
- 'name' => $header['Name'],
328
- 'description' => isset($header['Description']) ? $header['Description'] : '',
329
'isActive' => $isActive || $isActiveForNetwork,
330
331
- 'customName' => '',
332
- 'customDescription' => '',
333
);
334
}
335
162
return $plugins;
163
}
164
165
+ $editableProperties = array(
166
+ 'Name' => 'name',
167
+ 'Description' => 'description',
168
+ 'Author' => 'author',
169
+ 'PluginURI' => 'siteUrl',
170
+ 'AuthorURI' => 'siteUrl',
171
+ 'Version' => 'version',
172
+ );
173
+
174
$pluginFileNames = array_keys($plugins);
175
foreach($pluginFileNames as $fileName) {
176
//Remove all hidden plugins.
179
continue;
180
}
181
182
+ //Set custom names, descriptions, and other properties.
183
+ foreach($editableProperties as $header => $property) {
184
+ $customValue = ameUtils::get($settings, array('plugins', $fileName, 'custom' . ucfirst($property)), '');
185
+ if ( $customValue !== '' ) {
186
+ $plugins[$fileName][$header] = $customValue;
187
+ }
188
}
189
}
190
331
332
$plugins[] = array(
333
'fileName' => $pluginFile,
334
'isActive' => $isActive || $isActiveForNetwork,
335
336
+ 'name' => $header['Name'],
337
+ 'description' => isset($header['Description']) ? $header['Description'] : '',
338
+ 'author' => isset($header['Author']) ? $header['Author'] : '',
339
+ 'siteUrl' => isset($header['PluginURI']) ? $header['PluginURI'] : '',
340
+ 'version' => isset($header['Version']) ? $header['Version'] : '',
341
);
342
}
343
modules/plugin-visibility/plugin-visibility.scss CHANGED
@@ -58,10 +58,10 @@
58
59
#ame-plugin-visibility-editor .inline-edit-row {
60
.ame-pv-inline-edit-left {
61
- width: 30%;
62
}
63
.ame-pv-inline-edit-right {
64
- width: 70%;
65
}
66
67
span.title {
@@ -71,6 +71,10 @@
71
width: 100%;
72
}
73
74
.ame-pv-inline-reset {
75
line-height: 28px;
76
margin-left: 1em;
@@ -83,4 +87,11 @@
83
p.submit {
84
margin-bottom: 0.2em;
85
}
86
}
58
59
#ame-plugin-visibility-editor .inline-edit-row {
60
.ame-pv-inline-edit-left {
61
+ width: 35%;
62
}
63
.ame-pv-inline-edit-right {
64
+ width: 65%;
65
}
66
67
span.title {
71
width: 100%;
72
}
73
74
+ .ame-pv-custom-description {
75
+ height: 9em;
76
+ }
77
+
78
.ame-pv-inline-reset {
79
line-height: 28px;
80
margin-left: 1em;
87
p.submit {
88
margin-bottom: 0.2em;
89
}
90
+
91
+ .inline-edit-legend {
92
+ margin: 0;
93
+ padding: 0.2em 0.5em 0;
94
+ line-height: 2.5;
95
+ font-weight: 600;
96
+ }
97
}
modules/plugin-visibility/plugin-visibility.ts CHANGED
@@ -23,8 +23,11 @@ interface PluginVisibilitySettings {
23
[fileName : string] : {
24
isVisibleByDefault: boolean,
25
grantAccess: GrantAccessMap,
26
- customName: string,
27
- customDescription: string
28
}
29
}
30
}
@@ -35,21 +38,22 @@ interface GrantAccessMap {
35
36
interface PvPluginInfo {
37
name: string,
38
- fileName: string,
39
description: string,
40
- isActive: boolean,
41
42
- customName: string,
43
- customDescription: string
44
}
45
46
class AmePluginVisibilityModule {
47
static _ = wsAmeLodash;
48
49
plugins: Array<AmePlugin>;
50
- private canRoleManagePlugins: {[roleId: string] : boolean};
51
grantAccessByDefault: {[actorId: string] : KnockoutObservable<boolean>};
52
- private isMultisite: boolean;
53
54
actorSelector: AmeActorSelector;
55
selectedActor: KnockoutComputed<string>;
@@ -61,7 +65,7 @@ class AmePluginVisibilityModule {
61
* Actors that don't lose access to a plugin when you uncheck it in the "All" view.
62
* This is a convenience feature that lets the user quickly hide a bunch of plugins from everyone else.
63
*/
64
- private privilegedActors: Array<IAmeActor>;
65
66
constructor(scriptData: PluginVisibilityScriptData) {
67
const _ = AmePluginVisibilityModule._;
@@ -209,10 +213,14 @@ class AmePluginVisibilityModule {
209
isVisibleByDefault: plugin.isVisibleByDefault(),
210
grantAccess: _.mapValues(plugin.grantAccess, (allow): boolean => {
211
return allow();
212
- }),
213
- customName: plugin.customName(),
214
- customDescription: plugin.customDescription()
215
};
216
});
217
218
return result;
@@ -236,21 +244,23 @@ class AmePluginVisibilityModule {
236
}
237
}
238
239
class AmePlugin {
240
name: KnockoutComputed<string>;
241
fileName: string;
242
description: KnockoutComputed<string>;
243
isActive: boolean;
244
245
- defaultName: KnockoutObservable<string>;
246
- defaultDescription: KnockoutObservable<string>;
247
248
- isBeingEdited: KnockoutObservable<boolean>;
249
- customName: KnockoutObservable<string>;
250
- customDescription: KnockoutObservable<string>;
251
- editableName: KnockoutObservable<string>;
252
- editableDescription: KnockoutObservable<string>;
253
254
isChecked: KnockoutComputed<boolean>;
255
256
isVisibleByDefault: KnockoutObservable<boolean>;
@@ -259,22 +269,25 @@ class AmePlugin {
259
constructor(details: PvPluginInfo, settings: Object, module: AmePluginVisibilityModule) {
260
const _ = AmePluginVisibilityModule._;
261
262
- this.defaultName = ko.observable(details.name);
263
- this.defaultDescription = ko.observable(details.description);
264
- this.customName = ko.observable(_.get(settings, 'customName', ''));
265
- this.customDescription = ko.observable(_.get(settings, 'customDescription', ''));
266
267
this.name = ko.computed(() => {
268
- let value = this.customName();
269
if (value === '') {
270
- value = this.defaultName();
271
}
272
return AmePlugin.stripAllTags(value);
273
});
274
this.description = ko.computed(() => {
275
- let value = this.customDescription();
276
if (value === '') {
277
- value = this.defaultDescription();
278
}
279
return AmePlugin.stripAllTags(value);
280
});
@@ -283,12 +296,10 @@ class AmePlugin {
283
this.isActive = details.isActive;
284
285
this.isBeingEdited = ko.observable(false);
286
- this.editableName = ko.observable(this.defaultName());
287
- this.editableDescription = ko.observable(this.defaultDescription());
288
289
this.isVisibleByDefault = ko.observable(_.get(settings, 'isVisibleByDefault', true));
290
291
- const emptyGrant: {[actorId : string] : boolean} = {};
292
this.grantAccess = _.mapValues(_.get(settings, 'grantAccess', emptyGrant), (hasAccess) => {
293
return ko.observable<boolean>(hasAccess);
294
});
@@ -312,8 +323,12 @@ class AmePlugin {
312
313
//noinspection JSUnusedGlobalSymbols Used in KO template.
314
openInlineEditor() {
315
- this.editableName(this.customName() === '' ? this.defaultName() : this.customName());
316
- this.editableDescription(this.customDescription() === '' ? this.defaultDescription() : this.customDescription());
317
this.isBeingEdited(true);
318
}
319
@@ -324,14 +339,13 @@ class AmePlugin {
324
325
//noinspection JSUnusedGlobalSymbols Used in KO template.
326
confirmEdit() {
327
- this.customName(this.editableName());
328
- this.customDescription(this.editableDescription());
329
-
330
- if (this.customName() === this.defaultName()) {
331
- this.customName('');
332
- }
333
- if (this.customDescription() === this.defaultDescription()) {
334
- this.customDescription('');
335
}
336
337
this.isBeingEdited(false);
@@ -339,8 +353,10 @@ class AmePlugin {
339
340
//noinspection JSUnusedGlobalSymbols Used in KO template.
341
resetNameAndDescription() {
342
- this.customName('');
343
- this.customDescription('');
344
this.isBeingEdited(false);
345
}
346
23
[fileName : string] : {
24
isVisibleByDefault: boolean,
25
grantAccess: GrantAccessMap,
26
+ customName?: string,
27
+ customDescription?: string;
28
+ customAuthor?: string;
29
+ customSiteUrl?: string;
30
+ customVersion?: string;
31
}
32
}
33
}
38
39
interface PvPluginInfo {
40
name: string,
41
description: string,
42
+ author: string,
43
+ version: string,
44
+ siteUrl: string,
45
46
+ fileName: string,
47
+ isActive: boolean;
48
}
49
50
class AmePluginVisibilityModule {
51
static _ = wsAmeLodash;
52
53
plugins: Array<AmePlugin>;
54
+ private readonly canRoleManagePlugins: {[roleId: string] : boolean};
55
grantAccessByDefault: {[actorId: string] : KnockoutObservable<boolean>};
56
+ private readonly isMultisite: boolean;
57
58
actorSelector: AmeActorSelector;
59
selectedActor: KnockoutComputed<string>;
65
* Actors that don't lose access to a plugin when you uncheck it in the "All" view.
66
* This is a convenience feature that lets the user quickly hide a bunch of plugins from everyone else.
67
*/
68
+ private readonly privilegedActors: Array<IAmeActor>;
69
70
constructor(scriptData: PluginVisibilityScriptData) {
71
const _ = AmePluginVisibilityModule._;
213
isVisibleByDefault: plugin.isVisibleByDefault(),
214
grantAccess: _.mapValues(plugin.grantAccess, (allow): boolean => {
215
return allow();
216
+ })
217
};
218
+
219
+ for (let i = 0; i < AmePlugin.editablePropertyNames.length; i++) {
220
+ let key = AmePlugin.editablePropertyNames[i],
221
+ upperKey = key.substring(0, 1).toUpperCase() + key.substring(1);
222
+ result.plugins[plugin.fileName]['custom' + upperKey] = plugin.customProperties[key]();
223
+ }
224
});
225
226
return result;
244
}
245
}
246
247
+ interface AmeStringObservableMap {
248
+ [key: string]: KnockoutObservable<string>;
249
+ }
250
+
251
class AmePlugin {
252
name: KnockoutComputed<string>;
253
fileName: string;
254
description: KnockoutComputed<string>;
255
isActive: boolean;
256
257
+ static readonly editablePropertyNames = ['name', 'description', 'author', 'siteUrl', 'version'];
258
259
+ defaultProperties: AmeStringObservableMap = {};
260
+ customProperties: AmeStringObservableMap = {};
261
+ editableProperties: AmeStringObservableMap = {};
262
263
+ isBeingEdited: KnockoutObservable<boolean>;
264
isChecked: KnockoutComputed<boolean>;
265
266
isVisibleByDefault: KnockoutObservable<boolean>;
269
constructor(details: PvPluginInfo, settings: Object, module: AmePluginVisibilityModule) {
270
const _ = AmePluginVisibilityModule._;
271
272
+ for (let i = 0; i < AmePlugin.editablePropertyNames.length; i++) {
273
+ let key = AmePlugin.editablePropertyNames[i],
274
+ upperKey = key.substring(0, 1).toUpperCase() + key.substring(1);
275
+ this.defaultProperties[key] = ko.observable(_.get(details, key, ''));
276
+ this.customProperties[key] = ko.observable(_.get(settings, 'custom' + upperKey, ''));
277
+ this.editableProperties[key] = ko.observable(this.defaultProperties[key]());
278
+ }
279
280
this.name = ko.computed(() => {
281
+ let value = this.customProperties['name']();
282
if (value === '') {
283
+ value = this.defaultProperties['name']();
284
}
285
return AmePlugin.stripAllTags(value);
286
});
287
this.description = ko.computed(() => {
288
+ let value = this.customProperties['description']();
289
if (value === '') {
290
+ value = this.defaultProperties['description']();
291
}
292
return AmePlugin.stripAllTags(value);
293
});
296
this.isActive = details.isActive;
297
298
this.isBeingEdited = ko.observable(false);
299
300
this.isVisibleByDefault = ko.observable(_.get(settings, 'isVisibleByDefault', true));
301
302
+ const emptyGrant: { [actorId: string]: boolean } = {};
303
this.grantAccess = _.mapValues(_.get(settings, 'grantAccess', emptyGrant), (hasAccess) => {
304
return ko.observable<boolean>(hasAccess);
305
});
323
324
//noinspection JSUnusedGlobalSymbols Used in KO template.
325
openInlineEditor() {
326
+ for (let i = 0; i < AmePlugin.editablePropertyNames.length; i++) {
327
+ let key = AmePlugin.editablePropertyNames[i],
328
+ customValue = this.customProperties[key]();
329
+ this.editableProperties[key](customValue === '' ? this.defaultProperties[key]() : customValue);
330
+ }
331
+
332
this.isBeingEdited(true);
333
}
334
339
340
//noinspection JSUnusedGlobalSymbols Used in KO template.
341
confirmEdit() {
342
+ for (let i = 0; i < AmePlugin.editablePropertyNames.length; i++) {
343
+ let key = AmePlugin.editablePropertyNames[i],
344
+ customValue = this.editableProperties[key]();
345
+ if (customValue === this.defaultProperties[key]()) {
346
+ customValue = '';
347
+ }
348
+ this.customProperties[key](customValue);
349
}
350
351
this.isBeingEdited(false);
353
354
//noinspection JSUnusedGlobalSymbols Used in KO template.
355
resetNameAndDescription() {
356
+ for (let i = 0; i < AmePlugin.editablePropertyNames.length; i++) {
357
+ let key = AmePlugin.editablePropertyNames[i];
358
+ this.customProperties[key]('');
359
+ }
360
this.isBeingEdited(false);
361
}
362
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: whiteshadow
3
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A6P9S6CE3SRSW
4
Tags: admin, dashboard, menu, security, wpmu
5
Requires at least: 4.1
6
- Tested up to: 5.0
7
- Stable tag: 1.8.7
8
9
Lets you edit the WordPress admin menu. You can re-order, hide or rename menus, add custom menus and more.
10
@@ -63,6 +63,14 @@ Plugins installed in the `mu-plugins` directory are treated as "always on", so y
63
64
== Changelog ==
65
66
= 1.8.7 =
67
* Fixed a bug introcuded in 1.8.6 that caused a PHP warning "strpos(): Empty needle".
68
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: 4.1
6
+ Tested up to: 5.1
7
+ Stable tag: 1.8.8
8
9
Lets you edit the WordPress admin menu. You can re-order, hide or rename menus, add custom menus and more.
10
63
64
== Changelog ==
65
66
+ = 1.8.8 =
67
+ * Added the ability to edit more plugin details like author name, site URL and version number. Note that this feature only changes how plugins are displayed. It doesn't actually modify plugin files.
68
+ * Fixed a PHP deprecation notice: "strpos(): Non-string needles will be interpreted as strings in the future". Hopefully this time it's been fixed for good.
69
+ * Fixed a couple of HTML validation errors.
70
+ * Fixed an inefficiency where the plugin would reinitialise the media frame every time the user tried to select an image from the media library.
71
+ * Added a partial workaround for situations where menu icons that were more than 32 pixels wide would be displayed incorrectly.
72
+ * Tested up to WP 5.1.1.
73
+
74
= 1.8.7 =
75
* Fixed a bug introcuded in 1.8.6 that caused a PHP warning "strpos(): Empty needle".
76