Version Description
- Added capability suggestions and access preview to the "Extra capability" dropdown.
- The plugin now remembers the last selected menu item and re-selects it after you save changes.
- Fixed a layout issue where menus with very long titles would appear incorrectly in the menu editor.
- When you change the menu title, the window title will also be changed to match it. You can still edit the window title separately if necessary.
- Moved the "Icon URL" field up and moved "Window title" down.
Download this release
Release Info
Developer | whiteshadow |
Plugin | Admin Menu Editor |
Version | 1.7.2 |
Comparing to | |
See all releases |
Code changes from version 1.7.1 to 1.7.2
- css/menu-editor.css +24 -0
- css/menu-editor.scss +40 -1
- includes/cap-suggestion-box.php +14 -0
- includes/capabilities/cap-power.csv +54 -0
- includes/editor-page.php +19 -7
- includes/menu-editor-core.php +80 -7
- js/actor-manager.js +92 -0
- js/actor-manager.ts +122 -0
- js/menu-editor.js +392 -71
- menu-editor.php +1 -1
- modules/access-editor/access-editor.js +4 -0
- readme.txt +8 -1
css/menu-editor.css
CHANGED
@@ -1336,6 +1336,30 @@ a#ws-ame-delete-color-preset:hover {
|
|
1336 |
#ws-ame-copy-permissions-dialog select {
|
1337 |
min-width: 280px; }
|
1338 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1339 |
#ws_sidebar_pro_ad {
|
1340 |
min-width: 225px;
|
1341 |
margin-top: 5px;
|
1336 |
#ws-ame-copy-permissions-dialog select {
|
1337 |
min-width: 280px; }
|
1338 |
|
1339 |
+
/*********************************************
|
1340 |
+
Capability suggestions and preview
|
1341 |
+
**********************************************/
|
1342 |
+
#ws_capability_suggestions {
|
1343 |
+
padding: 4px;
|
1344 |
+
width: 350px;
|
1345 |
+
border: 1px solid #cdd5d5;
|
1346 |
+
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
|
1347 |
+
background: #fff;
|
1348 |
+
border-top-right-radius: 3px;
|
1349 |
+
border-bottom-right-radius: 3px; }
|
1350 |
+
#ws_capability_suggestions #ws_previewed_caps {
|
1351 |
+
margin-top: 0;
|
1352 |
+
margin-bottom: 6px; }
|
1353 |
+
#ws_capability_suggestions td, #ws_capability_suggestions th {
|
1354 |
+
padding-top: 3px;
|
1355 |
+
padding-bottom: 3px; }
|
1356 |
+
#ws_capability_suggestions tr.ws_preview_has_access .ws_ame_role_name {
|
1357 |
+
background-color: lightgreen; }
|
1358 |
+
#ws_capability_suggestions .ws_ame_suggested_capability {
|
1359 |
+
cursor: pointer; }
|
1360 |
+
#ws_capability_suggestions .ws_ame_suggested_capability:hover {
|
1361 |
+
background-color: #d0f2d0; }
|
1362 |
+
|
1363 |
#ws_sidebar_pro_ad {
|
1364 |
min-width: 225px;
|
1365 |
margin-top: 5px;
|
css/menu-editor.scss
CHANGED
@@ -692,10 +692,11 @@ select.ws_dropdown {
|
|
692 |
font-size: 12px;
|
693 |
}
|
694 |
|
|
|
695 |
select.ws_dropdown option {
|
696 |
font-family : "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
|
697 |
font-size: 12px;
|
698 |
-
padding:
|
699 |
}
|
700 |
|
701 |
select.ws_dropdown optgroup option {
|
@@ -1878,6 +1879,44 @@ $userSelectionPanelPadding: 10px;
|
|
1878 |
min-width: 280px;
|
1879 |
}
|
1880 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1881 |
|
1882 |
|
1883 |
#ws_sidebar_pro_ad {
|
692 |
font-size: 12px;
|
693 |
}
|
694 |
|
695 |
+
$dropdownOptionPaddingTop: 3px;
|
696 |
select.ws_dropdown option {
|
697 |
font-family : "Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
|
698 |
font-size: 12px;
|
699 |
+
padding: $dropdownOptionPaddingTop;
|
700 |
}
|
701 |
|
702 |
select.ws_dropdown optgroup option {
|
1879 |
min-width: 280px;
|
1880 |
}
|
1881 |
|
1882 |
+
/*********************************************
|
1883 |
+
Capability suggestions and preview
|
1884 |
+
**********************************************/
|
1885 |
+
|
1886 |
+
#ws_capability_suggestions {
|
1887 |
+
padding: 4px;
|
1888 |
+
width: 350px;
|
1889 |
+
|
1890 |
+
border: 1px solid #cdd5d5;
|
1891 |
+
box-shadow: 0 1px 1px rgba(0,0,0,0.04);
|
1892 |
+
background: #fff;
|
1893 |
+
|
1894 |
+
border-top-right-radius: 3px;
|
1895 |
+
border-bottom-right-radius: 3px;
|
1896 |
+
|
1897 |
+
#ws_previewed_caps {
|
1898 |
+
margin-top: 0;
|
1899 |
+
margin-bottom: 6px;
|
1900 |
+
}
|
1901 |
+
|
1902 |
+
td, th {
|
1903 |
+
//For consistency, padding should match the capability dropdown.
|
1904 |
+
padding-top: $dropdownOptionPaddingTop;
|
1905 |
+
padding-bottom: $dropdownOptionPaddingTop;
|
1906 |
+
}
|
1907 |
+
|
1908 |
+
tr.ws_preview_has_access .ws_ame_role_name{
|
1909 |
+
background-color: lightgreen;
|
1910 |
+
}
|
1911 |
+
|
1912 |
+
.ws_ame_suggested_capability {
|
1913 |
+
cursor: pointer;
|
1914 |
+
|
1915 |
+
&:hover {
|
1916 |
+
background-color: #d0f2d0;
|
1917 |
+
}
|
1918 |
+
}
|
1919 |
+
}
|
1920 |
|
1921 |
|
1922 |
#ws_sidebar_pro_ad {
|
includes/cap-suggestion-box.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div id="ws_capability_suggestions" style="display: none;">
|
2 |
+
<p id="ws_previewed_caps"> </p>
|
3 |
+
<table class="widefat striped">
|
4 |
+
<thead>
|
5 |
+
<tr>
|
6 |
+
<th class="ws_ame_role_name">Role</th>
|
7 |
+
<th>Suggestion</th>
|
8 |
+
</tr>
|
9 |
+
</thead>
|
10 |
+
<tbody>
|
11 |
+
<tr><td colspan="2">This table will be populated by JavaScript</td></tr>
|
12 |
+
</tbody>
|
13 |
+
</table>
|
14 |
+
</div>
|
includes/capabilities/cap-power.csv
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Capability;Power;Super Admin;Administrator;Editor;Author;Contributor;Subscriber
|
2 |
+
manage_network;20;Y;;;;;
|
3 |
+
manage_sites;20;Y;;;;;
|
4 |
+
manage_network_users;20;Y;;;;;
|
5 |
+
manage_network_plugins;20;Y;;;;;
|
6 |
+
manage_network_themes;20;Y;;;;;
|
7 |
+
manage_network_options;20;Y;;;;;
|
8 |
+
install_plugins;10;Y;Y (single site);;;;
|
9 |
+
install_themes;10;Y;Y (single site);;;;
|
10 |
+
edit_plugins;10;Y;Y (single site);;;;
|
11 |
+
edit_themes;10;Y;Y (single site);;;;
|
12 |
+
delete_plugins;8;Y;Y (single site);;;;
|
13 |
+
delete_themes;8;Y;Y (single site);;;;
|
14 |
+
update_core;7;Y;Y (single site);;;;
|
15 |
+
update_plugins;7;Y;Y (single site);;;;
|
16 |
+
update_themes;7;Y;Y (single site);;;;
|
17 |
+
create_users;7;Y;Y (single site);;;;
|
18 |
+
delete_users;7;Y;Y (single site);;;;
|
19 |
+
edit_users;7;Y;Y (single site);;;;
|
20 |
+
activate_plugins;6;Y;Y (single site or enabled by network setting);;;;
|
21 |
+
edit_theme_options;5;Y;Y;;;;
|
22 |
+
export;5;Y;Y;;;;
|
23 |
+
import;5;Y;Y;;;;
|
24 |
+
list_users;5;Y;Y;;;;
|
25 |
+
manage_options;5;Y;Y;;;;
|
26 |
+
promote_users;5;Y;Y;;;;
|
27 |
+
remove_users;5;Y;Y;;;;
|
28 |
+
switch_themes;5;Y;Y;;;;
|
29 |
+
moderate_comments;4;Y;Y;Y;;;
|
30 |
+
manage_categories;4;Y;Y;Y;;;
|
31 |
+
manage_links;4;Y;Y;Y;;;
|
32 |
+
edit_others_posts;4;Y;Y;Y;;;
|
33 |
+
edit_pages;4;Y;Y;Y;;;
|
34 |
+
edit_others_pages;4;Y;Y;Y;;;
|
35 |
+
edit_published_pages;4;Y;Y;Y;;;
|
36 |
+
publish_pages;4;Y;Y;Y;;;
|
37 |
+
delete_pages;4;Y;Y;Y;;;
|
38 |
+
delete_others_pages;4;Y;Y;Y;;;
|
39 |
+
delete_published_pages;4;Y;Y;Y;;;
|
40 |
+
delete_others_posts;4;Y;Y;Y;;;
|
41 |
+
delete_private_posts;4;Y;Y;Y;;;
|
42 |
+
edit_private_posts;4;Y;Y;Y;;;
|
43 |
+
read_private_posts;4;Y;Y;Y;;;
|
44 |
+
delete_private_pages;4;Y;Y;Y;;;
|
45 |
+
edit_private_pages;4;Y;Y;Y;;;
|
46 |
+
read_private_pages;4;Y;Y;Y;;;
|
47 |
+
unfiltered_html;4,2;Y;Y;Y;;;
|
48 |
+
edit_published_posts;3;Y;Y;Y;Y;;
|
49 |
+
upload_files;3;Y;Y;Y;Y;;
|
50 |
+
publish_posts;3;Y;Y;Y;Y;;
|
51 |
+
delete_published_posts;3;Y;Y;Y;Y;;
|
52 |
+
edit_posts;2;Y;Y;Y;Y;Y;
|
53 |
+
delete_posts;2;Y;Y;Y;Y;Y;
|
54 |
+
read;1;Y;Y;Y;Y;Y;Y
|
includes/editor-page.php
CHANGED
@@ -236,6 +236,13 @@ function ame_output_sort_buttons($icons) {
|
|
236 |
<input type="hidden" name="data" id="ws_data" value="">
|
237 |
<input type="hidden" name="data_length" id="ws_data_length" value="">
|
238 |
<input type="hidden" name="selected_actor" id="ws_selected_actor" value="">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
<input type="button" id='ws_save_menu' class="button-primary ws_main_button" value="Save Changes" />
|
240 |
</form>
|
241 |
|
@@ -264,15 +271,16 @@ function ame_output_sort_buttons($icons) {
|
|
264 |
|
265 |
if ( $show_pro_benefits ):
|
266 |
$benefit_variations = array(
|
267 |
-
'
|
268 |
-
'
|
269 |
-
'
|
|
|
270 |
);
|
271 |
//Pseudo-randomly select one phrase based on the site URL.
|
272 |
-
$variation_index = hexdec( substr(md5(get_site_url()), -
|
273 |
$selected_variation = $benefit_variations[$variation_index];
|
274 |
|
275 |
-
$pro_version_link = 'http://adminmenueditor.com/upgrade-to-pro/?utm_source=Admin%2BMenu%2BEditor%2Bfree&utm_medium=text_link&utm_content=
|
276 |
?>
|
277 |
<div class="clear"></div>
|
278 |
|
@@ -281,9 +289,11 @@ function ame_output_sort_buttons($icons) {
|
|
281 |
<div class="ws_hint_content">
|
282 |
<strong>Upgrade to Pro:</strong>
|
283 |
<ul>
|
|
|
|
|
|
|
|
|
284 |
<li><?php echo $selected_variation; ?></li>
|
285 |
-
<li>Drag items between menu levels.</li>
|
286 |
-
<li>Menu export & import.</li>
|
287 |
</ul>
|
288 |
<a href="<?php echo esc_attr($pro_version_link); ?>" target="_blank">Learn more</a>
|
289 |
|
|
@@ -551,6 +561,8 @@ function ame_output_sort_buttons($icons) {
|
|
551 |
</div>
|
552 |
</div>
|
553 |
|
|
|
|
|
554 |
<?php
|
555 |
if ( $is_pro_version ) {
|
556 |
include $extrasDirectory . '/page-dropdown.php';
|
236 |
<input type="hidden" name="data" id="ws_data" value="">
|
237 |
<input type="hidden" name="data_length" id="ws_data_length" value="">
|
238 |
<input type="hidden" name="selected_actor" id="ws_selected_actor" value="">
|
239 |
+
|
240 |
+
<input type="hidden" name="selected_menu_url" id="ws_selected_menu_url" value="">
|
241 |
+
<input type="hidden" name="selected_submenu_url" id="ws_selected_submenu_url" value="">
|
242 |
+
|
243 |
+
<input type="hidden" name="expand_menu" id="ws_expand_selected_menu" value="">
|
244 |
+
<input type="hidden" name="expand_submenu" id="ws_expand_selected_submenu" value="">
|
245 |
+
|
246 |
<input type="button" id='ws_save_menu' class="button-primary ws_main_button" value="Save Changes" />
|
247 |
</form>
|
248 |
|
271 |
|
272 |
if ( $show_pro_benefits ):
|
273 |
$benefit_variations = array(
|
274 |
+
'Drag items between menu levels.',
|
275 |
+
'More menu icons.',
|
276 |
+
'Make menus open in a new tab or an iframe.',
|
277 |
+
'Prevent users from deleting a specific user.',
|
278 |
);
|
279 |
//Pseudo-randomly select one phrase based on the site URL.
|
280 |
+
$variation_index = hexdec( substr(md5(get_site_url() . 'ab'), -2) ) % count($benefit_variations);
|
281 |
$selected_variation = $benefit_variations[$variation_index];
|
282 |
|
283 |
+
$pro_version_link = 'http://adminmenueditor.com/upgrade-to-pro/?utm_source=Admin%2BMenu%2BEditor%2Bfree&utm_medium=text_link&utm_content=sidebar_link_nv' . $variation_index . '&utm_campaign=Plugins';
|
284 |
?>
|
285 |
<div class="clear"></div>
|
286 |
|
289 |
<div class="ws_hint_content">
|
290 |
<strong>Upgrade to Pro:</strong>
|
291 |
<ul>
|
292 |
+
<li>Role-based menu permissions.</li>
|
293 |
+
<li>Hide items from specific users.</li>
|
294 |
+
<li>Menu import and export.</li>
|
295 |
+
<li>Change menu colors.</li>
|
296 |
<li><?php echo $selected_variation; ?></li>
|
|
|
|
|
297 |
</ul>
|
298 |
<a href="<?php echo esc_attr($pro_version_link); ?>" target="_blank">Learn more</a>
|
299 |
|
|
561 |
</div>
|
562 |
</div>
|
563 |
|
564 |
+
<?php include dirname(__FILE__) . '/cap-suggestion-box.php'; ?>
|
565 |
+
|
566 |
<?php
|
567 |
if ( $is_pro_version ) {
|
568 |
include $extrasDirectory . '/page-dropdown.php';
|
includes/menu-editor-core.php
CHANGED
@@ -754,6 +754,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
754 |
'roles' => $roles,
|
755 |
'users' => $users,
|
756 |
'isMultisite' => is_multisite(),
|
|
|
757 |
);
|
758 |
wp_localize_script('ame-actor-manager', 'wsAmeActorData', $actor_data);
|
759 |
|
@@ -821,6 +822,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
821 |
'getPagesNonce' => wp_create_nonce('ws_ame_get_pages'),
|
822 |
'getPageDetailsNonce' => wp_create_nonce('ws_ame_get_page_details'),
|
823 |
|
|
|
|
|
|
|
|
|
|
|
824 |
'isDemoMode' => defined('IS_DEMO_MODE'),
|
825 |
'isMasterMode' => defined('IS_MASTER_MODE'),
|
826 |
);
|
@@ -1083,15 +1089,34 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1083 |
return $admin_title;
|
1084 |
}
|
1085 |
|
|
|
|
|
1086 |
//Check if the we have a custom title for this page.
|
1087 |
$default_title = isset($item['defaults']['page_title']) ? $item['defaults']['page_title'] : '';
|
1088 |
if ( !empty($item['page_title']) && $item['page_title'] != $default_title ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1089 |
if ( empty($title) ) {
|
1090 |
-
$admin_title = $
|
1091 |
} else {
|
1092 |
//Replace the first occurrence of the default title with the custom one.
|
1093 |
$title_pos = strpos($admin_title, $title);
|
1094 |
-
$admin_title = substr_replace($admin_title, $
|
1095 |
}
|
1096 |
}
|
1097 |
|
@@ -1918,11 +1943,19 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
1918 |
$this->set_custom_menu($menu);
|
1919 |
|
1920 |
//Redirect back to the editor and display the success message.
|
1921 |
-
//Also, automatically select the last selected actor (convenience feature).
|
1922 |
$query = array('message' => 1);
|
1923 |
-
|
1924 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1925 |
}
|
|
|
1926 |
wp_redirect( add_query_arg($query, $url) );
|
1927 |
die();
|
1928 |
} else {
|
@@ -3394,6 +3427,36 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
|
|
3394 |
return '';
|
3395 |
}
|
3396 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3397 |
} //class
|
3398 |
|
3399 |
|
@@ -3493,13 +3556,23 @@ class ameMenuTemplateBuilder {
|
|
3493 |
|
3494 |
/**
|
3495 |
* Sanitize a menu title for display.
|
3496 |
-
* Removes HTML tags and update notification bubbles.
|
3497 |
*
|
3498 |
* @param string $title
|
3499 |
* @return string
|
3500 |
*/
|
3501 |
private function sanitizeMenuTitle($title) {
|
3502 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3503 |
}
|
3504 |
|
3505 |
public function getRelativeTemplateOrder() {
|
754 |
'roles' => $roles,
|
755 |
'users' => $users,
|
756 |
'isMultisite' => is_multisite(),
|
757 |
+
'capPower' => $this->load_cap_power(),
|
758 |
);
|
759 |
wp_localize_script('ame-actor-manager', 'wsAmeActorData', $actor_data);
|
760 |
|
822 |
'getPagesNonce' => wp_create_nonce('ws_ame_get_pages'),
|
823 |
'getPageDetailsNonce' => wp_create_nonce('ws_ame_get_page_details'),
|
824 |
|
825 |
+
'selectedMenu' => isset($this->get['selected_menu_url']) ? strval($this->get['selected_menu_url']) : null,
|
826 |
+
'selectedSubmenu' => isset($this->get['selected_submenu_url']) ? strval($this->get['selected_submenu_url']) : null,
|
827 |
+
'expandSelectedMenu' => isset($this->get['expand_menu']) && ($this->get['expand_menu'] === '1'),
|
828 |
+
'expandSelectedSubmenu' => isset($this->get['expand_submenu']) && ($this->get['expand_submenu'] === '1'),
|
829 |
+
|
830 |
'isDemoMode' => defined('IS_DEMO_MODE'),
|
831 |
'isMasterMode' => defined('IS_MASTER_MODE'),
|
832 |
);
|
1089 |
return $admin_title;
|
1090 |
}
|
1091 |
|
1092 |
+
$custom_title = null;
|
1093 |
+
|
1094 |
//Check if the we have a custom title for this page.
|
1095 |
$default_title = isset($item['defaults']['page_title']) ? $item['defaults']['page_title'] : '';
|
1096 |
if ( !empty($item['page_title']) && $item['page_title'] != $default_title ) {
|
1097 |
+
$custom_title = $item['page_title'];
|
1098 |
+
}
|
1099 |
+
|
1100 |
+
//Alternatively, use the custom menu title if the default page title is empty (as is usually
|
1101 |
+
//the case with core menus) or matches the default menu title (which is typical for plugins).
|
1102 |
+
//This saves the user a little bit of time, and, presumably, they'd want the titles to match.
|
1103 |
+
$default_menu_title = isset($item['defaults']['menu_title']) ? $item['defaults']['menu_title'] : '';
|
1104 |
+
if (
|
1105 |
+
!isset($custom_title)
|
1106 |
+
&& !empty($item['menu_title'])
|
1107 |
+
&& ($item['menu_title'] !== $default_menu_title)
|
1108 |
+
&& (($default_menu_title === $default_title) || ($default_title === ''))
|
1109 |
+
) {
|
1110 |
+
$custom_title = strip_tags($item['menu_title']);
|
1111 |
+
}
|
1112 |
+
|
1113 |
+
if ( isset($custom_title) ) {
|
1114 |
if ( empty($title) ) {
|
1115 |
+
$admin_title = $custom_title . $admin_title;
|
1116 |
} else {
|
1117 |
//Replace the first occurrence of the default title with the custom one.
|
1118 |
$title_pos = strpos($admin_title, $title);
|
1119 |
+
$admin_title = substr_replace($admin_title, $custom_title, $title_pos, strlen($title));
|
1120 |
}
|
1121 |
}
|
1122 |
|
1943 |
$this->set_custom_menu($menu);
|
1944 |
|
1945 |
//Redirect back to the editor and display the success message.
|
|
|
1946 |
$query = array('message' => 1);
|
1947 |
+
|
1948 |
+
//Also, automatically select the last selected actor and menu (convenience feature).
|
1949 |
+
$pass_through_params = array(
|
1950 |
+
'selected_actor', 'selected_menu_url', 'selected_submenu_url',
|
1951 |
+
'expand_menu', 'expand_submenu',
|
1952 |
+
);
|
1953 |
+
foreach($pass_through_params as $param) {
|
1954 |
+
if ( isset($post[$param]) && !empty($post[$param]) ) {
|
1955 |
+
$query[$param] = rawurlencode(strval($post[$param]));
|
1956 |
+
}
|
1957 |
}
|
1958 |
+
|
1959 |
wp_redirect( add_query_arg($query, $url) );
|
1960 |
die();
|
1961 |
} else {
|
3427 |
return '';
|
3428 |
}
|
3429 |
|
3430 |
+
/**
|
3431 |
+
* @return array
|
3432 |
+
*/
|
3433 |
+
private function load_cap_power() {
|
3434 |
+
$cap_power = array();
|
3435 |
+
|
3436 |
+
$power_filename = AME_ROOT_DIR . '/includes/capabilities/cap-power.csv';
|
3437 |
+
if ( is_file($power_filename) && is_readable($power_filename) ) {
|
3438 |
+
$csv = fopen($power_filename, 'r');
|
3439 |
+
$firstLineSkipped = false;
|
3440 |
+
|
3441 |
+
while ($csv && !feof($csv)) {
|
3442 |
+
$line = fgetcsv($csv, 1000, ';');
|
3443 |
+
if ( !$firstLineSkipped ) {
|
3444 |
+
$firstLineSkipped = true;
|
3445 |
+
continue;
|
3446 |
+
}
|
3447 |
+
|
3448 |
+
if ( count($line) >= 2 ) {
|
3449 |
+
$cap_power[strval($line[0])] = floatval(str_replace(',', '.', $line[1]));
|
3450 |
+
}
|
3451 |
+
}
|
3452 |
+
fclose($csv);
|
3453 |
+
|
3454 |
+
arsort($cap_power);
|
3455 |
+
}
|
3456 |
+
|
3457 |
+
return $cap_power;
|
3458 |
+
}
|
3459 |
+
|
3460 |
} //class
|
3461 |
|
3462 |
|
3556 |
|
3557 |
/**
|
3558 |
* Sanitize a menu title for display.
|
3559 |
+
* Removes HTML tags and update notification bubbles. Truncates long titles.
|
3560 |
*
|
3561 |
* @param string $title
|
3562 |
* @return string
|
3563 |
*/
|
3564 |
private function sanitizeMenuTitle($title) {
|
3565 |
+
$title = strip_tags( preg_replace('@<span[^>]*>.*</span>@i', '', $title) );
|
3566 |
+
|
3567 |
+
//Compact whitespace.
|
3568 |
+
$title = rtrim(preg_replace('@[\s\t\r\n]+@', ' ', $title));
|
3569 |
+
|
3570 |
+
$maxLength = 50;
|
3571 |
+
if ( strlen($title) > $maxLength ) {
|
3572 |
+
$title = rtrim(substr($title, 0, $maxLength)) . '...';
|
3573 |
+
}
|
3574 |
+
|
3575 |
+
return $title;
|
3576 |
}
|
3577 |
|
3578 |
public function getRelativeTemplateOrder() {
|
js/actor-manager.js
CHANGED
@@ -46,6 +46,9 @@ var AmeBaseActor = (function () {
|
|
46 |
}
|
47 |
return specificity;
|
48 |
};
|
|
|
|
|
|
|
49 |
return AmeBaseActor;
|
50 |
}());
|
51 |
var AmeRole = (function (_super) {
|
@@ -116,6 +119,7 @@ var AmeActorManager = (function () {
|
|
116 |
this.users = {};
|
117 |
this.grantedCapabilities = {};
|
118 |
this.isMultisite = false;
|
|
|
119 |
this.isMultisite = !!isMultisite;
|
120 |
AmeActorManager._.forEach(roles, function (roleDetails, id) {
|
121 |
var role = new AmeRole(id, roleDetails.name, roleDetails.capabilities);
|
@@ -341,10 +345,98 @@ var AmeActorManager = (function () {
|
|
341 |
return delta;
|
342 |
};
|
343 |
;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
344 |
AmeActorManager._ = wsAmeLodash;
|
345 |
return AmeActorManager;
|
346 |
}());
|
347 |
if (typeof wsAmeActorData !== 'undefined') {
|
348 |
AmeActors = new AmeActorManager(wsAmeActorData.roles, wsAmeActorData.users, wsAmeActorData.isMultisite);
|
|
|
|
|
|
|
349 |
}
|
350 |
//# sourceMappingURL=actor-manager.js.map
|
46 |
}
|
47 |
return specificity;
|
48 |
};
|
49 |
+
AmeBaseActor.prototype.toString = function () {
|
50 |
+
return this.displayName + ' [' + this.id + ']';
|
51 |
+
};
|
52 |
return AmeBaseActor;
|
53 |
}());
|
54 |
var AmeRole = (function (_super) {
|
119 |
this.users = {};
|
120 |
this.grantedCapabilities = {};
|
121 |
this.isMultisite = false;
|
122 |
+
this.suggestedCapabilities = [];
|
123 |
this.isMultisite = !!isMultisite;
|
124 |
AmeActorManager._.forEach(roles, function (roleDetails, id) {
|
125 |
var role = new AmeRole(id, roleDetails.name, roleDetails.capabilities);
|
345 |
return delta;
|
346 |
};
|
347 |
;
|
348 |
+
AmeActorManager.prototype.generateCapabilitySuggestions = function (capPower) {
|
349 |
+
var _ = AmeActorManager._;
|
350 |
+
var capsByPower = _.memoize(function (role) {
|
351 |
+
var sortedCaps = _.reduce(role.capabilities, function (result, hasCap, capability) {
|
352 |
+
if (hasCap) {
|
353 |
+
result.push({
|
354 |
+
capability: capability,
|
355 |
+
power: _.get(capPower, [capability], 0)
|
356 |
+
});
|
357 |
+
}
|
358 |
+
return result;
|
359 |
+
}, []);
|
360 |
+
sortedCaps = _.sortBy(sortedCaps, function (item) { return -item.power; });
|
361 |
+
return sortedCaps;
|
362 |
+
});
|
363 |
+
var rolesByPower = _.values(this.getRoles()).sort(function (a, b) {
|
364 |
+
var aCaps = capsByPower(a), bCaps = capsByPower(b);
|
365 |
+
//Prioritise roles with the highest number of the most powerful capabilities.
|
366 |
+
for (var i = 0, limit = Math.min(aCaps.length, bCaps.length); i < limit; i++) {
|
367 |
+
var delta_1 = bCaps[i].power - aCaps[i].power;
|
368 |
+
if (delta_1 !== 0) {
|
369 |
+
return delta_1;
|
370 |
+
}
|
371 |
+
}
|
372 |
+
//Give a tie to the role that has more capabilities.
|
373 |
+
var delta = bCaps.length - aCaps.length;
|
374 |
+
if (delta !== 0) {
|
375 |
+
return delta;
|
376 |
+
}
|
377 |
+
//Failing that, just sort alphabetically.
|
378 |
+
if (a.displayName > b.displayName) {
|
379 |
+
return 1;
|
380 |
+
}
|
381 |
+
else if (a.displayName < b.displayName) {
|
382 |
+
return -1;
|
383 |
+
}
|
384 |
+
return 0;
|
385 |
+
});
|
386 |
+
var preferredCaps = [
|
387 |
+
'manage_network_options',
|
388 |
+
'install_plugins', 'edit_plugins', 'delete_users',
|
389 |
+
'manage_options', 'switch_themes',
|
390 |
+
'edit_others_pages', 'edit_others_posts', 'edit_pages',
|
391 |
+
'unfiltered_html',
|
392 |
+
'publish_posts', 'edit_posts',
|
393 |
+
'read'
|
394 |
+
];
|
395 |
+
var deprecatedCaps = _(_.range(0, 10)).map(function (level) { return 'level_' + level; }).value();
|
396 |
+
deprecatedCaps.push('edit_files');
|
397 |
+
var findDiscriminant = function (caps, includeRoles, excludeRoles) {
|
398 |
+
var getEnabledCaps = function (role) {
|
399 |
+
return _.keys(_.pick(role.capabilities, _.identity));
|
400 |
+
};
|
401 |
+
//Find caps that all of the includeRoles have and excludeRoles don't.
|
402 |
+
var includeCaps = _.intersection.apply(_, _.map(includeRoles, getEnabledCaps)), excludeCaps = _.union.apply(_, _.map(excludeRoles, getEnabledCaps)), possibleCaps = _.without.apply(_, [includeCaps].concat(excludeCaps).concat(deprecatedCaps));
|
403 |
+
var bestCaps = _.intersection(preferredCaps, possibleCaps);
|
404 |
+
if (bestCaps.length > 0) {
|
405 |
+
return bestCaps[0];
|
406 |
+
}
|
407 |
+
else if (possibleCaps.length > 0) {
|
408 |
+
return possibleCaps[0];
|
409 |
+
}
|
410 |
+
return null;
|
411 |
+
};
|
412 |
+
var suggestedCapabilities = [];
|
413 |
+
for (var i = 0; i < rolesByPower.length; i++) {
|
414 |
+
var role = rolesByPower[i];
|
415 |
+
var cap = findDiscriminant(preferredCaps, _.slice(rolesByPower, 0, i + 1), _.slice(rolesByPower, i + 1, rolesByPower.length));
|
416 |
+
suggestedCapabilities.push({ role: role, capability: cap });
|
417 |
+
}
|
418 |
+
var previousSuggestion = null;
|
419 |
+
for (var i = suggestedCapabilities.length - 1; i >= 0; i--) {
|
420 |
+
if (suggestedCapabilities[i].capability === null) {
|
421 |
+
suggestedCapabilities[i].capability =
|
422 |
+
previousSuggestion ? previousSuggestion : 'exist';
|
423 |
+
}
|
424 |
+
else {
|
425 |
+
previousSuggestion = suggestedCapabilities[i].capability;
|
426 |
+
}
|
427 |
+
}
|
428 |
+
this.suggestedCapabilities = suggestedCapabilities;
|
429 |
+
};
|
430 |
+
AmeActorManager.prototype.getSuggestedCapabilities = function () {
|
431 |
+
return this.suggestedCapabilities;
|
432 |
+
};
|
433 |
AmeActorManager._ = wsAmeLodash;
|
434 |
return AmeActorManager;
|
435 |
}());
|
436 |
if (typeof wsAmeActorData !== 'undefined') {
|
437 |
AmeActors = new AmeActorManager(wsAmeActorData.roles, wsAmeActorData.users, wsAmeActorData.isMultisite);
|
438 |
+
if (typeof wsAmeActorData['capPower'] !== 'undefined') {
|
439 |
+
AmeActors.generateCapabilitySuggestions(wsAmeActorData['capPower']);
|
440 |
+
}
|
441 |
}
|
442 |
//# sourceMappingURL=actor-manager.js.map
|
js/actor-manager.ts
CHANGED
@@ -58,6 +58,10 @@ abstract class AmeBaseActor {
|
|
58 |
}
|
59 |
return specificity;
|
60 |
}
|
|
|
|
|
|
|
|
|
61 |
}
|
62 |
|
63 |
class AmeRole extends AmeBaseActor {
|
@@ -162,6 +166,11 @@ interface AmeGrantedCapabilityMap {
|
|
162 |
}
|
163 |
}
|
164 |
|
|
|
|
|
|
|
|
|
|
|
165 |
class AmeActorManager {
|
166 |
private static _ = wsAmeLodash;
|
167 |
|
@@ -172,6 +181,8 @@ class AmeActorManager {
|
|
172 |
private isMultisite: boolean = false;
|
173 |
private superAdmin: AmeSuperAdmin;
|
174 |
|
|
|
|
|
175 |
constructor(roles, users, isMultisite: boolean = false) {
|
176 |
this.isMultisite = !!isMultisite;
|
177 |
|
@@ -450,6 +461,113 @@ class AmeActorManager {
|
|
450 |
}
|
451 |
return delta;
|
452 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
453 |
}
|
454 |
|
455 |
if (typeof wsAmeActorData !== 'undefined') {
|
@@ -458,4 +576,8 @@ if (typeof wsAmeActorData !== 'undefined') {
|
|
458 |
wsAmeActorData.users,
|
459 |
wsAmeActorData.isMultisite
|
460 |
);
|
|
|
|
|
|
|
|
|
461 |
}
|
58 |
}
|
59 |
return specificity;
|
60 |
}
|
61 |
+
|
62 |
+
toString(): string {
|
63 |
+
return this.displayName + ' [' + this.id + ']';
|
64 |
+
}
|
65 |
}
|
66 |
|
67 |
class AmeRole extends AmeBaseActor {
|
166 |
}
|
167 |
}
|
168 |
|
169 |
+
interface AmeCapabilitySuggestion {
|
170 |
+
role: AmeRole;
|
171 |
+
capability: string;
|
172 |
+
}
|
173 |
+
|
174 |
class AmeActorManager {
|
175 |
private static _ = wsAmeLodash;
|
176 |
|
181 |
private isMultisite: boolean = false;
|
182 |
private superAdmin: AmeSuperAdmin;
|
183 |
|
184 |
+
private suggestedCapabilities: AmeCapabilitySuggestion[] = [];
|
185 |
+
|
186 |
constructor(roles, users, isMultisite: boolean = false) {
|
187 |
this.isMultisite = !!isMultisite;
|
188 |
|
461 |
}
|
462 |
return delta;
|
463 |
};
|
464 |
+
|
465 |
+
generateCapabilitySuggestions(capPower): void {
|
466 |
+
let _ = AmeActorManager._;
|
467 |
+
|
468 |
+
let capsByPower = _.memoize((role: AmeRole): {capability: string, power: number}[] => {
|
469 |
+
let sortedCaps = _.reduce(role.capabilities, (result, hasCap, capability) => {
|
470 |
+
if (hasCap) {
|
471 |
+
result.push({
|
472 |
+
capability: capability,
|
473 |
+
power: _.get(capPower, [capability], 0)
|
474 |
+
});
|
475 |
+
}
|
476 |
+
return result;
|
477 |
+
}, []);
|
478 |
+
|
479 |
+
sortedCaps = _.sortBy(sortedCaps, (item) => -item.power);
|
480 |
+
return sortedCaps;
|
481 |
+
});
|
482 |
+
|
483 |
+
let rolesByPower: AmeRole[] = _.values<AmeRole>(this.getRoles()).sort(function(a: AmeRole, b: AmeRole) {
|
484 |
+
let aCaps = capsByPower(a),
|
485 |
+
bCaps = capsByPower(b);
|
486 |
+
|
487 |
+
//Prioritise roles with the highest number of the most powerful capabilities.
|
488 |
+
for (var i = 0, limit = Math.min(aCaps.length, bCaps.length); i < limit; i++) {
|
489 |
+
let delta = bCaps[i].power - aCaps[i].power;
|
490 |
+
if (delta !== 0) {
|
491 |
+
return delta;
|
492 |
+
}
|
493 |
+
}
|
494 |
+
|
495 |
+
//Give a tie to the role that has more capabilities.
|
496 |
+
let delta = bCaps.length - aCaps.length;
|
497 |
+
if (delta !== 0) {
|
498 |
+
return delta;
|
499 |
+
}
|
500 |
+
|
501 |
+
//Failing that, just sort alphabetically.
|
502 |
+
if (a.displayName > b.displayName) {
|
503 |
+
return 1;
|
504 |
+
} else if (a.displayName < b.displayName) {
|
505 |
+
return -1;
|
506 |
+
}
|
507 |
+
return 0;
|
508 |
+
});
|
509 |
+
|
510 |
+
let preferredCaps = [
|
511 |
+
'manage_network_options',
|
512 |
+
'install_plugins', 'edit_plugins', 'delete_users',
|
513 |
+
'manage_options', 'switch_themes',
|
514 |
+
'edit_others_pages', 'edit_others_posts', 'edit_pages',
|
515 |
+
'unfiltered_html',
|
516 |
+
'publish_posts', 'edit_posts',
|
517 |
+
'read'
|
518 |
+
];
|
519 |
+
|
520 |
+
let deprecatedCaps = _(_.range(0, 10)).map((level) => 'level_' + level).value();
|
521 |
+
deprecatedCaps.push('edit_files');
|
522 |
+
|
523 |
+
let findDiscriminant = (caps: string[], includeRoles: AmeRole[], excludeRoles): string => {
|
524 |
+
let getEnabledCaps = (role: AmeRole): string[] => {
|
525 |
+
return _.keys(_.pick(role.capabilities, _.identity));
|
526 |
+
};
|
527 |
+
|
528 |
+
//Find caps that all of the includeRoles have and excludeRoles don't.
|
529 |
+
let includeCaps = _.intersection.apply(_, _.map(includeRoles, getEnabledCaps)),
|
530 |
+
excludeCaps = _.union.apply(_, _.map(excludeRoles, getEnabledCaps)),
|
531 |
+
possibleCaps = _.without.apply(_, [includeCaps].concat(excludeCaps).concat(deprecatedCaps));
|
532 |
+
|
533 |
+
let bestCaps = _.intersection(preferredCaps, possibleCaps);
|
534 |
+
|
535 |
+
if (bestCaps.length > 0) {
|
536 |
+
return bestCaps[0];
|
537 |
+
} else if (possibleCaps.length > 0) {
|
538 |
+
return possibleCaps[0];
|
539 |
+
}
|
540 |
+
return null;
|
541 |
+
};
|
542 |
+
|
543 |
+
let suggestedCapabilities = [];
|
544 |
+
for (let i = 0; i < rolesByPower.length; i++) {
|
545 |
+
let role = rolesByPower[i];
|
546 |
+
|
547 |
+
let cap = findDiscriminant(
|
548 |
+
preferredCaps,
|
549 |
+
_.slice(rolesByPower, 0, i + 1),
|
550 |
+
_.slice(rolesByPower, i + 1, rolesByPower.length)
|
551 |
+
);
|
552 |
+
suggestedCapabilities.push({role: role, capability: cap});
|
553 |
+
}
|
554 |
+
|
555 |
+
let previousSuggestion = null;
|
556 |
+
for (let i = suggestedCapabilities.length - 1; i >= 0; i--) {
|
557 |
+
if (suggestedCapabilities[i].capability === null) {
|
558 |
+
suggestedCapabilities[i].capability =
|
559 |
+
previousSuggestion ? previousSuggestion : 'exist';
|
560 |
+
} else {
|
561 |
+
previousSuggestion = suggestedCapabilities[i].capability;
|
562 |
+
}
|
563 |
+
}
|
564 |
+
|
565 |
+
this.suggestedCapabilities = suggestedCapabilities;
|
566 |
+
}
|
567 |
+
|
568 |
+
public getSuggestedCapabilities(): AmeCapabilitySuggestion[] {
|
569 |
+
return this.suggestedCapabilities;
|
570 |
+
}
|
571 |
}
|
572 |
|
573 |
if (typeof wsAmeActorData !== 'undefined') {
|
576 |
wsAmeActorData.users,
|
577 |
wsAmeActorData.isMultisite
|
578 |
);
|
579 |
+
|
580 |
+
if (typeof wsAmeActorData['capPower'] !== 'undefined') {
|
581 |
+
AmeActors.generateCapabilitySuggestions(wsAmeActorData['capPower']);
|
582 |
+
}
|
583 |
}
|
js/menu-editor.js
CHANGED
@@ -43,6 +43,9 @@
|
|
43 |
* @property {object} wsEditorData.postTypes
|
44 |
* @property {object} wsEditorData.taxonomies
|
45 |
*
|
|
|
|
|
|
|
46 |
* @property {boolean} wsEditorData.isDemoMode
|
47 |
* @property {boolean} wsEditorData.isMasterMode
|
48 |
*/
|
@@ -398,7 +401,7 @@ function buildMenuItem(itemData, isTopLevel) {
|
|
398 |
itemData.separator ? '' : '<a class="ws_edit_link"> </a><div class="ws_flag_container"> </div>',
|
399 |
'<input type="checkbox" class="ws_actor_access_checkbox">',
|
400 |
'<span class="ws_item_title">',
|
401 |
-
|
402 |
' </span>',
|
403 |
|
404 |
'</div>',
|
@@ -459,6 +462,37 @@ function stripAllTags(input) {
|
|
459 |
return input.replace(commentsAndPhpTags, '').replace(tags, '');
|
460 |
}
|
461 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
462 |
//Editor field spec template.
|
463 |
var baseField = {
|
464 |
caption : '[No caption]',
|
@@ -484,7 +518,7 @@ var knownMenuFields = {
|
|
484 |
caption : 'Menu title',
|
485 |
display: function(menuItem, displayValue, input, containerNode) {
|
486 |
//Update the header as well.
|
487 |
-
containerNode.find('.ws_item_title').html(
|
488 |
return displayValue;
|
489 |
},
|
490 |
write: function(menuItem, value, input, containerNode) {
|
@@ -736,66 +770,6 @@ var knownMenuFields = {
|
|
736 |
}
|
737 |
}),
|
738 |
|
739 |
-
'page_title' : $.extend({}, baseField, {
|
740 |
-
caption: "Window title",
|
741 |
-
standardCaption : true,
|
742 |
-
advanced : true
|
743 |
-
}),
|
744 |
-
|
745 |
-
'open_in' : $.extend({}, baseField, {
|
746 |
-
caption: 'Open in',
|
747 |
-
advanced : true,
|
748 |
-
type : 'select',
|
749 |
-
options : [
|
750 |
-
['Same window or tab', 'same_window'],
|
751 |
-
['New window', 'new_window'],
|
752 |
-
['Frame', 'iframe']
|
753 |
-
],
|
754 |
-
defaultValue: 'same_window',
|
755 |
-
visible: false
|
756 |
-
}),
|
757 |
-
|
758 |
-
'iframe_height' : $.extend({}, baseField, {
|
759 |
-
caption: 'Frame height (pixels)',
|
760 |
-
advanced : true,
|
761 |
-
visible: function(menuItem) {
|
762 |
-
return wsEditorData.wsMenuEditorPro && (getFieldValue(menuItem, 'open_in') === 'iframe');
|
763 |
-
},
|
764 |
-
|
765 |
-
display: function(menuItem, displayValue, input) {
|
766 |
-
input.prop('placeholder', 'Auto');
|
767 |
-
if (displayValue === 0 || displayValue === '0') {
|
768 |
-
displayValue = '';
|
769 |
-
}
|
770 |
-
return displayValue;
|
771 |
-
},
|
772 |
-
|
773 |
-
write: function(menuItem, value) {
|
774 |
-
value = parseInt(value, 10);
|
775 |
-
if (isNaN(value) || (value < 0)) {
|
776 |
-
value = 0;
|
777 |
-
}
|
778 |
-
value = Math.round(value);
|
779 |
-
|
780 |
-
if (value > 10000) {
|
781 |
-
value = 10000;
|
782 |
-
}
|
783 |
-
|
784 |
-
if (value === 0) {
|
785 |
-
menuItem.iframe_height = null;
|
786 |
-
} else {
|
787 |
-
menuItem.iframe_height = value;
|
788 |
-
}
|
789 |
-
|
790 |
-
}
|
791 |
-
}),
|
792 |
-
|
793 |
-
'css_class' : $.extend({}, baseField, {
|
794 |
-
caption: 'CSS classes',
|
795 |
-
advanced : true,
|
796 |
-
onlyForTopMenus: true
|
797 |
-
}),
|
798 |
-
|
799 |
'icon_url' : $.extend({}, baseField, {
|
800 |
caption: 'Icon URL',
|
801 |
type : 'icon_selector',
|
@@ -856,6 +830,60 @@ var knownMenuFields = {
|
|
856 |
}
|
857 |
}),
|
858 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
859 |
'colors' : $.extend({}, baseField, {
|
860 |
caption: 'Color scheme',
|
861 |
defaultValue: 'Default',
|
@@ -894,6 +922,12 @@ var knownMenuFields = {
|
|
894 |
}
|
895 |
}),
|
896 |
|
|
|
|
|
|
|
|
|
|
|
|
|
897 |
'page_heading' : $.extend({}, baseField, {
|
898 |
caption: 'Page heading',
|
899 |
advanced : true,
|
@@ -1329,6 +1363,22 @@ function getDefaultValue(entry, fieldName, defaultValue, containerNode) {
|
|
1329 |
}
|
1330 |
}
|
1331 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1332 |
if (typeof defaultValue === 'undefined') {
|
1333 |
defaultValue = null;
|
1334 |
}
|
@@ -1400,6 +1450,45 @@ AmeEditorApi.forEachMenuItem = function(callback, skipSeparators) {
|
|
1400 |
});
|
1401 |
};
|
1402 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1403 |
/***************************************************************************
|
1404 |
Parsing & encoding menu inputs
|
1405 |
***************************************************************************/
|
@@ -1891,11 +1980,16 @@ function ameOnDomReady() {
|
|
1891 |
submenuBox = $('#ws_submenu_box'),
|
1892 |
submenuDropZone = submenuBox.closest('.ws_main_container').find('.ws_dropzone');
|
1893 |
|
1894 |
-
//Highlight the clicked menu item and show it's submenu
|
1895 |
var currentVisibleSubmenu = null;
|
1896 |
-
|
1897 |
-
|
|
|
|
|
|
|
|
|
|
|
1898 |
if (container.hasClass('ws_active')) {
|
|
|
1899 |
return;
|
1900 |
}
|
1901 |
|
@@ -1920,6 +2014,12 @@ function ameOnDomReady() {
|
|
1920 |
container.closest('.ws_main_container')
|
1921 |
.find('.ws_toolbar .ws_delete_menu_button')
|
1922 |
.toggleClass('ws_button_disabled', !canDeleteItem(container));
|
|
|
|
|
|
|
|
|
|
|
|
|
1923 |
}));
|
1924 |
|
1925 |
function updateSubmenuBoxHeight(selectedMenu) {
|
@@ -2277,7 +2377,9 @@ function ameOnDomReady() {
|
|
2277 |
|
2278 |
var capSelectorDropdown = $('#ws_cap_selector');
|
2279 |
var currentDropdownOwner = null; //The input element that the dropdown is currently associated with.
|
2280 |
-
var
|
|
|
|
|
2281 |
|
2282 |
//Show/hide the capability drop-down list when the trigger button is clicked
|
2283 |
$('#ws_trigger_capability_dropdown').on('mousedown click', onDropdownTriggerClicked);
|
@@ -2288,9 +2390,13 @@ function ameOnDomReady() {
|
|
2288 |
var inputBox = null;
|
2289 |
var button = $(this);
|
2290 |
|
|
|
|
|
|
|
2291 |
//Find the input associated with the button that was clicked.
|
2292 |
if ( button.attr('id') === 'ws_trigger_capability_dropdown' ) {
|
2293 |
inputBox = $('#ws_extra_capability');
|
|
|
2294 |
} else {
|
2295 |
inputBox = button.closest('.ws_edit_field').find('.ws_field_value').first();
|
2296 |
}
|
@@ -2333,7 +2439,17 @@ function ameOnDomReady() {
|
|
2333 |
width(inputBox.outerWidth());
|
2334 |
|
2335 |
currentDropdownOwner = inputBox;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2336 |
capSelectorDropdown.focus();
|
|
|
|
|
2337 |
}
|
2338 |
|
2339 |
//Also show it when the user presses the down arrow in the input field (doesn't work in Opera).
|
@@ -2343,26 +2459,34 @@ function ameOnDomReady() {
|
|
2343 |
}
|
2344 |
});
|
2345 |
|
|
|
|
|
|
|
|
|
|
|
|
|
2346 |
//Event handlers for the drop-down lists themselves
|
2347 |
var dropdownNodes = $('.ws_dropdown');
|
2348 |
|
2349 |
// Hide capability drop-down when it loses focus.
|
2350 |
dropdownNodes.blur(function(){
|
2351 |
-
|
|
|
|
|
2352 |
});
|
2353 |
|
2354 |
dropdownNodes.keydown(function(event){
|
2355 |
|
2356 |
//Hide it when the user presses Esc
|
2357 |
if ( event.which === 27 ){
|
2358 |
-
|
2359 |
if (currentDropdownOwner) {
|
2360 |
currentDropdownOwner.focus();
|
2361 |
}
|
2362 |
|
2363 |
//Select an item & hide the list when the user presses Enter or Tab
|
2364 |
} else if ( (event.which === 13) || (event.which === 9) ){
|
2365 |
-
|
2366 |
|
2367 |
if (currentDropdownOwner) {
|
2368 |
if ( capSelectorDropdown.val() ){
|
@@ -2386,7 +2510,7 @@ function ameOnDomReady() {
|
|
2386 |
//Update the input & hide the list when an option is clicked
|
2387 |
dropdownNodes.click(function(){
|
2388 |
if (capSelectorDropdown.val()){
|
2389 |
-
|
2390 |
if (currentDropdownOwner) {
|
2391 |
currentDropdownOwner.val(capSelectorDropdown.val()).change().focus();
|
2392 |
}
|
@@ -2402,9 +2526,176 @@ function ameOnDomReady() {
|
|
2402 |
var option = event.target;
|
2403 |
if ( (typeof option.selected !== 'undefined') && !option.selected && option.value ){
|
2404 |
option.selected = true;
|
|
|
|
|
|
|
2405 |
}
|
2406 |
});
|
2407 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2408 |
/*************************************************************************
|
2409 |
Icon selector
|
2410 |
*************************************************************************/
|
@@ -2873,7 +3164,7 @@ function ameOnDomReady() {
|
|
2873 |
|
2874 |
//Add menu title to the dialog caption.
|
2875 |
var title = getFieldValue(menuItem, 'menu_title', null);
|
2876 |
-
setUpColorDialog(title ? ('Colors: ' + title
|
2877 |
|
2878 |
//Show the [global] preset only if the user has set it up.
|
2879 |
var globalPresetExists = colorPresets.hasOwnProperty('[global]');
|
@@ -3897,6 +4188,19 @@ function ameOnDomReady() {
|
|
3897 |
$('#ws_data').val(data);
|
3898 |
$('#ws_data_length').val(data.length);
|
3899 |
$('#ws_selected_actor').val(actorSelectorWidget.selectedActor === null ? '' : actorSelectorWidget.selectedActor);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3900 |
$('#ws_main_form').submit();
|
3901 |
});
|
3902 |
|
@@ -4309,6 +4613,23 @@ function ameOnDomReady() {
|
|
4309 |
//Finally, show the menu
|
4310 |
loadMenuConfiguration(customMenu);
|
4311 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4312 |
//... and make the UI visible now that it's fully rendered.
|
4313 |
menuEditorNode.css('visibility', 'visible');
|
4314 |
}
|
43 |
* @property {object} wsEditorData.postTypes
|
44 |
* @property {object} wsEditorData.taxonomies
|
45 |
*
|
46 |
+
* @property {string|null} wsEditorData.selectedMenu
|
47 |
+
* @property {string|null} wsEditorData.selectedSubmenu
|
48 |
+
*
|
49 |
* @property {boolean} wsEditorData.isDemoMode
|
50 |
* @property {boolean} wsEditorData.isMasterMode
|
51 |
*/
|
401 |
itemData.separator ? '' : '<a class="ws_edit_link"> </a><div class="ws_flag_container"> </div>',
|
402 |
'<input type="checkbox" class="ws_actor_access_checkbox">',
|
403 |
'<span class="ws_item_title">',
|
404 |
+
formatMenuTitle(menuTitle),
|
405 |
' </span>',
|
406 |
|
407 |
'</div>',
|
462 |
return input.replace(commentsAndPhpTags, '').replace(tags, '');
|
463 |
}
|
464 |
|
465 |
+
function truncateString(input, maxLength, padding) {
|
466 |
+
if (typeof padding === 'undefined') {
|
467 |
+
padding = '';
|
468 |
+
}
|
469 |
+
|
470 |
+
if (input.length > maxLength) {
|
471 |
+
input = input.substring(0, maxLength - 1) + padding;
|
472 |
+
}
|
473 |
+
|
474 |
+
return input;
|
475 |
+
}
|
476 |
+
|
477 |
+
/**
|
478 |
+
* Format menu title for display in HTML.
|
479 |
+
* Strips tags and truncates long titles.
|
480 |
+
*
|
481 |
+
* @param {String} title
|
482 |
+
* @returns {String}
|
483 |
+
*/
|
484 |
+
function formatMenuTitle(title) {
|
485 |
+
title = stripAllTags(title);
|
486 |
+
|
487 |
+
//Compact whitespace.
|
488 |
+
title = title.replace(/[\s\t\r\n]+/g, ' ');
|
489 |
+
title = jsTrim(title);
|
490 |
+
|
491 |
+
//The max. length was chosen empirically.
|
492 |
+
title = truncateString(title, 34, '\u2026');
|
493 |
+
return title;
|
494 |
+
}
|
495 |
+
|
496 |
//Editor field spec template.
|
497 |
var baseField = {
|
498 |
caption : '[No caption]',
|
518 |
caption : 'Menu title',
|
519 |
display: function(menuItem, displayValue, input, containerNode) {
|
520 |
//Update the header as well.
|
521 |
+
containerNode.find('.ws_item_title').html(formatMenuTitle(displayValue) + ' ');
|
522 |
return displayValue;
|
523 |
},
|
524 |
write: function(menuItem, value, input, containerNode) {
|
770 |
}
|
771 |
}),
|
772 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
773 |
'icon_url' : $.extend({}, baseField, {
|
774 |
caption: 'Icon URL',
|
775 |
type : 'icon_selector',
|
830 |
}
|
831 |
}),
|
832 |
|
833 |
+
'css_class' : $.extend({}, baseField, {
|
834 |
+
caption: 'CSS classes',
|
835 |
+
advanced : true,
|
836 |
+
onlyForTopMenus: true
|
837 |
+
}),
|
838 |
+
|
839 |
+
'open_in' : $.extend({}, baseField, {
|
840 |
+
caption: 'Open in',
|
841 |
+
advanced : true,
|
842 |
+
type : 'select',
|
843 |
+
options : [
|
844 |
+
['Same window or tab', 'same_window'],
|
845 |
+
['New window', 'new_window'],
|
846 |
+
['Frame', 'iframe']
|
847 |
+
],
|
848 |
+
defaultValue: 'same_window',
|
849 |
+
visible: false
|
850 |
+
}),
|
851 |
+
|
852 |
+
'iframe_height' : $.extend({}, baseField, {
|
853 |
+
caption: 'Frame height (pixels)',
|
854 |
+
advanced : true,
|
855 |
+
visible: function(menuItem) {
|
856 |
+
return wsEditorData.wsMenuEditorPro && (getFieldValue(menuItem, 'open_in') === 'iframe');
|
857 |
+
},
|
858 |
+
|
859 |
+
display: function(menuItem, displayValue, input) {
|
860 |
+
input.prop('placeholder', 'Auto');
|
861 |
+
if (displayValue === 0 || displayValue === '0') {
|
862 |
+
displayValue = '';
|
863 |
+
}
|
864 |
+
return displayValue;
|
865 |
+
},
|
866 |
+
|
867 |
+
write: function(menuItem, value) {
|
868 |
+
value = parseInt(value, 10);
|
869 |
+
if (isNaN(value) || (value < 0)) {
|
870 |
+
value = 0;
|
871 |
+
}
|
872 |
+
value = Math.round(value);
|
873 |
+
|
874 |
+
if (value > 10000) {
|
875 |
+
value = 10000;
|
876 |
+
}
|
877 |
+
|
878 |
+
if (value === 0) {
|
879 |
+
menuItem.iframe_height = null;
|
880 |
+
} else {
|
881 |
+
menuItem.iframe_height = value;
|
882 |
+
}
|
883 |
+
|
884 |
+
}
|
885 |
+
}),
|
886 |
+
|
887 |
'colors' : $.extend({}, baseField, {
|
888 |
caption: 'Color scheme',
|
889 |
defaultValue: 'Default',
|
922 |
}
|
923 |
}),
|
924 |
|
925 |
+
'page_title' : $.extend({}, baseField, {
|
926 |
+
caption: "Window title",
|
927 |
+
standardCaption : true,
|
928 |
+
advanced : true
|
929 |
+
}),
|
930 |
+
|
931 |
'page_heading' : $.extend({}, baseField, {
|
932 |
caption: 'Page heading',
|
933 |
advanced : true,
|
1363 |
}
|
1364 |
}
|
1365 |
|
1366 |
+
//Use the custom menu title as the page title if the default page title matches the default menu title.
|
1367 |
+
//Note that if the page title is an empty string (''), WP automatically uses the menu title. So we do the same.
|
1368 |
+
if ((fieldName === 'page_title') && (entry.template_id !== '')) {
|
1369 |
+
var defaultPageTitle = itemTemplates.getDefaultValue(entry.template_id, 'page_title'),
|
1370 |
+
defaultMenuTitle = itemTemplates.getDefaultValue(entry.template_id, 'menu_title'),
|
1371 |
+
customMenuTitle = entry['menu_title'];
|
1372 |
+
|
1373 |
+
if (
|
1374 |
+
(customMenuTitle !== null)
|
1375 |
+
&& (customMenuTitle !== '')
|
1376 |
+
&& ((defaultPageTitle === '') || (defaultMenuTitle === defaultPageTitle))
|
1377 |
+
) {
|
1378 |
+
return customMenuTitle;
|
1379 |
+
}
|
1380 |
+
}
|
1381 |
+
|
1382 |
if (typeof defaultValue === 'undefined') {
|
1383 |
defaultValue = null;
|
1384 |
}
|
1450 |
});
|
1451 |
};
|
1452 |
|
1453 |
+
/**
|
1454 |
+
* Select the first menu item that has the specified URL.
|
1455 |
+
*
|
1456 |
+
* @param {string} boxSelector
|
1457 |
+
* @param {string} url
|
1458 |
+
* @param {boolean|null} [expandProperties]
|
1459 |
+
* @returns {JQuery}
|
1460 |
+
*/
|
1461 |
+
AmeEditorApi.selectMenuItemByUrl = function(boxSelector, url, expandProperties) {
|
1462 |
+
if (typeof expandProperties === 'undefined') {
|
1463 |
+
expandProperties = null;
|
1464 |
+
}
|
1465 |
+
|
1466 |
+
var box = $(boxSelector);
|
1467 |
+
if (box.is('#ws_submenu_box')) {
|
1468 |
+
box = box.find('.ws_submenu:visible').first();
|
1469 |
+
}
|
1470 |
+
|
1471 |
+
var containerNode =
|
1472 |
+
box.find('.ws_container')
|
1473 |
+
.filter(function() {
|
1474 |
+
var itemUrl = AmeEditorApi.getItemDisplayUrl($(this).data('menu_item'));
|
1475 |
+
return (itemUrl === url);
|
1476 |
+
})
|
1477 |
+
.first();
|
1478 |
+
|
1479 |
+
if (containerNode.length > 0) {
|
1480 |
+
AmeEditorApi.selectItem(containerNode);
|
1481 |
+
|
1482 |
+
if (expandProperties !== null) {
|
1483 |
+
var expandLink = containerNode.find('.ws_edit_link').first();
|
1484 |
+
if (expandLink.hasClass('ws_edit_link_expanded') !== expandProperties) {
|
1485 |
+
expandLink.click();
|
1486 |
+
}
|
1487 |
+
}
|
1488 |
+
}
|
1489 |
+
return containerNode;
|
1490 |
+
};
|
1491 |
+
|
1492 |
/***************************************************************************
|
1493 |
Parsing & encoding menu inputs
|
1494 |
***************************************************************************/
|
1980 |
submenuBox = $('#ws_submenu_box'),
|
1981 |
submenuDropZone = submenuBox.closest('.ws_main_container').find('.ws_dropzone');
|
1982 |
|
|
|
1983 |
var currentVisibleSubmenu = null;
|
1984 |
+
|
1985 |
+
/**
|
1986 |
+
* Select a menu item and show its submenu.
|
1987 |
+
*
|
1988 |
+
* @param {JQuery|HTMLElement} container Menu container node.
|
1989 |
+
*/
|
1990 |
+
function selectItem(container) {
|
1991 |
if (container.hasClass('ws_active')) {
|
1992 |
+
//The menu item is already selected.
|
1993 |
return;
|
1994 |
}
|
1995 |
|
2014 |
container.closest('.ws_main_container')
|
2015 |
.find('.ws_toolbar .ws_delete_menu_button')
|
2016 |
.toggleClass('ws_button_disabled', !canDeleteItem(container));
|
2017 |
+
}
|
2018 |
+
AmeEditorApi.selectItem = selectItem;
|
2019 |
+
|
2020 |
+
//Select the clicked menu item and show its submenu
|
2021 |
+
menuEditorNode.on('click', '.ws_container', (function () {
|
2022 |
+
selectItem($(this));
|
2023 |
}));
|
2024 |
|
2025 |
function updateSubmenuBoxHeight(selectedMenu) {
|
2377 |
|
2378 |
var capSelectorDropdown = $('#ws_cap_selector');
|
2379 |
var currentDropdownOwner = null; //The input element that the dropdown is currently associated with.
|
2380 |
+
var currentDropdownOwnerMenu = null; //The menu item that the above input belongs to.
|
2381 |
+
|
2382 |
+
var isDropdownBeingHidden = false, isSuggestionClick = false;
|
2383 |
|
2384 |
//Show/hide the capability drop-down list when the trigger button is clicked
|
2385 |
$('#ws_trigger_capability_dropdown').on('mousedown click', onDropdownTriggerClicked);
|
2390 |
var inputBox = null;
|
2391 |
var button = $(this);
|
2392 |
|
2393 |
+
var isInAccessEditor = false;
|
2394 |
+
isSuggestionClick = false;
|
2395 |
+
|
2396 |
//Find the input associated with the button that was clicked.
|
2397 |
if ( button.attr('id') === 'ws_trigger_capability_dropdown' ) {
|
2398 |
inputBox = $('#ws_extra_capability');
|
2399 |
+
isInAccessEditor = true;
|
2400 |
} else {
|
2401 |
inputBox = button.closest('.ws_edit_field').find('.ws_field_value').first();
|
2402 |
}
|
2439 |
width(inputBox.outerWidth());
|
2440 |
|
2441 |
currentDropdownOwner = inputBox;
|
2442 |
+
|
2443 |
+
currentDropdownOwnerMenu = null;
|
2444 |
+
if (isInAccessEditor) {
|
2445 |
+
currentDropdownOwnerMenu = AmeItemAccessEditor.getCurrentMenuItem();
|
2446 |
+
} else {
|
2447 |
+
currentDropdownOwnerMenu = currentDropdownOwner.closest('.ws_container').data('menu_item');
|
2448 |
+
}
|
2449 |
+
|
2450 |
capSelectorDropdown.focus();
|
2451 |
+
|
2452 |
+
capSuggestionFeature.show();
|
2453 |
}
|
2454 |
|
2455 |
//Also show it when the user presses the down arrow in the input field (doesn't work in Opera).
|
2459 |
}
|
2460 |
});
|
2461 |
|
2462 |
+
function hideCapSelector() {
|
2463 |
+
capSelectorDropdown.hide();
|
2464 |
+
capSuggestionFeature.hide();
|
2465 |
+
isSuggestionClick = false;
|
2466 |
+
}
|
2467 |
+
|
2468 |
//Event handlers for the drop-down lists themselves
|
2469 |
var dropdownNodes = $('.ws_dropdown');
|
2470 |
|
2471 |
// Hide capability drop-down when it loses focus.
|
2472 |
dropdownNodes.blur(function(){
|
2473 |
+
if (!isSuggestionClick) {
|
2474 |
+
hideCapSelector();
|
2475 |
+
}
|
2476 |
});
|
2477 |
|
2478 |
dropdownNodes.keydown(function(event){
|
2479 |
|
2480 |
//Hide it when the user presses Esc
|
2481 |
if ( event.which === 27 ){
|
2482 |
+
hideCapSelector();
|
2483 |
if (currentDropdownOwner) {
|
2484 |
currentDropdownOwner.focus();
|
2485 |
}
|
2486 |
|
2487 |
//Select an item & hide the list when the user presses Enter or Tab
|
2488 |
} else if ( (event.which === 13) || (event.which === 9) ){
|
2489 |
+
hideCapSelector();
|
2490 |
|
2491 |
if (currentDropdownOwner) {
|
2492 |
if ( capSelectorDropdown.val() ){
|
2510 |
//Update the input & hide the list when an option is clicked
|
2511 |
dropdownNodes.click(function(){
|
2512 |
if (capSelectorDropdown.val()){
|
2513 |
+
hideCapSelector();
|
2514 |
if (currentDropdownOwner) {
|
2515 |
currentDropdownOwner.val(capSelectorDropdown.val()).change().focus();
|
2516 |
}
|
2526 |
var option = event.target;
|
2527 |
if ( (typeof option.selected !== 'undefined') && !option.selected && option.value ){
|
2528 |
option.selected = true;
|
2529 |
+
|
2530 |
+
//Preview which roles have this capability and the required cap.
|
2531 |
+
capSuggestionFeature.previewAccessForItem(currentDropdownOwnerMenu, option.value);
|
2532 |
}
|
2533 |
});
|
2534 |
|
2535 |
+
/************************************************************************
|
2536 |
+
* Capability suggestions
|
2537 |
+
*************************************************************************/
|
2538 |
+
|
2539 |
+
var capSuggestionFeature = (function() {
|
2540 |
+
//This feature is not used in the Pro version because it has a different permission UI.
|
2541 |
+
if (wsEditorData.wsMenuEditorPro) {
|
2542 |
+
return {
|
2543 |
+
previewAccessForItem: function () {},
|
2544 |
+
show: function () {},
|
2545 |
+
hide: function () {}
|
2546 |
+
}
|
2547 |
+
}
|
2548 |
+
|
2549 |
+
var capabilitySuggestions = $('#ws_capability_suggestions'),
|
2550 |
+
suggestionBody = capabilitySuggestions.find('table tbody').first().empty(),
|
2551 |
+
suggestedCapabilities = AmeActors.getSuggestedCapabilities();
|
2552 |
+
|
2553 |
+
for (var i = 0; i < suggestedCapabilities.length; i++) {
|
2554 |
+
var role = suggestedCapabilities[i].role, capability = suggestedCapabilities[i].capability;
|
2555 |
+
$('<tr>')
|
2556 |
+
.data('role', role)
|
2557 |
+
.data('capability', capability)
|
2558 |
+
.append(
|
2559 |
+
$('<th>', {text: role.displayName, scope: 'row'}).addClass('ws_ame_role_name')
|
2560 |
+
)
|
2561 |
+
.append(
|
2562 |
+
$('<td>', {text: capability}).addClass('ws_ame_suggested_capability')
|
2563 |
+
)
|
2564 |
+
.appendTo(suggestionBody);
|
2565 |
+
}
|
2566 |
+
|
2567 |
+
var currentPreviewedCaps = null;
|
2568 |
+
|
2569 |
+
/**
|
2570 |
+
* Update the access preview.
|
2571 |
+
* @param {string|string[]|null} capabilities
|
2572 |
+
*/
|
2573 |
+
function previewAccess(capabilities) {
|
2574 |
+
if (typeof capabilities === 'string') {
|
2575 |
+
capabilities = [capabilities];
|
2576 |
+
}
|
2577 |
+
|
2578 |
+
if (_.isEqual(capabilities, currentPreviewedCaps)) {
|
2579 |
+
return;
|
2580 |
+
}
|
2581 |
+
currentPreviewedCaps = capabilities;
|
2582 |
+
capabilitySuggestions.find('#ws_previewed_caps').text(currentPreviewedCaps.join(' + '));
|
2583 |
+
|
2584 |
+
//Short-circuit the no-caps case.
|
2585 |
+
if (capabilities === null || capabilities.length === 0) {
|
2586 |
+
suggestionBody.find('tr').removeClass('ws_preview_has_access');
|
2587 |
+
return;
|
2588 |
+
}
|
2589 |
+
|
2590 |
+
suggestionBody.find('tr').each(function() {
|
2591 |
+
var $row = $(this),
|
2592 |
+
role = $row.data('role');
|
2593 |
+
|
2594 |
+
var hasCaps = true;
|
2595 |
+
for (var i = 0; i < capabilities.length; i++) {
|
2596 |
+
hasCaps = hasCaps && AmeActors.hasCap(role.id, capabilities[i]);
|
2597 |
+
}
|
2598 |
+
$row.toggleClass('ws_preview_has_access', hasCaps);
|
2599 |
+
});
|
2600 |
+
}
|
2601 |
+
|
2602 |
+
function previewAccessForItem(menuItem, selectedExtraCap) {
|
2603 |
+
var requiredCap = '', extraCap = '';
|
2604 |
+
|
2605 |
+
if (menuItem) {
|
2606 |
+
requiredCap = getFieldValue(menuItem, 'access_level', '');
|
2607 |
+
extraCap = getFieldValue(menuItem, 'extra_capability', '');
|
2608 |
+
}
|
2609 |
+
if (typeof selectedExtraCap !== 'undefined') {
|
2610 |
+
extraCap = selectedExtraCap;
|
2611 |
+
}
|
2612 |
+
|
2613 |
+
var caps = [];
|
2614 |
+
if (menuItem && (menuItem.template_id !== '') || (extraCap === '')) {
|
2615 |
+
caps.push(requiredCap);
|
2616 |
+
}
|
2617 |
+
if (extraCap !== '') {
|
2618 |
+
caps.push(extraCap);
|
2619 |
+
}
|
2620 |
+
|
2621 |
+
previewAccess(caps);
|
2622 |
+
}
|
2623 |
+
|
2624 |
+
suggestionBody.on('mouseenter', 'td.ws_ame_suggested_capability', function() {
|
2625 |
+
var row = $(this).closest('tr');
|
2626 |
+
previewAccessForItem(currentDropdownOwnerMenu, row.data('capability'));
|
2627 |
+
});
|
2628 |
+
|
2629 |
+
capSelectorDropdown.on('keydown keyup', function() {
|
2630 |
+
previewAccessForItem(currentDropdownOwnerMenu, capSelectorDropdown.val());
|
2631 |
+
});
|
2632 |
+
|
2633 |
+
suggestionBody.on('mousedown', 'td.ws_ame_suggested_capability', function() {
|
2634 |
+
//Don't immediately hide the list when the user tries to click a suggestion.
|
2635 |
+
//It would prevent the click from registering.
|
2636 |
+
isSuggestionClick = true;
|
2637 |
+
});
|
2638 |
+
|
2639 |
+
suggestionBody.on('click', 'td.ws_ame_suggested_capability', function() {
|
2640 |
+
var capability = $(this).closest('tr').data('capability');
|
2641 |
+
|
2642 |
+
//Change the input to the selected capability.
|
2643 |
+
if (currentDropdownOwner) {
|
2644 |
+
currentDropdownOwner.val(capability).change();
|
2645 |
+
}
|
2646 |
+
|
2647 |
+
hideCapSelector();
|
2648 |
+
});
|
2649 |
+
|
2650 |
+
//Workaround for pressing LMB on a suggestion, then moving the mouse outside the suggestion box and releasing the button.
|
2651 |
+
$(document).on('click', function(event) {
|
2652 |
+
if (
|
2653 |
+
isSuggestionClick
|
2654 |
+
&& capabilitySuggestions.is(':visible')
|
2655 |
+
&& ( $(event.target).closest(capabilitySuggestions).length < 1 )
|
2656 |
+
) {
|
2657 |
+
hideCapSelector();
|
2658 |
+
}
|
2659 |
+
});
|
2660 |
+
|
2661 |
+
return {
|
2662 |
+
previewAccessForItem: previewAccessForItem,
|
2663 |
+
show: function() {
|
2664 |
+
//Position the capability suggestion table next to the selector and match heights.
|
2665 |
+
capabilitySuggestions
|
2666 |
+
.css({
|
2667 |
+
position: 'absolute',
|
2668 |
+
zIndex: 1009
|
2669 |
+
})
|
2670 |
+
.show()
|
2671 |
+
.position({
|
2672 |
+
my: 'left top',
|
2673 |
+
at: 'right top',
|
2674 |
+
of: capSelectorDropdown,
|
2675 |
+
collision: 'none'
|
2676 |
+
});
|
2677 |
+
|
2678 |
+
var selectorHeight = capSelectorDropdown.height(),
|
2679 |
+
suggestionsHeight = capabilitySuggestions.height(),
|
2680 |
+
desiredHeight = Math.max(selectorHeight, suggestionsHeight);
|
2681 |
+
if (selectorHeight < desiredHeight) {
|
2682 |
+
capSelectorDropdown.height(desiredHeight);
|
2683 |
+
}
|
2684 |
+
if (suggestionsHeight < desiredHeight) {
|
2685 |
+
capabilitySuggestions.height(desiredHeight);
|
2686 |
+
}
|
2687 |
+
|
2688 |
+
if (currentDropdownOwnerMenu) {
|
2689 |
+
previewAccessForItem(currentDropdownOwnerMenu);
|
2690 |
+
}
|
2691 |
+
},
|
2692 |
+
hide: function() {
|
2693 |
+
capabilitySuggestions.hide();
|
2694 |
+
}
|
2695 |
+
};
|
2696 |
+
})();
|
2697 |
+
|
2698 |
+
|
2699 |
/*************************************************************************
|
2700 |
Icon selector
|
2701 |
*************************************************************************/
|
3164 |
|
3165 |
//Add menu title to the dialog caption.
|
3166 |
var title = getFieldValue(menuItem, 'menu_title', null);
|
3167 |
+
setUpColorDialog(title ? ('Colors: ' + formatMenuTitle(title)) : 'Colors');
|
3168 |
|
3169 |
//Show the [global] preset only if the user has set it up.
|
3170 |
var globalPresetExists = colorPresets.hasOwnProperty('[global]');
|
4188 |
$('#ws_data').val(data);
|
4189 |
$('#ws_data_length').val(data.length);
|
4190 |
$('#ws_selected_actor').val(actorSelectorWidget.selectedActor === null ? '' : actorSelectorWidget.selectedActor);
|
4191 |
+
|
4192 |
+
var selectedMenu = getSelectedMenu();
|
4193 |
+
if (selectedMenu.length > 0) {
|
4194 |
+
$('#ws_selected_menu_url').val(AmeEditorApi.getItemDisplayUrl(selectedMenu.data('menu_item')));
|
4195 |
+
$('#ws_expand_selected_menu').val(selectedMenu.find('.ws_editbox').is(':visible') ? '1' : '');
|
4196 |
+
|
4197 |
+
var selectedSubmenu = getSelectedSubmenuItem();
|
4198 |
+
if (selectedSubmenu.length > 0) {
|
4199 |
+
$('#ws_selected_submenu_url').val(AmeEditorApi.getItemDisplayUrl(selectedSubmenu.data('menu_item')));
|
4200 |
+
$('#ws_expand_selected_submenu').val(selectedSubmenu.find('.ws_editbox').is(':visible') ? '1' : '');
|
4201 |
+
}
|
4202 |
+
}
|
4203 |
+
|
4204 |
$('#ws_main_form').submit();
|
4205 |
});
|
4206 |
|
4613 |
//Finally, show the menu
|
4614 |
loadMenuConfiguration(customMenu);
|
4615 |
|
4616 |
+
//Select the previous selected menu, if any.
|
4617 |
+
if (wsEditorData.selectedMenu) {
|
4618 |
+
AmeEditorApi.selectMenuItemByUrl(
|
4619 |
+
'#ws_menu_box',
|
4620 |
+
wsEditorData.selectedMenu,
|
4621 |
+
_.get(wsEditorData, 'expandSelectedMenu') === '1'
|
4622 |
+
);
|
4623 |
+
|
4624 |
+
if (wsEditorData.selectedSubmenu) {
|
4625 |
+
AmeEditorApi.selectMenuItemByUrl(
|
4626 |
+
'#ws_submenu_box',
|
4627 |
+
wsEditorData.selectedSubmenu,
|
4628 |
+
_.get(wsEditorData, 'expandSelectedSubmenu') === '1'
|
4629 |
+
);
|
4630 |
+
}
|
4631 |
+
}
|
4632 |
+
|
4633 |
//... and make the UI visible now that it's fully rendered.
|
4634 |
menuEditorNode.css('visibility', 'visible');
|
4635 |
}
|
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.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.7.2
|
7 |
Author: Janis Elsts
|
8 |
Author URI: http://w-shadow.com/blog/
|
9 |
*/
|
modules/access-editor/access-editor.js
CHANGED
@@ -488,6 +488,10 @@ window.AmeItemAccessEditor = (function ($) {
|
|
488 |
$('#ws_role_access_container').toggleClass('ws_has_extended_permissions', hasExtendedPermissions);
|
489 |
},
|
490 |
|
|
|
|
|
|
|
|
|
491 |
detectExtPermissions: detectExtPermissions
|
492 |
};
|
493 |
})(jQuery);
|
488 |
$('#ws_role_access_container').toggleClass('ws_has_extended_permissions', hasExtendedPermissions);
|
489 |
},
|
490 |
|
491 |
+
getCurrentMenuItem: function() {
|
492 |
+
return menuItem;
|
493 |
+
},
|
494 |
+
|
495 |
detectExtPermissions: detectExtPermissions
|
496 |
};
|
497 |
})(jQuery);
|
readme.txt
CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_i
|
|
4 |
Tags: admin, dashboard, menu, security, wpmu
|
5 |
Requires at least: 4.1
|
6 |
Tested up to: 4.6
|
7 |
-
Stable tag: 1.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,13 @@ Plugins installed in the `mu-plugins` directory are treated as "always on", so y
|
|
63 |
|
64 |
== Changelog ==
|
65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
= 1.7.1 =
|
67 |
* Split the "required capability" field into two parts - a read-only field that shows the actual required capability, and an editable "extra capability" that you can use to restrict access to the menu.
|
68 |
* Added more detailed permission error messages. You can turn them off in the "Settings" tab by changing "Error verbosity level" to "Low".
|
4 |
Tags: admin, dashboard, menu, security, wpmu
|
5 |
Requires at least: 4.1
|
6 |
Tested up to: 4.6
|
7 |
+
Stable tag: 1.7.2
|
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.7.2 =
|
67 |
+
* Added capability suggestions and access preview to the "Extra capability" dropdown.
|
68 |
+
* The plugin now remembers the last selected menu item and re-selects it after you save changes.
|
69 |
+
* Fixed a layout issue where menus with very long titles would appear incorrectly in the menu editor.
|
70 |
+
* When you change the menu title, the window title will also be changed to match it. You can still edit the window title separately if necessary.
|
71 |
+
* Moved the "Icon URL" field up and moved "Window title" down.
|
72 |
+
|
73 |
= 1.7.1 =
|
74 |
* Split the "required capability" field into two parts - a read-only field that shows the actual required capability, and an editable "extra capability" that you can use to restrict access to the menu.
|
75 |
* Added more detailed permission error messages. You can turn them off in the "Settings" tab by changing "Error verbosity level" to "Low".
|