'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,
];
/**
$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
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);
}
'entity_reference' => 'EntityRef',
'advmultiselect' => 'Element',
];
+ $mapping += array_fill_keys(CRM_Core_Form::$html5Types, '');
return $mapping[$htmlType];
}
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);
}
* 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));
}
}
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.
*
'ExtensionKey',
'Json',
'Alphanumeric',
+ 'Color',
];
if (!in_array($type, $possibleTypes)) {
if ($isThrowException) {
return $data;
}
break;
+
+ case 'Color':
+ if (CRM_Utils_Rule::color($data)) {
+ return $data;
+ }
+ break;
}
if ($abort) {
font-size: 13px;
}
#civicrm-menu {
- background-color: #f2f2f2;
- width: 100%;
+ background-color: $menubarColor;
+ width: 100%;
z-index: 500;
height: auto;
margin: 0;
#civicrm-menu li a {
padding: 12px 8px;
text-decoration: none;
- color: #333;
box-shadow: none;
border: none;
}
#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;
}
cursor: pointer;
color: transparent;
-webkit-tap-highlight-color: rgba(0,0,0,0);
- background-color: #333;
+ background-color: #1b1b1b;
}
/* responsive icon */
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);
}
#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;
}
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;
}
#civicrm-menu {
z-index: 100000;
- background-color: #333;
+ background-color: #1b1b1b;
}
#civicrm-menu ul {
background-color: #444;
'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,
'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',
+ ),
);
<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>
);
}
+ /**
+ * @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
*/