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