Commit b30809e4 authored by colemanw's avatar colemanw

Migrate KAM smartmenus to core

parent 7ae65a06
......@@ -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,
);
/**
......
......@@ -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;
}
/**
......
......@@ -299,51 +299,6 @@ FROM civicrm_navigation WHERE domain_id = $domainID";
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 .= '<li class="menu-separator"></li>';
}
$removeCharacters = array('/', '!', '&', '*', ' ', '(', ')', '.');
$navigationString .= '<li class="menumain crm-' . str_replace($removeCharacters, '_', $value['attributes']['label']) . '">' . $name;
}
}
self::recurseNavigation($value, $navigationString, $skipMenuItems);
}
// clean up - Need to remove empty <ul>'s, this happens when user don't have
// permission to access parent
$navigationString = str_replace('<ul></ul></li>', '', $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 @@ FROM civicrm_navigation WHERE domain_id = $domainID";
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 = '<span class="crm-logo-sm" ></span>';
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 = "
<li class='menumain crm-link-home'>$homeIcon
<ul id='civicrm-home'>
<li><a href='$homeURL'>$homeLabel</a></li>
<li><a href='#' class='crm-hidemenu'>$hideLabel</a></li>
<li><a href='$logoutURL' class='crm-logout-link'>" . ts('Log out') . "</a></li>
</ul>";
// <li> 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 @@ FROM civicrm_navigation WHERE domain_id = $domainID";
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;
}
}
}
}
......@@ -609,10 +609,6 @@ class CRM_Core_Resources {
'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 @@ class CRM_Core_Resources {
'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 @@ class CRM_Core_Resources {
// 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";
......
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 5 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2019 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2019
* $Id$
*
*/
/**
* Output navigation script tag
*
* @param array $params
* - is_default: bool, true if this is normal/default instance of the menu (which may be subject to CIVICRM_DISABLE_DEFAULT_MENU)
* @param CRM_Core_Smarty $smarty
* The Smarty object.
*
* @return string
* HTML
*/
function smarty_function_crmNavigationMenu($params, &$smarty) {
$config = CRM_Core_Config::singleton();
//check if logged in user has access CiviCRM permission and build menu
$buildNavigation = !CRM_Core_Config::isUpgradeMode() && CRM_Core_Permission::check('access CiviCRM');
if (defined('CIVICRM_DISABLE_DEFAULT_MENU') && CRM_Utils_Array::value('is_default', $params, FALSE)) {
$buildNavigation = FALSE;
}
if ($config->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 '<script id="civicrm-navigation-menu" type="text/javascript" src="' . $src . '" data-qfkey=' . json_encode($qfKey) . '></script>';
}
}
return '';
}
......@@ -633,9 +633,9 @@
<weight>9000</weight>
</item>
<item>
<path>civicrm/ajax/menujs</path>
<page_callback>CRM_Admin_Page_AJAX::getNavigationMenu</page_callback>
<access_arguments>access CiviCRM</access_arguments>
<path>civicrm/ajax/navmenu</path>
<page_callback>CRM_Admin_Page_AJAX::navMenu</page_callback>
<access_arguments>access CiviCRM</access_arguments>
</item>
<item>
<path>civicrm/ajax/menutree</path>
......
......@@ -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": {
......
......@@ -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,
......
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;
}
/* 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;