Commit 8a52ae34 authored by colemanw's avatar colemanw

Configurable menubar color

parent 1889d803
......@@ -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,
];
/**
......@@ -105,6 +106,8 @@ class CRM_Admin_Form_Preferences_Display extends CRM_Admin_Form_Preferences {
$this->postProcessCommon();
\Civi::service('asset_builder')->clear();
// If "Configure CKEditor" button was clicked
if (!empty($this->_params['ckeditor_config'])) {
// Suppress the "Saved" status message and redirect to the CKEditor Config page
......
......@@ -238,6 +238,9 @@ trait CRM_Admin_Form_SettingTrait {
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);
}
......@@ -293,6 +296,7 @@ trait CRM_Admin_Form_SettingTrait {
'entity_reference' => 'EntityRef',
'advmultiselect' => 'Element',
];
$mapping += array_fill_keys(CRM_Core_Form::$html5Types, '');
return $mapping[$htmlType];
}
......
......@@ -844,9 +844,18 @@ class CRM_Core_Resources {
foreach ($items as $item) {
$e->content .= file_get_contents(self::singleton()->getPath('civicrm', $item));
}
$color = Civi::settings()->get('menubar_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);
}
......
......@@ -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));
}
}
......@@ -540,6 +540,16 @@ class CRM_Utils_Rule {
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.
*
......
......@@ -434,6 +434,7 @@ class CRM_Utils_Type {
'ExtensionKey',
'Json',
'Alphanumeric',
'Color',
];
if (!in_array($type, $possibleTypes)) {
if ($isThrowException) {
......@@ -554,6 +555,12 @@ class CRM_Utils_Type {
return $data;
}
break;
case 'Color':
if (CRM_Utils_Rule::color($data)) {
return $data;
}
break;
}
if ($abort) {
......
......@@ -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;
......@@ -22,7 +22,6 @@
#civicrm-menu li a {
padding: 12px 8px;
text-decoration: none;
color: #333;
box-shadow: none;
border: none;
}
......@@ -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;
}
......@@ -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 */
......@@ -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);
......@@ -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;
}
......@@ -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;
......@@ -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;
......
......@@ -1024,7 +1024,7 @@ return array(
'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,
......@@ -1037,4 +1037,19 @@ return array(
'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',
),
);
......@@ -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>
......
......@@ -112,6 +112,36 @@ class CRM_Utils_RuleTest extends CiviUnitTestCase {
);
}
/**
* @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
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment