diff --git a/CRM/Admin/Form/Preferences/Display.php b/CRM/Admin/Form/Preferences/Display.php
index 2273f25b7fe8..ea13835328fb 100644
--- a/CRM/Admin/Form/Preferences/Display.php
+++ b/CRM/Admin/Form/Preferences/Display.php
@@ -51,6 +51,7 @@ class CRM_Admin_Form_Preferences_Display extends CRM_Admin_Form_Preferences {
'ajaxPopupsEnabled' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
'display_name_format' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
'sort_name_format' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
+ 'menubar_position' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
);
/**
diff --git a/CRM/Admin/Page/AJAX.php b/CRM/Admin/Page/AJAX.php
index 871b6d3e6d55..dc1f06589604 100644
--- a/CRM/Admin/Page/AJAX.php
+++ b/CRM/Admin/Page/AJAX.php
@@ -37,35 +37,76 @@
class CRM_Admin_Page_AJAX {
/**
- * CRM-12337 Output navigation menu as executable javascript.
- *
- * @see smarty_function_crmNavigationMenu
+ * Outputs menubar data (json format) for the current user.
*/
- public static function getNavigationMenu() {
- $contactID = CRM_Core_Session::singleton()->get('userID');
- if ($contactID) {
- CRM_Core_Page_AJAX::setJsHeaders();
- $smarty = CRM_Core_Smarty::singleton();
- $smarty->assign('quicksearchOptions', self::getSearchOptions());
- print $smarty->fetchWith('CRM/common/navigation.js.tpl', array(
- 'navigation' => CRM_Core_BAO_Navigation::createNavigation(),
- ));
+ public static function navMenu() {
+ if (CRM_Core_Session::getLoggedInContactID()) {
+
+ $menu = CRM_Core_BAO_Navigation::buildNavigationTree();
+ CRM_Core_BAO_Navigation::buildHomeMenu($menu);
+ CRM_Utils_Hook::navigationMenu($menu);
+ CRM_Core_BAO_Navigation::fixNavigationMenu($menu);
+ CRM_Core_BAO_Navigation::orderByWeight($menu);
+ CRM_Core_BAO_Navigation::filterByPermission($menu);
+ self::formatMenuItems($menu);
+
+ $output = [
+ 'menu' => $menu,
+ 'search' => CRM_Utils_Array::makeNonAssociative(self::getSearchOptions()),
+ ];
+ // Encourage browsers to cache for a long time - 1 year
+ $ttl = 60 * 60 * 24 * 364;
+ CRM_Utils_System::setHttpHeader('Expires', gmdate('D, d M Y H:i:s \G\M\T', time() + $ttl));
+ CRM_Utils_System::setHttpHeader('Cache-Control', "max-age=$ttl, public");
+ CRM_Utils_System::setHttpHeader('Content-Type', 'application/json');
+ print (json_encode($output));
}
CRM_Utils_System::civiExit();
}
+ /**
+ * @param array $menu
+ */
+ public static function formatMenuItems(&$menu) {
+ foreach ($menu as $key => &$item) {
+ $props = $item['attributes'];
+ unset($item['attributes']);
+ if (!empty($props['separator'])) {
+ $item['separator'] = ($props['separator'] == 1 ? 'bottom' : 'top');
+ }
+ if (!empty($props['icon'])) {
+ $item['icon'] = $props['icon'];
+ }
+ if (!empty($props['attr'])) {
+ $item['attr'] = $props['attr'];
+ }
+ if (!empty($props['url'])) {
+ $item['url'] = CRM_Utils_System::evalUrl(CRM_Core_BAO_Navigation::makeFullyFormedUrl($props['url']));
+ }
+ if (!empty($props['label'])) {
+ $item['label'] = ts($props['label'], ['context' => 'menu']);
+ }
+ $item['name'] = !empty($props['name']) ? $props['name'] : CRM_Utils_String::munge(CRM_Utils_Array::value('label', $props));
+ if (!empty($item['child'])) {
+ self::formatMenuItems($item['child']);
+ }
+ }
+ $menu = array_values($menu);
+ }
+
public static function getSearchOptions() {
- $searchOptions = Civi::settings()->get('quicksearch_options');
- $searchOptions[] = 'sort_name';
- $searchOptions = array_intersect_key(CRM_Core_SelectValues::quicksearchOptions(), array_flip($searchOptions));
- foreach ($searchOptions as $key => $label) {
+ $searchOptions = array_merge(['sort_name'], Civi::settings()->get('quicksearch_options'));
+ $labels = CRM_Core_SelectValues::quicksearchOptions();
+ $result = [];
+ foreach ($searchOptions as $key) {
+ $label = $labels[$key];
if (strpos($key, 'custom_') === 0) {
- unset($searchOptions[$key]);
- $id = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', substr($key, 7), 'id', 'name');
- $searchOptions["custom_$id"] = $label;
+ $key = 'custom_' . CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', substr($key, 7), 'id', 'name');
+ $label = array_slice(explode(': ', $label, 2), -1);
}
+ $result[$key] = $label;
}
- return $searchOptions;
+ return $result;
}
/**
diff --git a/CRM/Core/BAO/Navigation.php b/CRM/Core/BAO/Navigation.php
index 4006a300fef5..7e025198b67b 100644
--- a/CRM/Core/BAO/Navigation.php
+++ b/CRM/Core/BAO/Navigation.php
@@ -299,51 +299,6 @@ private static function buildTree($elements, $parentId = NULL) {
return $branch;
}
- /**
- * Build menu.
- *
- * @return string
- */
- public static function buildNavigation() {
- $navigations = self::buildNavigationTree();
- $navigationString = '';
-
- // run the Navigation through a hook so users can modify it
- CRM_Utils_Hook::navigationMenu($navigations);
- self::fixNavigationMenu($navigations);
-
- // Hooks have added menu items in an arbitrary order. We need to order by
- // weight again. I would put this function directly after
- // CRM_Utils_Hook::navigationMenu but for some reason, fixNavigationMenu is
- // moving items added by hooks on the end of the menu. Hence I do it
- // afterwards
- self::orderByWeight($navigations);
-
- //skip children menu item if user don't have access to parent menu item
- $skipMenuItems = array();
- foreach ($navigations as $key => $value) {
- // Home is a special case
- if ($value['attributes']['name'] != 'Home') {
- $name = self::getMenuName($value, $skipMenuItems);
- if ($name) {
- //separator before
- if (isset($value['attributes']['separator']) && $value['attributes']['separator'] == 2) {
- $navigationString .= '
';
- }
- $removeCharacters = array('/', '!', '&', '*', ' ', '(', ')', '.');
- $navigationString .= '' . $name;
- }
- }
- self::recurseNavigation($value, $navigationString, $skipMenuItems);
- }
-
- // clean up - Need to remove empty 's, this happens when user don't have
- // permission to access parent
- $navigationString = str_replace('
', '', $navigationString);
-
- return $navigationString;
- }
-
/**
* buildNavigationTree retreives items in order. We call this function to
* ensure that any items added by the hook are also in the correct order.
@@ -560,52 +515,6 @@ public static function checkPermission($item) {
return TRUE;
}
- /**
- * Create navigation for CiviCRM Admin Menu.
- *
- * @return string
- * returns navigation html
- */
- public static function createNavigation() {
- $navigation = self::buildNavigation();
-
- if ($navigation) {
-
- //add additional navigation items
- $logoutURL = CRM_Utils_System::url('civicrm/logout', 'reset=1');
-
- // get home menu from db
- $homeParams = array('name' => 'Home');
- $homeNav = array();
- $homeIcon = '';
- self::retrieve($homeParams, $homeNav);
- if ($homeNav) {
- $homeURL = self::makeFullyFormedUrl($homeNav['url']);
- $homeLabel = $homeNav['label'];
- // CRM-6804 (we need to special-case this as we don’t ts()-tag variables)
- if ($homeLabel == 'Home') {
- $homeLabel = ts('CiviCRM Home');
- }
- }
- else {
- $homeURL = CRM_Utils_System::url('civicrm/dashboard', 'reset=1');
- $homeLabel = ts('CiviCRM Home');
- }
- // Link to hide the menubar
- $hideLabel = ts('Hide Menu');
-
- $prepandString = "
- $homeIcon
- ";
- // tag doesn't need to be closed
- }
- return $prepandString . $navigation;
- }
-
/**
* Turns relative URLs (like civicrm/foo/bar) into fully-formed
* ones (i.e. example.com/wp-admin?q=civicrm/dashboard).
@@ -1051,4 +960,64 @@ public static function getCacheKey($cid) {
return $key;
}
+ /**
+ * Unset menu items for disabled components and non-permissioned users
+ *
+ * @param $menu
+ */
+ public static function filterByPermission(&$menu) {
+ foreach ($menu as $key => $item) {
+ if (
+ (array_key_exists('active', $item['attributes']) && !$item['attributes']['active']) ||
+ !CRM_Core_BAO_Navigation::checkPermission($item['attributes'])
+ ) {
+ unset($menu[$key]);
+ continue;
+ }
+ if (!empty($item['child'])) {
+ self::filterByPermission($menu[$key]['child']);
+ }
+ }
+ }
+
+ /**
+ * @param array $menu
+ */
+ public static function buildHomeMenu(&$menu) {
+ foreach ($menu as &$item) {
+ if (CRM_Utils_Array::value('name', $item['attributes']) === 'Home') {
+ unset($item['attributes']['label'], $item['attributes']['url']);
+ $item['attributes']['icon'] = 'crm-logo-sm';
+ $item['attributes']['attr']['accesskey'] = 'm';
+ $item['child'] = [
+ [
+ 'attributes' => [
+ 'label' => 'CiviCRM Home',
+ 'name' => 'CiviCRM Home',
+ 'url' => 'civicrm/dashboard?reset=1',
+ 'weight' => 1,
+ ]
+ ],
+ [
+ 'attributes' => [
+ 'label' => 'Hide Menu',
+ 'name' => 'Hide Menu',
+ 'url' => '#hidemenu',
+ 'weight' => 2,
+ ]
+ ],
+ [
+ 'attributes' => [
+ 'label' => 'Log out',
+ 'name' => 'Log out',
+ 'url' => 'civicrm/logout?reset=1',
+ 'weight' => 3,
+ ]
+ ],
+ ];
+ return;
+ }
+ }
+ }
+
}
diff --git a/CRM/Core/Resources.php b/CRM/Core/Resources.php
index bf98e4f6e985..3c9ecf006e9e 100644
--- a/CRM/Core/Resources.php
+++ b/CRM/Core/Resources.php
@@ -609,10 +609,6 @@ 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 +681,8 @@ public static function outputLocalizationJS() {
'ajaxPopupsEnabled' => self::singleton()->ajaxPopupsEnabled,
'allowAlertAutodismissal' => (bool) Civi::settings()->get('allow_alert_autodismissal'),
'resourceCacheCode' => self::singleton()->getCacheCode(),
+ 'locale' => CRM_Core_I18n::getLocale(),
+ 'cid' => (int) CRM_Core_Session::getLoggedInContactID(),
);
print CRM_Core_Smarty::singleton()->fetchWith('CRM/common/l10n.js.tpl', $vars);
CRM_Utils_System::civiExit();
@@ -746,11 +744,31 @@ public function coreResourceList($region) {
// These scripts are only needed by back-office users
if (CRM_Core_Permission::check('access CiviCRM')) {
$items[] = "packages/jquery/plugins/jquery.tableHeader.js";
- $items[] = "packages/jquery/plugins/jquery.menu.min.js";
- $items[] = "css/civicrmNavigation.css";
$items[] = "packages/jquery/plugins/jquery.notify.min.js";
}
+ $contactID = CRM_Core_Session::getLoggedInContactID();
+
+ // Menubar
+ $position = $contactID && CRM_Core_Permission::check('access CiviCRM') ? Civi::settings()->get('menubar_position') : 'none';
+ if ($position !== 'none' && !@constant('CIVICRM_DISABLE_DEFAULT_MENU') && !CRM_Core_Config::isUpgradeMode()) {
+ $cms = strtolower(CRM_Core_Config::singleton()->userFramework);
+ $cms = $cms === 'drupal' ? 'drupal7' : $cms;
+ $items[] = 'bower_components/smartmenus/dist/jquery.smartmenus.min.js';
+ $items[] = 'bower_components/smartmenus/dist/addons/keyboard/jquery.smartmenus.keyboard.min.js';
+ $items[] = 'js/crm.menubar.js';
+ $items[] = 'bower_components/smartmenus/dist/css/sm-core-css.css';
+ $items[] = 'css/crm-menubar.css';
+ $items[] = "css/menubar-$cms.css";
+ $items[] = [
+ 'menubar' => [
+ 'position' => $position ?: 'over-cms-menu',
+ 'qfKey' => CRM_Core_Key::get('CRM_Contact_Controller_Search', TRUE),
+ 'cacheCode' => CRM_Core_BAO_Navigation::getCacheKey($contactID),
+ ],
+ ];
+ }
+
// JS for multilingual installations
if (!empty($config->languageLimit) && count($config->languageLimit) > 1 && CRM_Core_Permission::check('translate CiviCRM')) {
$items[] = "js/crm.multilingual.js";
diff --git a/CRM/Core/Smarty/plugins/function.crmNavigationMenu.php b/CRM/Core/Smarty/plugins/function.crmNavigationMenu.php
deleted file mode 100644
index de8f873b8e4c..000000000000
--- a/CRM/Core/Smarty/plugins/function.crmNavigationMenu.php
+++ /dev/null
@@ -1,74 +0,0 @@
-userFrameworkFrontend) {
- $buildNavigation = FALSE;
- }
- if ($buildNavigation) {
- $session = CRM_Core_Session::singleton();
- $contactID = $session->get('userID');
- if ($contactID) {
- // These params force the browser to refresh the js file when switching user, domain, or language
- // We don't put them as a query string because some browsers will refuse to cache a page with a ? in the url
- // @see CRM_Admin_Page_AJAX::getNavigationMenu
- $lang = CRM_Core_I18n::getLocale();
- $domain = CRM_Core_Config::domainID();
- $key = CRM_Core_BAO_Navigation::getCacheKey($contactID);
- $src = CRM_Utils_System::url("civicrm/ajax/menujs/$contactID/$lang/$domain/$key");
- // CRM-15493 QFkey needed for quicksearch bar - must be unique on each page refresh so adding it directly to markup
- $qfKey = CRM_Core_Key::get('CRM_Contact_Controller_Search', TRUE);
- return '';
- }
- }
- return '';
-}
diff --git a/CRM/Core/xml/Menu/Admin.xml b/CRM/Core/xml/Menu/Admin.xml
index 918d372cae3e..d23a10910bee 100644
--- a/CRM/Core/xml/Menu/Admin.xml
+++ b/CRM/Core/xml/Menu/Admin.xml
@@ -633,9 +633,9 @@
9000
-
- civicrm/ajax/menujs
- CRM_Admin_Page_AJAX::getNavigationMenu
- access CiviCRM
+ civicrm/ajax/navmenu
+ CRM_Admin_Page_AJAX::navMenu
+ access CiviCRM
-
civicrm/ajax/menutree
diff --git a/bower.json b/bower.json
index fbd2229b4344..e40429345d85 100644
--- a/bower.json
+++ b/bower.json
@@ -30,6 +30,7 @@
"font-awesome": "~4",
"angular-bootstrap": "^2.5.0",
"angular-sanitize": "~1.5.0",
+ "smartmenus": "~1.1",
"phantomjs-polyfill": "^0.0.2"
},
"resolutions": {
diff --git a/css/civicrm.css b/css/civicrm.css
index 73e5e72fd82c..087f64c56c48 100644
--- a/css/civicrm.css
+++ b/css/civicrm.css
@@ -5,8 +5,7 @@
* Other civi blocks outside the main container also have the class crm-container (but not the id)
* All styles should start with .crm-container unless they are specific to the main div only
*/
-.crm-container input,
-#civicrm-menu input {
+.crm-container input {
box-sizing: content-box;
}
@@ -2922,7 +2921,6 @@ tbody.scrollContent tr.alternateRow {
.crm-container div.status,
-div.m ul#civicrm-menu,
.crm-container #help,
.crm-container .help,
.crm-container .ui-tabs-panel,
diff --git a/css/civicrmNavigation.css b/css/civicrmNavigation.css
deleted file mode 100644
index 5cf48d3f3cd8..000000000000
--- a/css/civicrmNavigation.css
+++ /dev/null
@@ -1,181 +0,0 @@
-html>body div.outerbox
-{
- padding: 0 5px 5px 0;
- z-index:999999;
- font-size: 13px;
- margin-top:2px;
-}
-html>body div.outerbox div.shadowbox1
-{
- position: absolute;
- right: 0;
- bottom: 5px;
- width: 5px;
- height: 100%;
- background: url(../i/myshadow.png) no-repeat right top;
-}
-html>body div.outerbox div.shadowbox2
-{
- position: absolute;
- bottom: 0;
- right: 5px;
- height: 5px;
- width: 100%;
- background: url(../i/myshadow.png) left bottom;
-}
-html>body div.outerbox div.shadowbox3
-{
- position: absolute;
- bottom: 0;
- right: 0;
- height: 5px;
- width: 5px;
- background: url(../i/myshadow.png) no-repeat right bottom;
-}
-html>body .innerbox
-{
- margin: 0;
- z-index:999999;
- margin-left:10px;
-}
-
-#root-menu-div ul {
- border: 1px solid #000;
-}
-#root-menu-div li{
- white-space:nowrap;
- background-image: none;
- text-align: left;
-}
-* html #root-menu-div li{
- height: 1.5em; /* fixing ie6 problem */
-}
-#civicrm-menu,
-#root-menu-div ul {
- list-style: none;
- margin: 0;
- padding: 0;
- background:#5D5D5D;
- color: white;
- cursor: default;
- font-size: 12px;
- font-family: Tahoma, Verdana, Arial, sans-serif;
-}
-
-#civicrm-menu {
- position:fixed;
- top:0;
- left:0;
- background:#1B1B1B repeat-x;
- width:100%;
- height:27px;
- z-index:99999;
- overflow: hidden;
-}
-
-li.menu-separator.active{
- background-color: transparent;
-}
-
-.menu-ul li.active {
- background-color: #aaa;
-}
-
-#civicrm-menu .activetarget{
- background-color: #aaa;
-}
-
-#civicrm-menu li a, #root-menu-div li a {
- color:white;
- cursor:pointer;
- display:block;
- font-weight:normal;
- text-decoration:none;
- border:0;
-}
-
-* html div.menu-item {
- display: inline; /* fixes problem in ie6 */
-}
-
-li.menumain {
- float: left;
- padding: 3px 10px;
- background-image: none;
- border-right: 1px solid #5D5D5D;
- margin:0;
-}
-
-#root-menu-div .menu-ul li {
- margin: 0;
- padding: 0 20px 0 2px;
-}
-
-div.menu-item {
- padding: 1px 10px 1px 4px;
- height: auto;
-}
-#civicrm-menu .menu-item-arrow,
-#root-menu-div .menu-item-arrow {
- position: absolute;
- right: 4px;
- top: 6px;
-}
-#civicrm-menu i,
-#root-menu-div i {
- margin-right: 5px;
-}
-li.menu-separator{
- border-bottom: 1px solid #000;
- font-size: 0; /* for ie */
- height: 0;
- line-height: 0; /* for ie */
- margin: 2px 0;
-}
-#civicrm-menu .crm-logo-sm,
-.crm-container .crm-logo-sm {
- background: url('../i/logo_sm.png') no-repeat;
- display: inline-block;
- width: 16px;
- height: 16px;
- vertical-align: middle;
-}
-
-#civicrm-menu .ui-autocomplete-input,
-.crm-container .ui-autocomplete-input {
- background: white url("../bower_components/select2/select2.png") no-repeat scroll right -23px;
- padding-right: 16px;
- /* so that text doesn't flow on top of icon */
-}
-
-#civicrm-menu #crm-qsearch {
- padding: 1px 0 1px 2px;
- background-color: transparent !important;
- border-right: none;
-}
-
-#civicrm-menu #crm-qsearch input {
- background-color: #eaeaea;
- border: 1px solid black;
- margin: 0;
- padding: 2px 16px 3px 2px;
- height: 17px;
-}
-#civicrm-menu #crm-qsearch input:hover,
-#civicrm-menu #crm-qsearch input:focus,
-#civicrm-menu #crm-qsearch.activetarget input {
- background-color: #ffffff;
-}
-/* This ensures the drop-down menus appear at the right height */
-#civicrm-menu > li.menumain {
- height: 19px !important;
-}
-
-/* No results */
-.crm-quickSearch-results.ui-menu-disabled {
- opacity: .9;
- background-color: #f8f8f8;
-}
-.crm-quickSearch-results.ui-menu-disabled li {
- cursor: default;
-}
diff --git a/css/crm-menubar.css b/css/crm-menubar.css
new file mode 100644
index 000000000000..06800779a664
--- /dev/null
+++ b/css/crm-menubar.css
@@ -0,0 +1,347 @@
+/* CiviCRM navigation menu stylesheet */
+
+#civicrm-menu-nav {
+ line-height: 0;
+ text-align: left;
+ font-size: 13px;
+}
+#civicrm-menu {
+ background-color: #f2f2f2;
+ width: 100%;
+ z-index: 500;
+ height: auto;
+ margin: 0;
+}
+#civicrm-menu i {
+ margin-right: 3px;
+}
+#civicrm-menu li {
+ border: none;
+ padding: 0;
+}
+#civicrm-menu li a {
+ padding: 12px 8px;
+ text-decoration: none;
+ color: #333;
+ box-shadow: none;
+ border: none;
+}
+#civicrm-menu li a[href="#"] {
+ cursor: default;
+}
+#civicrm-menu li li a {
+ padding: 6px 36px 6px 10px;
+}
+#civicrm-menu li.crm-menu-border-bottom:not(:last-child) {
+ border-bottom: 1px solid #bbb;
+}
+#civicrm-menu li:not(.crm-menu-border-bottom) + li.crm-menu-border-top {
+ border-top: 1px solid #bbb;
+}
+#civicrm-menu li a:focus,
+#civicrm-menu li a:hover,
+#civicrm-menu li a.highlighted {
+ text-decoration: none;
+ background-color: #fff;
+}
+#civicrm-menu li li .sub-arrow:before {
+ content: "\f0da";
+ font-family: 'FontAwesome';
+ color: #666;
+ float: right;
+ margin-right: -25px;
+}
+/* x icon */
+#crm-menubar-state:checked ~ .crm-menubar-toggle-btn .crm-menubar-toggle-btn-icon {
+ height: 0;
+ background: transparent;
+}
+#crm-menubar-state:checked ~ .crm-menubar-toggle-btn .crm-menubar-toggle-btn-icon:before {
+ top: 0;
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+}
+#crm-menubar-state:checked ~ .crm-menubar-toggle-btn .crm-menubar-toggle-btn-icon:after {
+ top: 0;
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+/* hide menu state checkbox (keep it visible to screen readers) */
+#civicrm-menu-nav #crm-menubar-state {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ border: 0;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(1px,1px,1px,1px);
+}
+#civicrm-menu-nav .crm-menubar-toggle-btn {
+ position: relative;
+ display: inline-block;
+ width: 57px;
+ height: 28px;
+ text-indent: 28px;
+ white-space: nowrap;
+ overflow: hidden;
+ cursor: pointer;
+ color: transparent;
+ -webkit-tap-highlight-color: rgba(0,0,0,0);
+ background-color: #333;
+}
+
+/* responsive icon */
+
+#civicrm-menu-nav .crm-menubar-toggle-btn-icon,
+#civicrm-menu-nav .crm-menubar-toggle-btn-icon:before,
+#civicrm-menu-nav .crm-menubar-toggle-btn-icon:after {
+ position: absolute;
+ top: 50%;
+ left: 27px;
+ height: 2px;
+ width: 24px;
+ background: #bbb;
+ -webkit-transition: all 0.25s;
+ transition: all 0.25s;
+}
+#civicrm-menu-nav .crm-menubar-toggle-btn-icon:before {
+ content: '';
+ top: -7px;
+ left: 0;
+}
+#civicrm-menu-nav .crm-menubar-toggle-btn-icon:after {
+ content: '';
+ top: 7px;
+ left: 0;
+}
+
+/* Quicksearch */
+#crm-qsearch {
+ padding: 1px 0 1px 2px;
+ background-color: transparent !important;
+}
+#civicrm-menu #crm-qsearch > a {
+ padding: 2px 0 0 2px;
+}
+
+input#crm-qsearch-input {
+ box-sizing: border-box;
+ background-color: #eaeaea;
+ font-size: 13px;
+ border: 1px solid #ccc;
+ margin: 4px 4px 0;
+ padding: 2px 8px;
+ height: 30px;
+ width: 30px;
+ transition: width .5s .05s, background-color .3s .05s;
+}
+a.highlighted #crm-qsearch-input,
+#crm-qsearch-input:focus,
+#crm-qsearch-input.has-user-input {
+ background-color: white;
+ width: 130px;
+}
+input#crm-qsearch-input:-ms-input-placeholder {
+ font-family: 'FontAwesome';
+}
+input#crm-qsearch-input::-webkit-input-placeholder {
+ font-family: 'FontAwesome';
+}
+input#crm-qsearch-input::-moz-placeholder {
+ font-family: 'FontAwesome';
+}
+input#crm-qsearch-input::placeholder {
+ font-family: 'FontAwesome';
+}
+
+ul.crm-quickSearch-results {
+ z-index: 100001;
+}
+ul.crm-quickSearch-results.ui-state-disabled {
+ opacity: .8;
+}
+
+#civicrm-menu-nav .crm-logo-sm {
+ background: url(../i/logo_sm.png) no-repeat;
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ margin: 0 2px;
+}
+
+#civicrm-menu #crm-menubar-toggle-position {
+ float: right;
+}
+#civicrm-menu #crm-menubar-toggle-position a i {
+ color: #888;
+ margin: 0;
+ border-top: 2px solid #888;
+ font-size: 11px;
+}
+body.crm-menubar-over-cms-menu #crm-menubar-toggle-position a i {
+ transform: rotate(180deg);
+}
+
+@media (min-width: 768px) {
+
+ /* Switch to desktop layout
+ -----------------------------------------------
+ These transform the menu tree from
+ collapsible to desktop (navbar + dropdowns)
+ -----------------------------------------------*/
+ /* start... (it's not recommended editing these rules) */
+ #civicrm-menu ul{position:absolute;width:12em;}
+ #civicrm-menu li{float:left;}
+ #civicrm-menu.sm-rtl li{float:right;}
+ #civicrm-menu ul li,#civicrm-menu.sm-rtl ul li,#civicrm-menu.sm-vertical li{float:none;}
+ #civicrm-menu a{white-space:nowrap;}
+ #civicrm-menu ul a,#civicrm-menu.sm-vertical a{white-space:normal;}
+ #civicrm-menu .sm-nowrap > li > a,#civicrm-menu .sm-nowrap > li > :not(ul) a{white-space:nowrap;}
+ /* ...end */
+
+ /* hide the button in desktop view */
+ #civicrm-menu-nav .crm-menubar-toggle-btn {
+ position: absolute;
+ top: -99999px;
+ }
+
+ #civicrm-menu {
+ border-bottom: 1px solid #ccc;
+ }
+
+ body.crm-menubar-below-cms-menu > #civicrm-menu-nav #civicrm-menu {
+ top: 30px;
+ }
+
+ #civicrm-menu ul {
+ background-color: #fff;
+ box-shadow: 0px 0px 2px 0 rgba(0,0,0,0.3);
+ }
+
+ #civicrm-menu > li > a {
+ height: 40px;
+ }
+
+ #civicrm-menu > li > a.highlighted {
+ z-index: 200000;
+ }
+
+ #civicrm-menu ul li a:focus,
+ #civicrm-menu ul li a:hover,
+ #civicrm-menu ul li a.highlighted {
+ background-color: #f2f2f2;
+ color: #222;
+ }
+
+ body.crm-menubar-over-cms-menu #civicrm-menu,
+ body.crm-menubar-below-cms-menu #civicrm-menu {
+ position: fixed;
+ top: 0;
+ }
+
+ body.crm-menubar-over-cms-menu #civicrm-menu {
+ z-index: 99999;
+ }
+
+ body.crm-menubar-hidden #civicrm-menu {
+ display: none;
+ }
+}
+
+@media (max-width: 768px) {
+ /* hide the menu in mobile view */
+ #crm-menubar-state:not(:checked) ~ #civicrm-menu {
+ display: none;
+ }
+ #civicrm-menu {
+ z-index: 100000;
+ background-color: #333;
+ }
+ #civicrm-menu ul {
+ background-color: #444;
+ }
+ #civicrm-menu ul ul {
+ background-color: #555;
+ }
+ #civicrm-menu ul ul ul {
+ background-color: #666;
+ }
+ #civicrm-menu li {
+ padding: 5px;
+ }
+ #civicrm-menu li a {
+ text-align: center;
+ font-size: 14px;
+ color: #ddd;
+ }
+ #civicrm-menu li a:focus,
+ #civicrm-menu li a:hover,
+ #civicrm-menu li a.highlighted {
+ background-color: #676767;
+ color: #fff;
+ }
+ #civicrm-menu li .sub-arrow:before,
+ #civicrm-menu li li .sub-arrow:before {
+ content: "\f0da";
+ font-family: 'FontAwesome';
+ color: #bbb;
+ float: none;
+ margin-left: 10px;
+ }
+ #civicrm-menu li a.highlighted .sub-arrow:before {
+ content: "\f0d7";
+ }
+ #civicrm-menu-nav {
+ position: fixed;
+ background: transparent;
+ pointer-events: none;
+ top: 0;
+ left: 0;
+ height: 50px;
+ width: 100%;
+ z-index: 100000;
+ }
+ #civicrm-menu-nav > * {
+ pointer-events: auto;
+ }
+ #civicrm-menu-nav .crm-menubar-toggle-btn {
+ margin-left: 20px;
+ z-index: 110000;
+ margin-top: 10px;
+ }
+ #civicrm-menu-nav .crm-menubar-toggle-btn span.crm-menu-logo {
+ display: block;
+ position: absolute;
+ left: 5px;
+ width: 18px;
+ height: 18px;
+ background: url(../i/logo_lg.png) no-repeat;
+ background-size: 18px;
+ top: 6px;
+ }
+ #crm-qsearch {
+ text-align: center;
+ }
+ #crm-qsearch .sub-arrow {
+ display: none;
+ }
+
+ #civicrm-menu li[data-name="Hide Menu"] {
+ display: none;
+ }
+
+ #crm-qsearch-input {
+ width: 14em !important;
+ }
+
+ #crm-menubar-toggle-position {
+ display: none;
+ }
+
+ /* Make sure we can click overlapped submenus in responsive mode */
+ #civicrm-menu li ul li {
+ z-index: 110000;
+ background-color: inherit;
+ }
+}
diff --git a/css/joomla.css b/css/joomla.css
index 6d00363f97e1..d49a76489824 100644
--- a/css/joomla.css
+++ b/css/joomla.css
@@ -294,10 +294,6 @@ br.clear {
/* Joomla Admin Menu alterations */
/* Moved from civicrm.css in v3.2 */
-ul#civicrm-menu {
- position:relative;
- z-index: 1;
-}
div#toolbar-box div.m {
padding: 0px !important;
@@ -310,9 +306,6 @@ div#toolbar-box, div#toolbar-box div.m{
height: auto;
}
-ul#civicrm-menu li#crm-qsearch {
- height:0px;
-}
.crm-tab-button,
.ui-tabs .ui-tabs-nav li {
border: 1px;
@@ -399,19 +392,3 @@ body.admin.com_civicrm #crm-nav-menu-container {
body.admin.com_civicrm #content-right {
padding: 12px;
}
-body.admin.com_civicrm #civicrm-menu #crm-qsearch {
- padding-left: 20px;
-}
-body.admin.com_civicrm #root-menu-div div.outerbox:first-of-type {
- margin-left: 20px;
-}
-body.admin.com_civicrm div.outerbox {
- z-index: 1000;
-}
-
-/* Shoreditch-specific */
-
-body.admin.com_civicrm {
- padding-top: 0px !important;
- margin-top: 30px !important;
-}
diff --git a/css/menubar-backdrop.css b/css/menubar-backdrop.css
new file mode 100644
index 000000000000..b238c51f40e1
--- /dev/null
+++ b/css/menubar-backdrop.css
@@ -0,0 +1,44 @@
+@media (min-width: 768px) {
+
+ body.crm-menubar-visible.crm-menubar-over-cms-menu {
+ border-top: 0 none !important;
+ margin-top: 40px;
+ }
+ body.crm-menubar-visible.crm-menubar-over-cms-menu.crm-menubar-wrapped {
+ margin-top: 80px;
+ }
+ body.crm-menubar-visible.crm-menubar-over-cms-menu #admin-bar {
+ visibility: hidden;
+ }
+
+ body.crm-menubar-visible.crm-menubar-below-cms-menu {
+ padding-top: 37px;
+ }
+ body.crm-menubar-visible.crm-menubar-below-cms-menu.crm-menubar-wrapped {
+ padding-top: 77px;
+ }
+ .admin-bar body.crm-menubar-below-cms-menu #civicrm-menu {
+ z-index: 999;
+ }
+ .admin-bar body.backdrop-admin-bar-position-absolute #civicrm-menu {
+ position: absolute;
+ }
+ body.crm-menubar-below-cms-menu #admin-bar {
+ z-index: 1000;
+ }
+
+}
+@media (max-width: 768px) {
+
+ body.backdrop-admin-bar-position-absolute #civicrm-menu-nav {
+ position: absolute;
+ }
+
+ body #civicrm-menu-nav .crm-menubar-toggle-btn {
+ position: absolute;
+ right: 120px;
+ top: 0;
+ margin-top: 3px;
+ }
+
+}
diff --git a/css/menubar-drupal7.css b/css/menubar-drupal7.css
new file mode 100644
index 000000000000..8317feb02205
--- /dev/null
+++ b/css/menubar-drupal7.css
@@ -0,0 +1,115 @@
+@media (min-width: 768px) {
+
+ body.crm-menubar-visible.crm-menubar-over-cms-menu #toolbar {
+ display: none;
+ }
+
+ body.crm-menubar-visible.crm-menubar-over-cms-menu {
+ padding-top: 40px !important;
+ }
+ body.crm-menubar-visible.crm-menubar-over-cms-menu.crm-menubar-wrapped {
+ padding-top: 80px !important;
+ }
+
+ body.crm-menubar-visible.crm-menubar-over-cms-menu #toolbar .toolbar-drawer {
+ display: none !important;
+ }
+
+ body.toolbar.crm-menubar-visible.crm-menubar-below-cms-menu {
+ padding-top: 70px !important;
+ }
+ body.toolbar.crm-menubar-visible.crm-menubar-below-cms-menu.crm-menubar-wrapped {
+ padding-top: 110px !important;
+ }
+
+ body.toolbar.toolbar-drawer.crm-menubar-visible.crm-menubar-below-cms-menu {
+ padding-top: 104px !important;
+ }
+ body.toolbar.toolbar-drawer.crm-menubar-visible.crm-menubar-below-cms-menu.crm-menubar-wrapped {
+ padding-top: 144px !important;
+ }
+
+ body.toolbar.crm-menubar-visible.crm-menubar-below-cms-menu #toolbar {
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ }
+
+ body.toolbar.toolbar-drawer.crm-menubar-below-cms-menu #civicrm-menu {
+ top: 64px !important;
+ }
+
+ /* For admin_menu */
+ body.admin-menu.crm-menubar-visible.crm-menubar-over-cms-menu {
+ margin-top: 0 !important;
+ }
+ body.crm-menubar-visible.crm-menubar-below-cms-menu #admin-menu {
+ min-height: 30px;
+ }
+ body.crm-menubar-visible.crm-menubar-below-cms-menu.admin-menu {
+ padding-top: 40px !important;
+ }
+ body.crm-menubar-visible.crm-menubar-below-cms-menu.crm-menubar-wrapped.admin-menu {
+ padding-top: 80px !important;
+ }
+ body.crm-menubar-visible.crm-menubar-over-cms-menu #admin-menu {
+ display: none;
+ }
+
+ /* For adminimal_admin_menu */
+ body.crm-menubar-visible.crm-menubar-over-cms-menu.admin-menu.adminimal-menu:before {
+ height: 0;
+ }
+ body.crm-menubar-visible.crm-menubar-below-cms-menu.admin-menu.adminimal-menu.menu-render-newline #civicrm-menu-nav #civicrm-menu {
+ top: 55px;
+ }
+
+}
+
+/* For adminimal_admin_menu */
+@media (min-width: 768px) and (max-width: 1024px) {
+
+ body.crm-menubar-visible.crm-menubar-over-cms-menu.admin-menu.adminimal-menu > .slicknav_menu {
+ display: none;
+ }
+ body.crm-menubar-visible.crm-menubar-below-cms-menu.admin-menu.adminimal-menu {
+ padding-top: 0px !important;
+ }
+ body.crm-menubar-below-cms-menu.admin-menu.adminimal-menu > #civicrm-menu-nav #civicrm-menu {
+ top: 0px !important;
+ }
+}
+
+@media (max-width: 768px) {
+
+ body.toolbar.crm-menubar-visible #toolbar-home {
+ visibility: hidden;
+ }
+ body.crm-menubar-visible #toolbar-menu {
+ display: none;
+ }
+
+ body #civicrm-menu-nav .crm-menubar-toggle-btn {
+ margin-top: 0;
+ }
+
+ /* For admin_menu */
+ body.admin-menu #civicrm-menu-nav .crm-menubar-toggle-btn {
+ margin-left: 0;
+ }
+ body.admin-menu.crm-menubar-visible #admin-menu-icon > li.admin-menu-icon > a {
+ visibility: hidden;
+ width: 40px;
+ }
+
+ /* For adminimal_admin_menu */
+ body.admin-menu.adminimal-menu #civicrm-menu-nav {
+ position: absolute;
+ }
+ body.admin-menu.adminimal-menu #civicrm-menu-nav .crm-menubar-toggle-btn {
+ float: right;
+ margin-top: 9px;
+ margin-right: 50px;
+ }
+
+}
diff --git a/css/menubar-drupal8.css b/css/menubar-drupal8.css
new file mode 100644
index 000000000000..868a7864dad0
--- /dev/null
+++ b/css/menubar-drupal8.css
@@ -0,0 +1,55 @@
+#toolbar-tray-civicrm {
+ display: none;
+}
+
+body.crm-menubar-visible.crm-menubar-over-cms-menu,
+body.crm-menubar-visible.crm-menubar-below-cms-menu {
+ margin-left: 0 !important;
+}
+
+nav#civicrm-menu-nav .crm-menubar-toggle-btn {
+ margin: 0;
+ position: absolute;
+ top: 0;
+ height: 38px;
+}
+#crm-menubar-state:checked ~ .crm-menubar-toggle-btn {
+ left: 0!important;
+}
+nav#civicrm-menu-nav .crm-menubar-toggle-btn span.crm-menu-logo {
+ top: 10px;
+ left: 20px;
+}
+nav#civicrm-menu-nav .crm-menubar-toggle-btn-icon {
+ left: 44px;
+}
+
+@media (min-width: 768px) {
+
+ body.crm-menubar-visible.crm-menubar-over-cms-menu #toolbar-administration {
+ display: none;
+ }
+
+ body.crm-menubar-visible.crm-menubar-over-cms-menu {
+ padding-top: 40px !important;
+ }
+ body.crm-menubar-visible.crm-menubar-over-cms-menu.crm-menubar-wrapped,
+ body.crm-menubar-visible.crm-menubar-below-cms-menu {
+ padding-top: 80px !important;
+ }
+ body.crm-menubar-visible.crm-menubar-below-cms-menu.crm-menubar-wrapped {
+ padding-top: 120px !important;
+ }
+
+ body.crm-menubar-below-cms-menu > #civicrm-menu-nav ul#civicrm-menu {
+ z-index: 1000;
+ top: 40px;
+ }
+
+}
+
+@media (max-width: 609px) {
+ nav#civicrm-menu-nav {
+ position: absolute;
+ }
+}
diff --git a/css/menubar-joomla.css b/css/menubar-joomla.css
new file mode 100644
index 000000000000..482f8c2ac643
--- /dev/null
+++ b/css/menubar-joomla.css
@@ -0,0 +1,29 @@
+@media (min-width: 768px) {
+
+ body.crm-menubar-over-cms-menu.crm-menubar-visible {
+ padding-top: 40px;
+ }
+ body.crm-menubar-over-cms-menu.crm-menubar-visible.crm-menubar-wrapped {
+ padding-top: 80px;
+ }
+
+ body.crm-menubar-below-cms-menu.crm-menubar-visible {
+ margin-top: 40px;
+ }
+ body.crm-menubar-below-cms-menu.crm-menubar-visible.crm-menubar-wrapped {
+ margin-top: 80px;
+ }
+
+}
+@media (max-width: 768px) {
+
+ body #civicrm-menu-nav {
+ position: absolute;
+ }
+
+ body #civicrm-menu-nav .crm-menubar-toggle-btn {
+ margin-top: 3px;
+ margin-left: 6px;
+ }
+
+}
diff --git a/css/menubar-wordpress.css b/css/menubar-wordpress.css
new file mode 100644
index 000000000000..33d227418da8
--- /dev/null
+++ b/css/menubar-wordpress.css
@@ -0,0 +1,59 @@
+@media (min-width: 768px) {
+
+ body.crm-menubar-over-cms-menu.crm-menubar-visible #wpbody {
+ padding-top: 8px;
+ }
+ body.crm-menubar-over-cms-menu.crm-menubar-visible.crm-menubar-wrapped #wpbody {
+ padding-top: 48px;
+ }
+
+ body.crm-menubar-over-cms-menu.crm-menubar-visible #wpadminbar {
+ visibility: hidden;
+ }
+
+ .wp-toolbar body.crm-menubar-below-cms-menu > #civicrm-menu-nav #civicrm-menu {
+ top: 32px;
+ left: 160px;
+ width: calc(100% - 160px);
+ }
+
+ .wp-toolbar body.crm-menubar-below-cms-menu.folded > #civicrm-menu-nav #civicrm-menu {
+ left: 36px;
+ width: calc(100% - 36px);
+ }
+
+ body.crm-menubar-below-cms-menu.crm-menubar-visible #wpbody {
+ padding-top: 40px;
+ }
+ body.crm-menubar-below-cms-menu.crm-menubar-visible.crm-menubar-wrapped #wpbody {
+ padding-top: 80px;
+ }
+ body.crm-menubar-over-cms-menu.crm-menubar-visible.crm-menubar-wrapped #adminmenuwrap {
+ margin-top: 40px;
+ }
+
+}
+@media (min-width: 768px) and (max-width: 960px) {
+
+ /* For the auto-fold toolbar */
+ .wp-toolbar body.crm-menubar-below-cms-menu.auto-fold > #civicrm-menu-nav #civicrm-menu {
+ left: 36px;
+ width: calc(100% - 36px);
+ }
+
+}
+@media (max-width: 768px) {
+
+ body #civicrm-menu-nav .crm-menubar-toggle-btn {
+ position: absolute;
+ right: 50px;
+ }
+
+}
+@media (max-width: 600px) {
+
+ body #civicrm-menu-nav {
+ position: absolute;
+ }
+
+}
diff --git a/extension-compatibility.json b/extension-compatibility.json
index 2f8bd4b81332..10efa35a7d40 100644
--- a/extension-compatibility.json
+++ b/extension-compatibility.json
@@ -1,4 +1,10 @@
{
+ "uk.squiffle.kam": {
+ "obsolete": "5.12"
+ },
+ "com.aghstrategies.slicknav": {
+ "obsolete": "5.12"
+ },
"com.ixiam.modules.quicksearch": {
"obsolete": "5.8"
}
diff --git a/js/crm.backdrop.js b/js/crm.backdrop.js
index 911982de0366..9620b4e30cfd 100644
--- a/js/crm.backdrop.js
+++ b/js/crm.backdrop.js
@@ -1,10 +1,8 @@
// http://civicrm.org/licensing
-CRM.$(function($) {
- $('#admin-bar').css('display', 'none');
- $('.crm-hidemenu').click(function(e) {
- $('#admin-bar').css('display', 'block');
+(function($) {
+ $(document).on('crmLoad', '#civicrm-menu', function() {
+ if (Backdrop.settings.admin_bar && !Backdrop.settings.admin_bar.position_fixed) {
+ $('body').addClass('backdrop-admin-bar-position-absolute');
+ }
});
- $('#crm-notification-container').on('click', '#crm-restore-menu', function() {
- $('#admin-bar').css('display', 'none');
- });
-});
+})(CRM.$);
diff --git a/js/crm.drupal7.js b/js/crm.drupal7.js
index fe3c5b9ec637..8dc539d50279 100644
--- a/js/crm.drupal7.js
+++ b/js/crm.drupal7.js
@@ -10,10 +10,5 @@
// 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)'});
- }
});
})(CRM.$);
diff --git a/js/crm.drupal8.js b/js/crm.drupal8.js
index d141841815ed..74e64cb77929 100644
--- a/js/crm.drupal8.js
+++ b/js/crm.drupal8.js
@@ -1,17 +1,29 @@
// http://civicrm.org/licensing
-CRM.$(function($) {
- // d8 Hack to hide title when it should be (CRM-19960)
- var pageTitle = $('.page-title');
- if ('CiviCRM' == pageTitle.text()) {
- pageTitle.hide();
- }
- $('#toolbar-bar').hide();
+// When on a CiviCRM page the CiviCRM toolbar tab should be active
+localStorage.setItem('Drupal.toolbar.activeTabID', JSON.stringify('toolbar-item-civicrm'));
- $('body').on('click', '.crm-hidemenu', function() {
- $('#toolbar-bar').slideDown();
- });
- $('#crm-notification-container').on('click', '#crm-restore-menu', function() {
- $('#toolbar-bar').slideUp();
- });
-});
+(function($) {
+ function adjustToggle() {
+ if ($(window).width() < 768) {
+ $('#civicrm-menu-nav .crm-menubar-toggle-btn').css({
+ left: '' + $('#toolbar-item-civicrm').offset().left + 'px',
+ width: '' + $('#toolbar-item-civicrm').innerWidth() + 'px'
+ });
+ }
+ }
+ $(window).resize(adjustToggle);
+ $(document).on('crmLoad', adjustToggle);
+
+ // Wait for document.ready so Drupal's jQuery is available to this script
+ $(function($) {
+ // Need Drupal's jQuery to listen to this event
+ jQuery(document).on('drupalToolbarTabChange', function(event, tab) {
+ if (CRM.menubar && CRM.menubar.position === 'below-cms-menu') {
+ var action = jQuery(tab).is('#toolbar-item-civicrm') ? 'show' : 'hide';
+ CRM.menubar[action]();
+ }
+ });
+ });
+
+})(CRM.$);
diff --git a/js/crm.menubar.js b/js/crm.menubar.js
new file mode 100644
index 000000000000..ad5fccbb3afa
--- /dev/null
+++ b/js/crm.menubar.js
@@ -0,0 +1,485 @@
+// https://civicrm.org/licensing
+(function($, _) {
+ "use strict";
+ var templates, initialized,
+ ENTER_KEY = 13,
+ SPACE_KEY = 32;
+ CRM.menubar = _.extend({
+ data: null,
+ settings: {collapsibleBehavior: 'accordion'},
+ position: 'over-cms-menu',
+ attachTo: (CRM.menubar && CRM.menubar.position === 'above-crm-container') ? '#crm-container' : 'body',
+ initialize: function() {
+ var cache = CRM.cache.get('menubar');
+ if (cache && cache.code === CRM.menubar.cacheCode && cache.locale === CRM.config.locale && cache.cid === CRM.config.cid && localStorage.civiMenubar) {
+ CRM.menubar.data = cache.data;
+ insert(localStorage.civiMenubar);
+ } else {
+ $.getJSON(CRM.url('civicrm/ajax/navmenu', {code: CRM.menubar.cacheCode, locale: CRM.config.locale, cid: CRM.config.cid}))
+ .done(function(data) {
+ var markup = getTpl('tree')(data);
+ CRM.cache.set('menubar', {code: CRM.menubar.cacheCode, locale: CRM.config.locale, cid: CRM.config.cid, data: data});
+ CRM.menubar.data = data;
+ localStorage.setItem('civiMenubar', markup);
+ insert(markup);
+ });
+ }
+
+ // Wait for crm-container present on the page as it's faster than document.ready
+ function insert(markup) {
+ if ($('#crm-container').length) {
+ render(markup);
+ } else {
+ new MutationObserver(function(mutations, observer) {
+ _.each(mutations, function(mutant) {
+ _.each(mutant.addedNodes, function(node) {
+ if ($(node).is('#crm-container')) {
+ render(markup);
+ observer.disconnect();
+ }
+ });
+ });
+ }).observe(document, {childList: true, subtree: true});
+ }
+ }
+
+ function render(markup) {
+ var position = CRM.menubar.attachTo === 'body' ? 'beforeend' : 'afterbegin';
+ $(CRM.menubar.attachTo)[0].insertAdjacentHTML(position, markup);
+ CRM.menubar.initializePosition();
+ $('#civicrm-menu').trigger('crmLoad');
+ $(document).ready(function() {
+ handleResize();
+ $('#civicrm-menu')
+ .on('click', 'a[href="#"]', function() {
+ // For empty links - keep the menu open and don't jump the page anchor
+ return false;
+ })
+ .on('click', 'a:not([href^="#"])', function(e) {
+ if (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey) {
+ // Prevent menu closing when link is clicked with a keyboard modifier.
+ e.stopPropagation();
+ }
+ })
+ .on('dragstart', function() {
+ // Stop user from accidentally dragging menu links
+ // This was added because a user noticed they could drag the civi icon into the quicksearch box.
+ return false;
+ })
+ .on('click', 'a[href="#hidemenu"]', function(e) {
+ e.preventDefault();
+ CRM.menubar.hide(250, true);
+ })
+ .on('keyup', 'a', function(e) {
+ // Simulate a click when spacebar key is pressed
+ if (e.which == SPACE_KEY) {
+ $(e.currentTarget)[0].click();
+ }
+ })
+ .on('show.smapi', function(e, menu) {
+ // Focus menu when opened with an accesskey
+ $(menu).siblings('a[accesskey]:not(:hover)').focus();
+ })
+ .smartmenus(CRM.menubar.settings);
+ initialized = true;
+ CRM.menubar.initializeResponsive();
+ CRM.menubar.initializeSearch();
+ });
+ }
+ },
+ destroy: function() {
+ $.SmartMenus.destroy();
+ $('#civicrm-menu-nav').remove();
+ initialized = false;
+ $('body[class]').attr('class', function(i, c) {
+ return c.replace(/(^|\s)crm-menubar-\S+/g, '');
+ });
+ },
+ show: function(speed) {
+ if (typeof speed === 'number') {
+ $('#civicrm-menu').slideDown(speed, function() {
+ $(this).css('display', '');
+ });
+ }
+ $('body')
+ .removeClass('crm-menubar-hidden')
+ .addClass('crm-menubar-visible');
+ },
+ hide: function(speed, showMessage) {
+ if (typeof speed === 'number') {
+ $('#civicrm-menu').slideUp(speed, function() {
+ $(this).css('display', '');
+ });
+ }
+ $('body')
+ .addClass('crm-menubar-hidden')
+ .removeClass('crm-menubar-visible');
+ if (showMessage === true && $('#crm-notification-container').length && initialized) {
+ var alert = CRM.alert('', '', 'none', {expires: 10000});
+ $('#crm-restore-menu')
+ .button({icons: {primary: 'fa-undo'}})
+ .click(function(e) {
+ e.preventDefault();
+ alert.close();
+ CRM.menubar.show(speed);
+ })
+ .parent().css('text-align', 'center').find('.ui-button-text').css({'padding-top': '4px', 'padding-bottom': '4px'});
+ }
+ },
+ open: function(itemName) {
+ var $item = $('li[data-name="' + itemName + '"] > a', '#civicrm-menu');
+ if ($item.length) {
+ $('#civicrm-menu').smartmenus('itemActivate', $item);
+ $item[0].focus();
+ }
+ },
+ close: $.SmartMenus.hideAll,
+ isOpen: function(itemName) {
+ if (itemName) {
+ return !!$('li[data-name="' + itemName + '"] > ul[aria-expanded="true"]', '#civicrm-menu').length;
+ }
+ return !!$('ul[aria-expanded="true"]', '#civicrm-menu').length;
+ },
+ spin: function(spin) {
+ $('.crm-logo-sm', '#civicrm-menu').toggleClass('fa-spin', spin);
+ },
+ getItem: function(itemName) {
+ return traverse(CRM.menubar.data.menu, itemName, 'get');
+ },
+ addItems: function(position, targetName, items) {
+ var list, container, $ul;
+ if (position === 'before' || position === 'after') {
+ if (!targetName) {
+ throw 'Cannot add sibling of main menu';
+ }
+ list = traverse(CRM.menubar.data.menu, targetName, 'parent');
+ if (!list) {
+ throw targetName + ' not found';
+ }
+ var offset = position === 'before' ? 0 : 1;
+ position = offset + _.findIndex(list, {name: targetName});
+ $ul = $('li[data-name="' + targetName + '"]', '#civicrm-menu').closest('ul');
+ } else if (targetName) {
+ container = traverse(CRM.menubar.data.menu, targetName, 'get');
+ if (!container) {
+ throw targetName + ' not found';
+ }
+ container.child = container.child || [];
+ list = container.child;
+ var $target = $('li[data-name="' + targetName + '"]', '#civicrm-menu');
+ if (!$target.children('ul').length) {
+ $target.append('
');
+ }
+ $ul = $target.children('ul').first();
+ } else {
+ list = CRM.menubar.data.menu;
+ }
+ if (position < 0) {
+ position = list.length + 1 + position;
+ }
+ if (position >= list.length) {
+ list.push.apply(list, items);
+ position = list.length - 1;
+ } else {
+ list.splice.apply(list, [position, 0].concat(items));
+ }
+ if (targetName && !$ul.is('#civicrm-menu')) {
+ $ul.html(getTpl('branch')({items: list, branchTpl: getTpl('branch')}));
+ } else {
+ $('#civicrm-menu > li').eq(position).after(getTpl('branch')({items: items, branchTpl: getTpl('branch')}));
+ }
+ CRM.menubar.refresh();
+ },
+ removeItem: function(itemName) {
+ var item = traverse(CRM.menubar.data.menu, itemName, 'delete');
+ if (item) {
+ $('li[data-name="' + itemName + '"]', '#civicrm-menu').remove();
+ CRM.menubar.refresh();
+ }
+ return item;
+ },
+ updateItem: function(item) {
+ if (!item.name) {
+ throw 'No name passed to CRM.menubar.updateItem';
+ }
+ var menuItem = CRM.menubar.getItem(item.name);
+ if (!menuItem) {
+ throw item.name + ' not found';
+ }
+ _.extend(menuItem, item);
+ $('li[data-name="' + item.name + '"]', '#civicrm-menu').replaceWith(getTpl('branch')({items: [menuItem], branchTpl: getTpl('branch')}));
+ CRM.menubar.refresh();
+ },
+ refresh: function() {
+ if (initialized) {
+ $('#civicrm-menu').smartmenus('refresh');
+ handleResize();
+ }
+ },
+ togglePosition: function(persist) {
+ $('body').toggleClass('crm-menubar-over-cms-menu crm-menubar-below-cms-menu');
+ CRM.menubar.position = CRM.menubar.position === 'over-cms-menu' ? 'below-cms-menu' : 'over-cms-menu';
+ handleResize();
+ if (persist !== false) {
+ CRM.cache.set('menubarPosition', CRM.menubar.position);
+ }
+ },
+ initializePosition: function() {
+ if (CRM.menubar.position === 'over-cms-menu' || CRM.menubar.position === 'below-cms-menu') {
+ $('#civicrm-menu')
+ .on('click', 'a[href="#toggle-position"]', function(e) {
+ e.preventDefault();
+ CRM.menubar.togglePosition();
+ })
+ .append('- ' +
+ ' ' +
+ '' +
+ '' +
+ '' +
+ '
',
+ branchTpl:
+ '<% _.forEach(items, function(item) { %>' +
+ '- >' +
+ '>' +
+ '<% if (item.icon) { %>' +
+ '' +
+ '<% } %>' +
+ '<% if (item.label) { %>' +
+ '<%- item.label %>' +
+ '<% } %>' +
+ '' +
+ '<% if (item.child) { %>' +
+ '
<%= branchTpl({items: item.child, branchTpl: branchTpl}) %>
' +
+ '<% } %>' +
+ ' ' +
+ '<% }) %>'
+ }, CRM.menubar || {});
+
+ function getTpl(name) {
+ if (!templates) {
+ templates = {
+ branch: _.template(CRM.menubar.branchTpl, {imports: {_: _, attr: attr}}),
+ search: _.template(CRM.menubar.searchTpl, {imports: {_: _, ts: ts, CRM: CRM}})
+ };
+ templates.tree = _.template(CRM.menubar.treeTpl, {imports: {branchTpl: templates.branch, searchTpl: templates.search, ts: ts}});
+ }
+ return templates[name];
+ }
+
+ function handleResize() {
+ if ($(window).width() >= 768 && $('#civicrm-menu').height() > 50) {
+ $('body').addClass('crm-menubar-wrapped');
+ } else {
+ $('body').removeClass('crm-menubar-wrapped');
+ }
+ }
+
+ function traverse(items, itemName, op) {
+ var found;
+ _.each(items, function(item, index) {
+ if (item.name === itemName) {
+ found = (op === 'parent' ? items : item);
+ if (op === 'delete') {
+ items.splice(index, 1);
+ }
+ return false;
+ }
+ if (item.child) {
+ found = traverse(item.child, itemName, op);
+ if (found) {
+ return false;
+ }
+ }
+ });
+ return found;
+ }
+
+ function attr(el, item) {
+ var ret = [], attr = _.cloneDeep(item.attr || {}), a = ['rel', 'accesskey'];
+ if (el === 'a') {
+ attr = _.pick(attr, a);
+ attr.href = item.url || "#";
+ } else {
+ attr = _.omit(attr, a);
+ attr['data-name'] = item.name;
+ if (item.separator) {
+ attr.class = (attr.class ? attr.class + ' ' : '') + 'crm-menu-border-' + item.separator;
+ }
+ }
+ _.each(attr, function(val, name) {
+ ret.push(name + '="' + val + '"');
+ });
+ return ret.join(' ');
+ }
+
+ CRM.menubar.initialize();
+
+})(CRM.$, CRM._);
diff --git a/js/crm.wordpress.js b/js/crm.wordpress.js
index 774b24a35fd0..740b2246500c 100644
--- a/js/crm.wordpress.js
+++ b/js/crm.wordpress.js
@@ -18,4 +18,20 @@ CRM.$(function($) {
});
}
});
+ // Prevent screen reader shortcuts from changing the document hash and breaking angular routes
+ $('a.screen-reader-shortcut').click(function() {
+ var href = $(this).attr('href');
+ // Show toolbar if hidden
+ if (href === '#wp-toolbar' && CRM.menubar.position === 'over-cms-menu') {
+ CRM.menubar.togglePosition(false);
+ }
+ $(href).focus();
+ return false;
+ });
+ $('' + ts("Open CiviCRM Menu") + '')
+ .prependTo('#adminmenumain')
+ .click(function() {
+ CRM.menubar.open('Home');
+ return false;
+ });
});
diff --git a/settings/Core.setting.php b/settings/Core.setting.php
index b332f3b8e18d..8d4cdc09686f 100644
--- a/settings/Core.setting.php
+++ b/settings/Core.setting.php
@@ -1017,4 +1017,24 @@
),
'quick_form_type' => 'Select',
),
+ 'menubar_position' => array(
+ 'group_name' => 'CiviCRM Preferences',
+ 'group' => 'core',
+ 'name' => 'menubar_position',
+ 'type' => 'String',
+ 'html_type' => 'select',
+ 'default' => 'over-cms-menu',
+ 'add' => '5.9',
+ 'title' => ts('Menubar position'),
+ 'is_domain' => 1,
+ 'is_contact' => 0,
+ 'description' => ts('Location of the CiviCRM main menu.'),
+ 'help_text' => NULL,
+ 'options' => array(
+ 'over-cms-menu' => ts('Replace website menu'),
+ 'below-cms-menu' => ts('Below website menu'),
+ 'above-crm-container' => ts('Above content area'),
+ 'none' => ts('None - disable menu'),
+ ),
+ ),
);
diff --git a/templates/CRM/Admin/Form/Preferences/Display.tpl b/templates/CRM/Admin/Form/Preferences/Display.tpl
index de8986889fac..02f0f38f7e08 100644
--- a/templates/CRM/Admin/Form/Preferences/Display.tpl
+++ b/templates/CRM/Admin/Form/Preferences/Display.tpl
@@ -209,6 +209,13 @@
|
{$settings_fields.sort_name_format.description} |
+
{include file="CRM/common/formButtons.tpl" location="bottom"}
diff --git a/templates/CRM/common/CMSPrint.tpl b/templates/CRM/common/CMSPrint.tpl
index 03ec8cdce5d1..f091432f4734 100644
--- a/templates/CRM/common/CMSPrint.tpl
+++ b/templates/CRM/common/CMSPrint.tpl
@@ -29,8 +29,6 @@
-{crmNavigationMenu is_default=1}
-
{if $breadcrumb}
{foreach from=$breadcrumb item=crumb key=key}
diff --git a/templates/CRM/common/accesskeys.hlp b/templates/CRM/common/accesskeys.hlp
index da9a8501f4b5..2db8fc0506b8 100644
--- a/templates/CRM/common/accesskeys.hlp
+++ b/templates/CRM/common/accesskeys.hlp
@@ -23,14 +23,35 @@
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*}
-{htxt id="accesskeys-title"}
- {ts}Access Keys{/ts}
-{/htxt}
{htxt id="accesskeys"}
-
-
- - ALT+SHIFT+E - {ts}Edit Contact (View Contact Summary Page){/ts}
- - ALT+SHIFT+S - {ts}Save Button{/ts}
- - ALT+SHIFT+N - {ts}Add a new record in each tab (Activities, Tags,..etc){/ts}
-
+ {php}
+ $ua = strtolower($_SERVER['HTTP_USER_AGENT']);
+ if (strstr($ua, 'mac')) {
+ $key = '
CTRL+
ALT';
+ }
+ else {
+ $key = strstr($ua, 'firefox') ? '
ALT+
SHIFT' : '
ALT';
+ }
+ $this->assign('accessKey', $key);
+ {/php}
+
+
+ - {$accessKey}+E - {ts}Edit Contact (View Contact Summary Page){/ts}
+ - {$accessKey}+S - {ts}Save Button{/ts}
+ - {$accessKey}+N - {ts}Add a new record in each tab (Activities, Tags,..etc){/ts}
+ - {$accessKey}+M - {ts}Open the CiviCRM menubar{/ts}
+ - {$accessKey}+Q - {ts}Quicksearch{/ts}
+
+ {literal}{/literal}
{/htxt}
diff --git a/templates/CRM/common/accesskeys.tpl b/templates/CRM/common/accesskeys.tpl
index 025b3d467506..8da5207ae3c2 100644
--- a/templates/CRM/common/accesskeys.tpl
+++ b/templates/CRM/common/accesskeys.tpl
@@ -24,7 +24,9 @@
+--------------------------------------------------------------------+
*}
{if not $urlIsPublic}
-
+
{/if}
diff --git a/templates/CRM/common/joomla.tpl b/templates/CRM/common/joomla.tpl
index d3b81f5a6270..71f11c2b84c0 100644
--- a/templates/CRM/common/joomla.tpl
+++ b/templates/CRM/common/joomla.tpl
@@ -29,10 +29,6 @@
-{* Joomla-only container to hold the civicrm menu *}
-
-{crmNavigationMenu is_default=1}
-
{if $sidebarLeft}
diff --git a/templates/CRM/common/l10n.js.tpl b/templates/CRM/common/l10n.js.tpl
index 2ee5fb3a25f5..27338c8dfc9a 100644
--- a/templates/CRM/common/l10n.js.tpl
+++ b/templates/CRM/common/l10n.js.tpl
@@ -30,6 +30,8 @@
CRM.config.userFramework = {$config->userFramework|@json_encode};
CRM.config.resourceBase = {$config->userFrameworkResourceURL|@json_encode};
CRM.config.lcMessages = {$config->lcMessages|@json_encode};
+ CRM.config.locale = {$locale|@json_encode};
+ CRM.config.cid = {$cid|@json_encode};
$.datepicker._defaults.dateFormat = CRM.config.dateInputFormat = {$config->dateInputFormat|@json_encode};
CRM.config.timeIs24Hr = {if $config->timeInputFormat eq 2}true{else}false{/if};
CRM.config.ajaxPopupsEnabled = {$ajaxPopupsEnabled|@json_encode};