diff --git a/CRM/Core/BAO/Navigation.php b/CRM/Core/BAO/Navigation.php
index abb6f1801e7d..706e83822dd4 100644
--- a/CRM/Core/BAO/Navigation.php
+++ b/CRM/Core/BAO/Navigation.php
@@ -348,7 +348,7 @@ public static function buildNavigation() {
* buildNavigationTree retreives items in order. We call this function to
* ensure that any items added by the hook are also in the correct order.
*/
- private static function orderByWeight(&$navigations) {
+ public static function orderByWeight(&$navigations) {
// sort each item in navigations by weight
usort($navigations, function($a, $b) {
@@ -463,7 +463,7 @@ private static function _fixNavigationMenu(&$nodes, &$maxNavID, $parentID) {
}
/**
- * Get Menu name.
+ * Check permissions and format menu item as html.
*
* @param $value
* @param array $skipMenuItems
@@ -477,34 +477,50 @@ public static function getMenuName(&$value, &$skipMenuItems) {
$name = $i18n->crm_translate($value['attributes']['label'], array('context' => 'menu'));
$url = CRM_Utils_Array::value('url', $value['attributes']);
- $permission = CRM_Utils_Array::value('permission', $value['attributes']);
- $operator = CRM_Utils_Array::value('operator', $value['attributes']);
$parentID = CRM_Utils_Array::value('parentID', $value['attributes']);
$navID = CRM_Utils_Array::value('navID', $value['attributes']);
$active = CRM_Utils_Array::value('active', $value['attributes']);
$target = CRM_Utils_Array::value('target', $value['attributes']);
- if (in_array($parentID, $skipMenuItems) || !$active) {
+ if (in_array($parentID, $skipMenuItems) || !$active || !self::checkPermission($value['attributes'])) {
$skipMenuItems[] = $navID;
return FALSE;
}
- $config = CRM_Core_Config::singleton();
-
$makeLink = FALSE;
if (!empty($url)) {
$url = self::makeFullyFormedUrl($url);
$makeLink = TRUE;
}
- static $allComponents;
- if (!$allComponents) {
- $allComponents = CRM_Core_Component::getNames();
+ if (!empty($value['attributes']['icon'])) {
+ $menuIcon = sprintf('', $value['attributes']['icon']);
+ $name = $menuIcon . $name;
}
- if (isset($permission) && $permission) {
- $permissions = explode(',', $permission);
+ if ($makeLink) {
+ $url = CRM_Utils_System::evalUrl($url);
+ if ($target) {
+ $name = "{$name}";
+ }
+ else {
+ $name = "{$name}";
+ }
+ }
+ return $name;
+ }
+
+ /**
+ * Check if a menu item should be visible based on permissions and component.
+ *
+ * @param $item
+ * @return bool
+ */
+ public static function checkPermission($item) {
+ if (!empty($item['permission'])) {
+ $permissions = explode(',', $item['permission']);
+ $operator = CRM_Utils_Array::value('operator', $item);
$hasPermission = FALSE;
foreach ($permissions as $key) {
$key = trim($key);
@@ -514,13 +530,12 @@ public static function getMenuName(&$value, &$skipMenuItems) {
$componentName = CRM_Core_Permission::getComponentName($key);
if ($componentName) {
- if (!in_array($componentName, $config->enableComponents) ||
+ if (!in_array($componentName, CRM_Core_Config::singleton()->enableComponents) ||
!CRM_Core_Permission::check($key)
) {
$showItem = FALSE;
if ($operator == 'AND') {
- $skipMenuItems[] = $navID;
- return $showItem;
+ return FALSE;
}
}
else {
@@ -530,8 +545,7 @@ public static function getMenuName(&$value, &$skipMenuItems) {
elseif (!CRM_Core_Permission::check($key)) {
$showItem = FALSE;
if ($operator == 'AND') {
- $skipMenuItems[] = $navID;
- return $showItem;
+ return FALSE;
}
}
else {
@@ -539,28 +553,11 @@ public static function getMenuName(&$value, &$skipMenuItems) {
}
}
- if (!$showItem && !$hasPermission) {
- $skipMenuItems[] = $navID;
+ if (empty($showItem) && !$hasPermission) {
return FALSE;
}
}
-
- if (!empty($value['attributes']['icon'])) {
- $menuIcon = sprintf('', $value['attributes']['icon']);
- $name = $menuIcon . $name;
- }
-
- if ($makeLink) {
- $url = CRM_Utils_System::evalUrl($url);
- if ($target) {
- $name = "{$name}";
- }
- else {
- $name = "{$name}";
- }
- }
-
- return $name;
+ return TRUE;
}
/**
@@ -619,7 +616,7 @@ public static function createNavigation() {
*
* @return string
*/
- private static function makeFullyFormedUrl($url) {
+ public static function makeFullyFormedUrl($url) {
if (self::isNotFullyFormedUrl($url)) {
//CRM-7656 --make sure to separate out url path from url params,
//as we'r going to validate url path across cross-site scripting.
diff --git a/CRM/Core/Resources.php b/CRM/Core/Resources.php
index 7feafd260d69..9a3abf2c918e 100644
--- a/CRM/Core/Resources.php
+++ b/CRM/Core/Resources.php
@@ -611,6 +611,10 @@ public function addCoreResources($region = 'html-header') {
'isFrontend' => $config->userFrameworkFrontend,
),
);
+ $contactID = CRM_Core_Session::getLoggedInContactID();
+ if ($contactID) {
+ $settings['config']['menuCacheCode'] = CRM_Core_BAO_Navigation::getCacheKey($contactID);
+ }
// Disable profile creation if user lacks permission
if (!CRM_Core_Permission::check('edit all contacts') && !CRM_Core_Permission::check('add contacts')) {
$settings['config']['entityRef']['contactCreate'] = FALSE;
@@ -685,6 +689,7 @@ public static function outputLocalizationJS() {
),
'ajaxPopupsEnabled' => self::singleton()->ajaxPopupsEnabled,
'allowAlertAutodismissal' => (bool) Civi::settings()->get('allow_alert_autodismissal'),
+ 'resourceCacheCode' => self::singleton()->getCacheCode(),
);
print CRM_Core_Smarty::singleton()->fetchWith('CRM/common/l10n.js.tpl', $vars);
CRM_Utils_System::civiExit();
diff --git a/js/crm.drupal7.js b/js/crm.drupal7.js
index facd7f1a095b..fe3c5b9ec637 100644
--- a/js/crm.drupal7.js
+++ b/js/crm.drupal7.js
@@ -1,5 +1,5 @@
// http://civicrm.org/licensing
-CRM.$(function($) {
+(function($) {
$(document)
.on('dialogopen', function(e) {
// D7 hack to get the toolbar out of the way (CRM-15341)
@@ -10,8 +10,10 @@ CRM.$(function($) {
// D7 hack, restore toolbar position (CRM-15341)
$('#toolbar').css('z-index', '');
}
+ })
+ .on('crmLoad', '#civicrm-menu', function(e) {
+ if ($('#toolbar a.toggle').length) {
+ $('#civicrm-menu').css({width: 'calc(100% - 40px)'});
+ }
});
- if ($('#toolbar a.toggle').length) {
- $('#civicrm-menu').css({width: 'calc(100% - 40px)'});
- }
-});
+})(CRM.$);
diff --git a/js/crm.drupal8.js b/js/crm.drupal8.js
index 32c06a6bb43a..d141841815ed 100644
--- a/js/crm.drupal8.js
+++ b/js/crm.drupal8.js
@@ -8,7 +8,7 @@ CRM.$(function($) {
$('#toolbar-bar').hide();
- $('.crm-hidemenu').click(function(e) {
+ $('body').on('click', '.crm-hidemenu', function() {
$('#toolbar-bar').slideDown();
});
$('#crm-notification-container').on('click', '#crm-restore-menu', function() {
diff --git a/templates/CRM/common/l10n.js.tpl b/templates/CRM/common/l10n.js.tpl
index b2274723b373..436a6ea3547e 100644
--- a/templates/CRM/common/l10n.js.tpl
+++ b/templates/CRM/common/l10n.js.tpl
@@ -34,6 +34,7 @@
CRM.config.timeIs24Hr = {if $config->timeInputFormat eq 2}true{else}false{/if};
CRM.config.ajaxPopupsEnabled = {$ajaxPopupsEnabled|@json_encode};
CRM.config.allowAlertAutodismissal = {$allowAlertAutodismissal|@json_encode};
+ CRM.config.resourceCacheCode = {$resourceCacheCode|@json_encode};
// Merge entityRef settings
CRM.config.entityRef = $.extend({ldelim}{rdelim}, {$entityRef|@json_encode}, CRM.config.entityRef || {ldelim}{rdelim});
diff --git a/templates/CRM/common/navigation.js.tpl b/templates/CRM/common/navigation.js.tpl
index 5c3d589cc9fa..a84a169d8857 100644
--- a/templates/CRM/common/navigation.js.tpl
+++ b/templates/CRM/common/navigation.js.tpl
@@ -195,6 +195,7 @@ $('#civicrm-menu').ready(function() {
$('#root-menu-div').on('click', 'a', $.Menu.closeAll);
});
$('#civicrm-menu').menuBar({arrowClass: 'crm-i fa-caret-right'});
+$('#civicrm-menu').trigger('crmLoad');
$(window).on("beforeunload", function() {
$('.crm-logo-sm', '#civicrm-menu').addClass('crm-i fa-spin');
});
diff --git a/tests/phpunit/CRM/Core/BAO/NavigationTest.php b/tests/phpunit/CRM/Core/BAO/NavigationTest.php
index 1d65155731c0..fab453d83e67 100644
--- a/tests/phpunit/CRM/Core/BAO/NavigationTest.php
+++ b/tests/phpunit/CRM/Core/BAO/NavigationTest.php
@@ -284,4 +284,30 @@ public function testFixNavigationMenu_inferIDs_deep() {
$this->assertEquals(100, $output[10]['child'][101]['child'][100]['attributes']['navID']);
}
+ /**
+ * Tests that permissions and component status are checked with the correct operator.
+ */
+ public function testCheckPermissions() {
+ $menuItem = [
+ 'permission' => 'access CiviCRM, access CiviContribute',
+ 'operator' => 'AND'
+ ];
+ CRM_Core_BAO_ConfigSetting::enableComponent('CiviContribute');
+ CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviCRM', 'access CiviContribute'];
+ $this->assertTrue(CRM_Core_BAO_Navigation::checkPermission($menuItem));
+
+ CRM_Core_BAO_ConfigSetting::disableComponent('CiviContribute');
+ $this->assertFalse(CRM_Core_BAO_Navigation::checkPermission($menuItem));
+
+ CRM_Core_BAO_ConfigSetting::enableComponent('CiviContribute');
+ CRM_Core_Config::singleton()->userPermissionClass->permissions = ['access CiviContribute'];
+ $this->assertFalse(CRM_Core_BAO_Navigation::checkPermission($menuItem));
+
+ $menuItem['operator'] = 'OR';
+ $this->assertTrue(CRM_Core_BAO_Navigation::checkPermission($menuItem));
+
+ CRM_Core_BAO_ConfigSetting::disableComponent('CiviContribute');
+ $this->assertFalse(CRM_Core_BAO_Navigation::checkPermission($menuItem));
+ }
+
}