Skip to content

Commit

Permalink
Merge pull request #13996 from colemanw/menuColor
Browse files Browse the repository at this point in the history
Configurable menubar color
  • Loading branch information
totten authored Apr 9, 2019
2 parents ddbda54 + 08a3a51 commit f9e118b
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 31 deletions.
1 change: 1 addition & 0 deletions CRM/Admin/Form/Preferences/Display.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class CRM_Admin_Form_Preferences_Display extends CRM_Admin_Form_Preferences {
'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,
'menubar_color' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
];

/**
Expand Down
4 changes: 4 additions & 0 deletions CRM/Admin/Form/SettingTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ protected function addFieldsDefinedInSettingsMetadata() {
elseif ($add === 'addYesNo' && ($props['type'] === 'Boolean')) {
$this->addRadio($setting, ts($props['title']), [1 => 'Yes', 0 => 'No'], NULL, '  ');
}
elseif ($add === 'add') {
$this->add($props['html_type'], $setting, ts($props['title']), $options);
}
else {
$this->$add($setting, ts($props['title']), $options);
}
Expand Down Expand Up @@ -293,6 +296,7 @@ protected function getQuickFormType($spec) {
'entity_reference' => 'EntityRef',
'advmultiselect' => 'Element',
];
$mapping += array_fill_keys(CRM_Core_Form::$html5Types, '');
return $mapping[$htmlType];
}

Expand Down
48 changes: 43 additions & 5 deletions CRM/Core/Resources.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
use Civi\Core\Event\GenericHookEvent;

/**
* This class facilitates the loading of resources
Expand Down Expand Up @@ -581,6 +582,7 @@ public function resetCacheCode() {
* @return CRM_Core_Resources
*/
public function addCoreResources($region = 'html-header') {
Civi::dispatcher()->addListener('hook_civicrm_buildAsset', [$this, 'renderMenubarStylesheet']);
if (!isset($this->addedCoreResources[$region]) && !self::isAjaxMode()) {
$this->addedCoreResources[$region] = TRUE;
$config = CRM_Core_Config::singleton();
Expand Down Expand Up @@ -760,14 +762,12 @@ public function coreResourceList($region) {
$position = Civi::settings()->get('menubar_position') ?: 'over-cms-menu';
}
if ($position !== 'none') {
$cms = strtolower($config->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[] = Civi::service('asset_builder')->getUrl('crm-menubar.css', [
'color' => Civi::settings()->get('menubar_color'),
]);
$items[] = [
'menubar' => [
'position' => $position,
Expand Down Expand Up @@ -825,6 +825,44 @@ public static function isAjaxMode() {
return (strpos($url, 'civicrm/ajax') === 0) || (strpos($url, 'civicrm/angular') === 0);
}

/**
* @param GenericHookEvent $e
* @see \CRM_Utils_Hook::buildAsset()
*/
public static function renderMenubarStylesheet(GenericHookEvent $e) {
if ($e->asset !== 'crm-menubar.css') {
return;
}
$e->mimeType = 'text/css';
$e->content = '';
$config = CRM_Core_Config::singleton();
$cms = strtolower($config->userFramework);
$cms = $cms === 'drupal' ? 'drupal7' : $cms;
$items = [
'bower_components/smartmenus/dist/css/sm-core-css.css',
'css/crm-menubar.css',
"css/menubar-$cms.css",
];
foreach ($items as $item) {
$e->content .= file_get_contents(self::singleton()->getPath('civicrm', $item));
}
$color = $e->params['color'];
if (!CRM_Utils_Rule::color($color)) {
$color = Civi::settings()->getDefault('menubar_color');
}
$vars = [
'resourceBase' => rtrim($config->resourceBase, '/'),
'menubarColor' => $color,
'semiTransparentMenuColor' => 'rgba(' . implode(', ', CRM_Utils_Color::getRgb($color)) . ', .85)',
'highlightColor' => CRM_Utils_Color::getHighlight($color),
'textColor' => CRM_Utils_Color::getContrast($color, '#333', '#ddd'),
];
$vars['highlightTextColor'] = CRM_Utils_Color::getContrast($vars['highlightColor'], '#333', '#ddd');
foreach ($vars as $var => $val) {
$e->content = str_replace('$' . $var, $val, $e->content);
}
}

/**
* Provide a list of available entityRef filters.
*
Expand Down
52 changes: 46 additions & 6 deletions CRM/Utils/Color.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,55 @@ class CRM_Utils_Color {
* Based on YIQ value.
*
* @param string $hexcolor
* @param string $black
* @param string $white
* @return string
*/
public static function getContrast($hexcolor) {
$hexcolor = trim($hexcolor, ' #');
$r = hexdec(substr($hexcolor, 0, 2));
$g = hexdec(substr($hexcolor, 2, 2));
$b = hexdec(substr($hexcolor, 4, 2));
public static function getContrast($hexcolor, $black = 'black', $white = 'white') {
list($r, $g, $b) = self::getRgb($hexcolor);
$yiq = (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
return ($yiq >= 128) ? 'black' : 'white';
return ($yiq >= 128) ? $black : $white;
}

/**
* Convert hex color to decimal
*
* @param string $hexcolor
* @return array
* [red, green, blue]
*/
public static function getRgb($hexcolor) {
$hexcolor = trim($hexcolor, ' #');
if (strlen($hexcolor) === 3) {
$hexcolor = $hexcolor[0] . $hexcolor[0] . $hexcolor[1] . $hexcolor[1] . $hexcolor[2] . $hexcolor[2];
}
return [
hexdec(substr($hexcolor, 0, 2)),
hexdec(substr($hexcolor, 2, 2)),
hexdec(substr($hexcolor, 4, 2)),
];
}

/**
* Calculate a highlight color from a base color
*
* @param $hexcolor
* @return string
*/
public static function getHighlight($hexcolor) {
$rgb = CRM_Utils_Color::getRgb($hexcolor);
$avg = array_sum($rgb) / 3;
foreach ($rgb as &$v) {
if ($avg > 242) {
// For very bright values, lower the brightness
$v -= 50;
}
else {
// Bump up brightness on a nonlinear curve - darker colors get more of a boost
$v = min(255, intval((-.0035 * ($v - 242) ** 2) + 260));
}
}
return '#' . implode(array_map('dechex', $rgb));
}

}
10 changes: 10 additions & 0 deletions CRM/Utils/Rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,16 @@ public static function numberOfDigit($value, $noOfDigit) {
return preg_match('/^\d{' . $noOfDigit . '}$/', $value) ? TRUE : FALSE;
}

/**
* Strict validation of 6-digit hex color notation per html5 <input type="color">
*
* @param $value
* @return bool
*/
public static function color($value) {
return (bool) preg_match('/^#([\da-fA-F]{6})$/', $value);
}

/**
* Strip thousand separator from a money string.
*
Expand Down
7 changes: 7 additions & 0 deletions CRM/Utils/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ public static function validate($data, $type, $abort = TRUE, $name = 'One of par
'ExtensionKey',
'Json',
'Alphanumeric',
'Color',
];
if (!in_array($type, $possibleTypes)) {
if ($isThrowException) {
Expand Down Expand Up @@ -554,6 +555,12 @@ public static function validate($data, $type, $abort = TRUE, $name = 'One of par
return $data;
}
break;

case 'Color':
if (CRM_Utils_Rule::color($data)) {
return $data;
}
break;
}

if ($abort) {
Expand Down
34 changes: 15 additions & 19 deletions css/crm-menubar.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
font-size: 13px;
}
#civicrm-menu {
background-color: #f2f2f2;
width: 100%;
background-color: $menubarColor;
width: 100%;
z-index: 500;
height: auto;
margin: 0;
Expand All @@ -22,7 +22,6 @@
#civicrm-menu li a {
padding: 12px 8px;
text-decoration: none;
color: #333;
box-shadow: none;
border: none;
}
Expand All @@ -42,12 +41,12 @@
#civicrm-menu li a:hover,
#civicrm-menu li a.highlighted {
text-decoration: none;
background-color: #fff;
background-color: $highlightColor;
color: $highlightTextColor;
}
#civicrm-menu li li .sub-arrow:before {
content: "\f0da";
font-family: 'FontAwesome';
color: #666;
float: right;
margin-right: -25px;
}
Expand Down Expand Up @@ -88,7 +87,7 @@
cursor: pointer;
color: transparent;
-webkit-tap-highlight-color: rgba(0,0,0,0);
background-color: #333;
background-color: #1b1b1b;
}

/* responsive icon */
Expand Down Expand Up @@ -163,7 +162,7 @@ ul.crm-quickSearch-results.ui-state-disabled {
}

#civicrm-menu-nav .crm-logo-sm {
background: url(../i/logo_sm.png) no-repeat;
background: url($resourceBase/i/logo_sm.png) no-repeat;
display: inline-block;
width: 16px;
height: 16px;
Expand All @@ -174,10 +173,10 @@ ul.crm-quickSearch-results.ui-state-disabled {
float: right;
}
#civicrm-menu #crm-menubar-toggle-position a i {
color: #888;
margin: 0;
border-top: 2px solid #888;
border-top: 2px solid $textColor;
font-size: 11px;
opacity: .8;
}
body.crm-menubar-over-cms-menu #crm-menubar-toggle-position a i {
transform: rotate(180deg);
Expand Down Expand Up @@ -215,10 +214,14 @@ body.crm-menubar-over-cms-menu #crm-menubar-toggle-position a i {
}

#civicrm-menu ul {
background-color: #fff;
box-shadow: 0px 0px 2px 0 rgba(0,0,0,0.3);
}

#civicrm-menu li a {
background-color: $semiTransparentMenuColor;
color: $textColor;
}

#civicrm-menu > li > a {
height: 40px;
}
Expand All @@ -227,13 +230,6 @@ body.crm-menubar-over-cms-menu #crm-menubar-toggle-position a i {
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;
Expand All @@ -256,7 +252,7 @@ body.crm-menubar-over-cms-menu #crm-menubar-toggle-position a i {
}
#civicrm-menu {
z-index: 100000;
background-color: #333;
background-color: #1b1b1b;
}
#civicrm-menu ul {
background-color: #444;
Expand Down Expand Up @@ -316,7 +312,7 @@ body.crm-menubar-over-cms-menu #crm-menubar-toggle-position a i {
left: 5px;
width: 18px;
height: 18px;
background: url(../i/logo_lg.png) no-repeat;
background: url($resourceBase/i/logo_lg.png) no-repeat;
background-size: 18px;
top: 6px;
}
Expand Down
17 changes: 16 additions & 1 deletion settings/Core.setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@
'type' => 'String',
'html_type' => 'select',
'default' => 'over-cms-menu',
'add' => '5.9',
'add' => '5.12',
'title' => ts('Menubar position'),
'is_domain' => 1,
'is_contact' => 0,
Expand All @@ -1037,4 +1037,19 @@
'none' => ts('None - disable menu'),
),
),
'menubar_color' => array(
'group_name' => 'CiviCRM Preferences',
'group' => 'core',
'name' => 'menubar_color',
'type' => 'String',
'html_type' => 'color',
'default' => '#1b1b1b',
'add' => '5.13',
'title' => ts('Menubar color'),
'is_domain' => 1,
'is_contact' => 0,
'description' => ts('Color of the CiviCRM main menu.'),
'help_text' => NULL,
'validate_callback' => 'CRM_Utils_Rule::color',
),
);
6 changes: 6 additions & 0 deletions templates/CRM/Admin/Form/Preferences/Display.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@
<div class="description">{ts}Default position for the CiviCRM menubar.{/ts}</div>
</td>
</tr>
<tr class="crm-preferences-display-form-block_menubar_color">
<td class="label">{$form.menubar_color.label}</td>
<td>
{$form.menubar_color.html}
</td>
</tr>
</table>
<div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
</div>
Expand Down
30 changes: 30 additions & 0 deletions tests/phpunit/CRM/Utils/RuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,36 @@ public function moneyDataProvider() {
);
}

/**
* @dataProvider colorDataProvider
* @param $inputData
* @param $expectedResult
*/
public function testColor($inputData, $expectedResult) {
$this->assertEquals($expectedResult, CRM_Utils_Rule::color($inputData));
}

/**
* @return array
*/
public function colorDataProvider() {
return [
['#000000', TRUE],
['#ffffff', TRUE],
['#123456', TRUE],
['#00aaff', TRUE],
// Some of these are valid css colors but we reject anything that doesn't conform to the html5 spec for <input type="color">
['#ffffff00', FALSE],
['#fff', FALSE],
['##000000', FALSE],
['ffffff', FALSE],
['red', FALSE],
['#orange', FALSE],
['', FALSE],
['rgb(255, 255, 255)', FALSE],
];
}

/**
* @return array
*/
Expand Down

0 comments on commit f9e118b

Please sign in to comment.