| Total Complexity | 93 |
| Total Lines | 784 |
| Duplicated Lines | 0 % |
| Changes | 3 | ||
| Bugs | 0 | Features | 1 |
Complex classes like Theming often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Theming, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 14 | class Theming { |
||
| 15 | /** |
||
| 16 | * A hash that is used to cache if a theme is a json theme. |
||
| 17 | * |
||
| 18 | * @property |
||
| 19 | */ |
||
| 20 | private static $isJsonThemeCache = []; |
||
| 21 | |||
| 22 | /** |
||
| 23 | * A hash that is used to cache the properties of json themes. |
||
| 24 | * |
||
| 25 | * @property |
||
| 26 | */ |
||
| 27 | private static $jsonThemePropsCache = []; |
||
| 28 | |||
| 29 | /** |
||
| 30 | * Retrieves all installed json themes. |
||
| 31 | * |
||
| 32 | * @return array An array with the directory names of the json themes as keys and their display names |
||
| 33 | * as values |
||
| 34 | */ |
||
| 35 | public static function getJsonThemes() { |
||
| 56 | } |
||
| 57 | |||
| 58 | /** |
||
| 59 | * Returns the name of the active theme if one was found, and false otherwise. |
||
| 60 | * The active theme can be set by the admin in the config.php, or by |
||
| 61 | * the user in his settings. |
||
| 62 | * |
||
| 63 | * @return bool|string |
||
| 64 | */ |
||
| 65 | public static function getActiveTheme() { |
||
| 66 | $theme = false; |
||
| 67 | $themePath = BASE_PATH . constant('THEME_PATH_' . DEBUG_LOADER); |
||
| 68 | |||
| 69 | // List of unified themes that don't require separate directories |
||
| 70 | $unifiedThemes = ['purple', 'orange', 'lime', 'magenta', 'highcontrast', 'blue', 'teal', 'indigo', 'red', 'green', 'amber']; |
||
| 71 | |||
| 72 | // First check if a theme was set by this user in his settings |
||
| 73 | if (WebAppAuthentication::isAuthenticated()) { |
||
| 74 | if (ENABLE_THEMES === false) { |
||
|
|
|||
| 75 | $theme = THEME !== "" ? THEME : 'basic'; |
||
| 76 | } |
||
| 77 | else { |
||
| 78 | $theme = $GLOBALS['settings']->get('zarafa/v1/main/active_theme'); |
||
| 79 | } |
||
| 80 | |||
| 81 | // If a theme was found, check if the theme is still installed |
||
| 82 | // Remember that 'basic' is not a real theme, but the name for the default look of grommunio Web |
||
| 83 | // Unified themes don't require directories, so we skip the directory check for them |
||
| 84 | if ( |
||
| 85 | isset($theme) && !empty($theme) && $theme !== 'basic' && |
||
| 86 | !in_array($theme, $unifiedThemes) && |
||
| 87 | !is_dir($themePath . '/' . $theme) && |
||
| 88 | !is_dir(BASE_PATH . PATH_PLUGIN_DIR . '/' . $theme) |
||
| 89 | ) { |
||
| 90 | $theme = false; |
||
| 91 | } |
||
| 92 | } |
||
| 93 | |||
| 94 | // If a valid theme was not found in the settings of the user, let's see if a valid theme |
||
| 95 | // was defined by the admin. |
||
| 96 | if (!$theme && defined('THEME') && THEME) { |
||
| 97 | // Check if it's a unified theme or if the directory exists |
||
| 98 | if (in_array(THEME, $unifiedThemes)) { |
||
| 99 | $theme = THEME; |
||
| 100 | } |
||
| 101 | else { |
||
| 102 | $theme = is_dir($themePath . '/' . THEME) || is_dir(BASE_PATH . PATH_PLUGIN_DIR . '/' . THEME) ? THEME : false; |
||
| 103 | } |
||
| 104 | } |
||
| 105 | |||
| 106 | if (Theming::isJsonTheme($theme) && !is_array(Theming::getJsonThemeProps($theme))) { |
||
| 107 | // Someone made an error, we cannot read this json theme |
||
| 108 | return false; |
||
| 109 | } |
||
| 110 | |||
| 111 | return $theme; |
||
| 112 | } |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Returns the path to the favicon if included with the theme. If found the |
||
| 116 | * path to it will be returned. Otherwise false. |
||
| 117 | * |
||
| 118 | * @param string $theme the name of the theme for which the css will be returned. |
||
| 119 | * Note: This is the directory name of the theme plugin. |
||
| 120 | * |
||
| 121 | * * @return bool|string |
||
| 122 | */ |
||
| 123 | public static function getFavicon($theme) { |
||
| 124 | $themePath = constant('THEME_PATH_' . DEBUG_LOADER); |
||
| 125 | |||
| 126 | // First check if we can find a core theme with this name |
||
| 127 | if ($theme && is_dir(BASE_PATH . $themePath . '/' . $theme) && is_file(BASE_PATH . $themePath . '/' . $theme . '/favicon.ico')) { |
||
| 128 | // Add a date as GET parameter, so we will fetch a new icon every day |
||
| 129 | // This way themes can update the favicon and it will show the next day latest. |
||
| 130 | return $themePath . '/' . $theme . '/favicon.ico?' . date('Ymd'); |
||
| 131 | } |
||
| 132 | |||
| 133 | // If no core theme was found, let's try to find a theme plugin with this name |
||
| 134 | if ($theme && is_dir(BASE_PATH . PATH_PLUGIN_DIR . '/' . $theme) && is_file(BASE_PATH . PATH_PLUGIN_DIR . '/' . $theme . '/favicon.ico')) { |
||
| 135 | // Add a date as GET parameter, so we will fetch a new icon every day |
||
| 136 | // This way themes can update the favicon and it will show the next day latest. |
||
| 137 | return PATH_PLUGIN_DIR . '/' . $theme . '/favicon.ico?' . date('Ymd'); |
||
| 138 | } |
||
| 139 | |||
| 140 | return false; |
||
| 141 | } |
||
| 142 | |||
| 143 | /** |
||
| 144 | * Returns the contents of the css files in the $theme as a string. |
||
| 145 | * |
||
| 146 | * @param string $theme the name of the theme for which the css will be returned. |
||
| 147 | * Note: This is the directory name of the theme plugin. |
||
| 148 | * |
||
| 149 | * @return string |
||
| 150 | */ |
||
| 151 | public static function getCss($theme) { |
||
| 152 | $themePathCoreThemes = BASE_PATH . constant('THEME_PATH_' . DEBUG_LOADER); |
||
| 153 | $cssFiles = []; |
||
| 154 | |||
| 155 | // Unified themes (purple, orange, lime, magenta, highcontrast, blue, teal, indigo, red, green, amber) are now in grommunio.css |
||
| 156 | // Only the dark theme still loads its own CSS file |
||
| 157 | $unifiedThemes = ['purple', 'orange', 'lime', 'magenta', 'highcontrast', 'blue', 'teal', 'indigo', 'red', 'green', 'amber']; |
||
| 158 | if (in_array($theme, $unifiedThemes)) { |
||
| 159 | return []; |
||
| 160 | } |
||
| 161 | |||
| 162 | // First check if this is a core theme, and if it isn't, check if it is a theme plugin |
||
| 163 | if ($theme && is_dir($themePathCoreThemes . '/' . $theme)) { |
||
| 164 | $themePath = $themePathCoreThemes . '/' . $theme; |
||
| 165 | } |
||
| 166 | elseif ($theme && is_dir(BASE_PATH . PATH_PLUGIN_DIR . '/' . $theme)) { |
||
| 167 | if (Theming::isJsonTheme($theme)) { |
||
| 168 | return []; |
||
| 169 | } |
||
| 170 | $themePath = BASE_PATH . PATH_PLUGIN_DIR . '/' . $theme; |
||
| 171 | } |
||
| 172 | |||
| 173 | if (isset($themePath)) { |
||
| 174 | // Use SPL iterators to recursively traverse the css directory and find all css files |
||
| 175 | $directoryIterator = new RecursiveDirectoryIterator($themePath . '/css/', FilesystemIterator::SKIP_DOTS); |
||
| 176 | $iterator = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::SELF_FIRST); |
||
| 177 | |||
| 178 | // Always rewind an iterator before using it!!! See https://bugs.php.net/bug.php?id=62914 (it might save you a couple of hours debugging) |
||
| 179 | $iterator->rewind(); |
||
| 180 | while ($iterator->valid()) { |
||
| 181 | $fileName = $iterator->getFilename(); |
||
| 182 | if (!$iterator->isDir() && (strtolower($iterator->getExtension()) === 'css' || str_ends_with($fileName, '.css.php'))) { |
||
| 183 | $cssFiles[] = substr((string) $iterator->key(), strlen(BASE_PATH)); |
||
| 184 | } |
||
| 185 | $iterator->next(); |
||
| 186 | } |
||
| 187 | } |
||
| 188 | |||
| 189 | // Sort the array alphabetically before adding the css |
||
| 190 | sort($cssFiles); |
||
| 191 | |||
| 192 | return $cssFiles; |
||
| 193 | } |
||
| 194 | |||
| 195 | /** |
||
| 196 | * Returns the value that is assigned to a property by the active theme |
||
| 197 | * or null otherwise. |
||
| 198 | * Currently only implemented for JSON themes. |
||
| 199 | * |
||
| 200 | * @param mixed $propName |
||
| 201 | * |
||
| 202 | * @return string the value that the active theme has set for the property, |
||
| 203 | * or NULL |
||
| 204 | */ |
||
| 205 | public static function getThemeProperty($propName) { |
||
| 206 | $theme = Theming::getActiveTheme(); |
||
| 207 | if (!Theming::isJsonTheme($theme)) { |
||
| 208 | return false; |
||
| 209 | } |
||
| 210 | |||
| 211 | $props = Theming::getJsonThemeProps($theme); |
||
| 212 | if (!isset($props[$propName])) { |
||
| 213 | return false; |
||
| 214 | } |
||
| 215 | |||
| 216 | return $props[$propName]; |
||
| 217 | } |
||
| 218 | |||
| 219 | /** |
||
| 220 | * Returns the color that the active theme has set for the primary color |
||
| 221 | * of the icons. Currently only supported for JSON themes. |
||
| 222 | * Note: Only SVG icons of an iconset that has defined the primary color |
||
| 223 | * can be 'recolored'. |
||
| 224 | * |
||
| 225 | * @return string the color that the active theme has set for the primary |
||
| 226 | * color of the icons, or FALSE |
||
| 227 | */ |
||
| 228 | public static function getPrimaryIconColor() { |
||
| 229 | $val = Theming::getThemeProperty('icons-primary-color'); |
||
| 230 | |||
| 231 | return $val ?? false; |
||
| 232 | } |
||
| 233 | |||
| 234 | /** |
||
| 235 | * Returns the color that the active theme has set for the secondary color |
||
| 236 | * of the icons. Currently only supported for JSON themes. |
||
| 237 | * Note: Only SVG icons of an iconset that has defined the secondary color |
||
| 238 | * can be 'recolored'. |
||
| 239 | * |
||
| 240 | * @return string the color that the active theme has set for the secondary |
||
| 241 | * color of the icons, or FALSE |
||
| 242 | */ |
||
| 243 | public static function getSecondaryIconColor() { |
||
| 244 | $val = Theming::getThemeProperty('icons-secondary-color'); |
||
| 245 | |||
| 246 | return $val ?? false; |
||
| 247 | } |
||
| 248 | |||
| 249 | /** |
||
| 250 | * Checks if a theme is a JSON theme. (Basically this means that it checks if a |
||
| 251 | * directory with the theme name exists and if that directory contains a file |
||
| 252 | * called theme.json). |
||
| 253 | * |
||
| 254 | * @param string $theme The name of the theme to check |
||
| 255 | * |
||
| 256 | * @return bool True if the theme is a json theme, false otherwise |
||
| 257 | */ |
||
| 258 | public static function isJsonTheme($theme) { |
||
| 259 | if (empty($theme)) { |
||
| 260 | return false; |
||
| 261 | } |
||
| 262 | |||
| 263 | if (!isset(Theming::$isJsonThemeCache[$theme])) { |
||
| 264 | $themePathCoreThemes = BASE_PATH . constant('THEME_PATH_' . DEBUG_LOADER); |
||
| 265 | |||
| 266 | // First check if this is a core theme, and if it isn't, check if it is a theme plugin |
||
| 267 | if (is_dir($themePathCoreThemes . '/' . $theme)) { |
||
| 268 | // We don't have core json themes, so return false |
||
| 269 | Theming::$isJsonThemeCache[$theme] = false; |
||
| 270 | } |
||
| 271 | elseif (is_dir(BASE_PATH . PATH_PLUGIN_DIR . '/' . $theme) && is_file(BASE_PATH . PATH_PLUGIN_DIR . '/' . $theme . '/theme.json')) { |
||
| 272 | Theming::$isJsonThemeCache[$theme] = true; |
||
| 273 | } |
||
| 274 | else { |
||
| 275 | Theming::$isJsonThemeCache[$theme] = false; |
||
| 276 | } |
||
| 277 | } |
||
| 278 | |||
| 279 | return Theming::$isJsonThemeCache[$theme]; |
||
| 280 | } |
||
| 281 | |||
| 282 | /** |
||
| 283 | * Retrieves the properties set in the theme.json file of the theme. |
||
| 284 | * |
||
| 285 | * @param string $theme The theme for which the properties should be retrieved |
||
| 286 | * |
||
| 287 | * @return array The decoded array of properties defined in the theme.json file |
||
| 288 | */ |
||
| 289 | public static function getJsonThemeProps($theme) { |
||
| 290 | if (!Theming::isJsonTheme($theme)) { |
||
| 291 | return false; |
||
| 292 | } |
||
| 293 | |||
| 294 | // Check if we have the props in the cache before reading the file |
||
| 295 | if (!isset(Theming::$jsonThemePropsCache[$theme])) { |
||
| 296 | $json = file_get_contents(BASE_PATH . PATH_PLUGIN_DIR . '/' . $theme . '/theme.json'); |
||
| 297 | Theming::$jsonThemePropsCache[$theme] = json_decode($json, true); |
||
| 298 | |||
| 299 | if (json_last_error() !== JSON_ERROR_NONE) { |
||
| 300 | error_log("The theme '{$theme}' does not have a valid theme.json file. " . json_last_error_msg()); |
||
| 301 | Theming::$jsonThemePropsCache[$theme] = ''; |
||
| 302 | } |
||
| 303 | } |
||
| 304 | |||
| 305 | return Theming::$jsonThemePropsCache[$theme]; |
||
| 306 | } |
||
| 307 | |||
| 308 | /** |
||
| 309 | * Normalizes all defined colors in a JSON theme to valid hex colors. |
||
| 310 | * |
||
| 311 | * @param array $themeProps A hash with the properties defined a theme.json file |
||
| 312 | */ |
||
| 313 | private static function normalizeColors($themeProps) { |
||
| 329 | } |
||
| 330 | |||
| 331 | /** |
||
| 332 | * Utility function to fix relative urls in JSON themes. |
||
| 333 | * |
||
| 334 | * @param string $url the url to be fixed |
||
| 335 | * @param string $theme the name of the theme the url is part of |
||
| 336 | */ |
||
| 337 | private static function fixUrl($url, $theme) { |
||
| 338 | // the url is absolute we don't have to fix anything |
||
| 339 | if (preg_match('/^https?:\/\//', $url)) { |
||
| 340 | return $url; |
||
| 341 | } |
||
| 342 | |||
| 343 | return PATH_PLUGIN_DIR . '/' . $theme . '/' . $url; |
||
| 344 | } |
||
| 345 | |||
| 346 | /** |
||
| 347 | * Retrieves the styles that should be added to the page for the json theme. |
||
| 348 | * |
||
| 349 | * @param string $theme The theme for which the properties should be retrieved |
||
| 350 | * |
||
| 351 | * @return string The styles (between <style> tags) |
||
| 352 | */ |
||
| 353 | public static function getStyles($theme) { |
||
| 452 | } |
||
| 453 | |||
| 454 | /** |
||
| 455 | * The templates of the styles that a json theme can add to the page. |
||
| 456 | * |
||
| 457 | * @property |
||
| 458 | */ |
||
| 459 | private static $styles = [ |
||
| 460 | 'primary-color' => ' |
||
| 461 | /* The Sign in button of the login screen */ |
||
| 462 | body.login #form-container #submitbutton, |
||
| 463 | #loading-mask #form-container #submitbutton { |
||
| 464 | background: {{primary-color}}; |
||
| 465 | } |
||
| 466 | |||
| 467 | /* The top bar of the Welcome dialog */ |
||
| 468 | .zarafa-welcome-body > .x-panel-bwrap > .x-panel-body div.zarafa-welcome-title { |
||
| 469 | border-left: 1px solid {{primary-color}}; |
||
| 470 | border-right: 1px solid {{primary-color}}; |
||
| 471 | background: {{primary-color}}; |
||
| 472 | } |
||
| 473 | |||
| 474 | /* The border line under the top menu bar */ |
||
| 475 | body #zarafa-mainmenu { |
||
| 476 | border-color: {{primary-color}}; |
||
| 477 | } |
||
| 478 | /* The background color of the top menu bar */ |
||
| 479 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct { |
||
| 480 | background-color: {{primary-color}}; |
||
| 481 | } |
||
| 482 | /* Unread items */ |
||
| 483 | .k-unreadborders .x-grid3-row.x-grid3-row-collapsed.mail_unread > table, |
||
| 484 | .k-unreadborders .x-grid3-row.x-grid3-row-expanded.mail_unread > table { |
||
| 485 | border-left: 4px solid {{primary-color}} !important; |
||
| 486 | } |
||
| 487 | ', |
||
| 488 | |||
| 489 | 'primary-color:hover' => ' |
||
| 490 | /* Hover state and active state of the Sign in button */ |
||
| 491 | body.login #form-container #submitbutton:hover, |
||
| 492 | #loading-mask #form-container #submitbutton:hover, |
||
| 493 | body.login #form-container #submitbutton:active, |
||
| 494 | #loading-mask #form-container #submitbutton:active { |
||
| 495 | background: {{primary-color:hover}}; |
||
| 496 | } |
||
| 497 | |||
| 498 | /* Background color of the hover state of the buttons in the top menu bar */ |
||
| 499 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .x-btn.x-btn-over, |
||
| 500 | /* Background color of the active state of the buttons (i.e. when the buttons get clicked) */ |
||
| 501 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .x-btn.x-btn-over.x-btn-click, |
||
| 502 | /* Background color of the selected button */ |
||
| 503 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .zarafa-maintabbar-maintab-active, |
||
| 504 | /* Background color of the hover state of selected button */ |
||
| 505 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .zarafa-maintabbar-maintab-active.x-btn-over, |
||
| 506 | /* Background color of the active state of selected button */ |
||
| 507 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .zarafa-maintabbar-maintab-active.x-btn-over.x-btn-click { |
||
| 508 | background-color: {{primary-color:hover}} !important; |
||
| 509 | } |
||
| 510 | ', |
||
| 511 | |||
| 512 | 'mainbar-text-color' => ' |
||
| 513 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct, |
||
| 514 | /* Text color of the buttons in the top menu bar */ |
||
| 515 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .x-btn button.x-btn-text, |
||
| 516 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .x-btn-over button.x-btn-text, |
||
| 517 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .x-btn-over.x-btn-click button.x-btn-text, |
||
| 518 | /* Text color of the selected button in the top menu bar */ |
||
| 519 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .zarafa-maintabbar-maintab-active button.x-btn-text, |
||
| 520 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .zarafa-maintabbar-maintab-active.x-btn-over button.x-btn-text, |
||
| 521 | body #zarafa-mainmenu.zarafa-maintabbar > .x-toolbar-ct .zarafa-maintabbar-maintab-active.x-btn-over.x-btn-click button.x-btn-text { |
||
| 522 | color: {{mainbar-text-color}} !important; |
||
| 523 | } |
||
| 524 | ', |
||
| 525 | |||
| 526 | 'action-color' => ' |
||
| 527 | /**************************************************************************** |
||
| 528 | * Action color |
||
| 529 | * =============== |
||
| 530 | * Some elements have a different color than the default color of these elements |
||
| 531 | * to get extra attention, e.g. "call-to-action buttons", the current day |
||
| 532 | * in the calendar, etc. |
||
| 533 | ****************************************************************************/ |
||
| 534 | /* Buttons, normal state */ |
||
| 535 | .x-btn.zarafa-action .x-btn-small, |
||
| 536 | .x-btn.zarafa-action .x-btn-medium, |
||
| 537 | .x-btn.zarafa-action .x-btn-large, |
||
| 538 | /* Buttons, active state */ |
||
| 539 | .x-btn.zarafa-action.x-btn-over.x-btn-click .x-btn-small, |
||
| 540 | .x-btn.zarafa-action.x-btn-over.x-btn-click .x-btn-medium, |
||
| 541 | .x-btn.zarafa-action.x-btn-over.x-btn-click .x-btn-large, |
||
| 542 | .x-btn.zarafa-action.x-btn-click .x-btn-small, |
||
| 543 | .x-btn.zarafa-action.x-btn-click .x-btn-medium, |
||
| 544 | .x-btn.zarafa-action.x-btn-click .x-btn-large, |
||
| 545 | /* Special case: Popup, Windows, or Messageboxes (first button is by default styled as the action button) */ |
||
| 546 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-small, |
||
| 547 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-medium, |
||
| 548 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-large, |
||
| 549 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-small, |
||
| 550 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-medium, |
||
| 551 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-large, |
||
| 552 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-small, |
||
| 553 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-medium, |
||
| 554 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-large, |
||
| 555 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-small, |
||
| 556 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-medium, |
||
| 557 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-large, |
||
| 558 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-small, |
||
| 559 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-medium, |
||
| 560 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-large, |
||
| 561 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-small, |
||
| 562 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-medium, |
||
| 563 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-large, |
||
| 564 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-small, |
||
| 565 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-medium, |
||
| 566 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-large, |
||
| 567 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-small, |
||
| 568 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-medium, |
||
| 569 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-large, |
||
| 570 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-small, |
||
| 571 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-medium, |
||
| 572 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-large, |
||
| 573 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-small, |
||
| 574 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-medium, |
||
| 575 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-large, |
||
| 576 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-small, |
||
| 577 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-medium, |
||
| 578 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-large, |
||
| 579 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-small, |
||
| 580 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-medium, |
||
| 581 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-large, |
||
| 582 | /* action button in reminder popout */ |
||
| 583 | .k-reminderpanel .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn:not(.zarafa-normal) .x-btn-small, |
||
| 584 | .k-reminderpanel .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-click:not(.zarafa-normal) .x-btn-small, |
||
| 585 | .k-reminderpanel .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over.x-btn-click:not(.zarafa-normal) .x-btn-small, |
||
| 586 | /* Current day in the calendar */ |
||
| 587 | .zarafa-freebusy-panel .x-freebusy-timeline-container .x-freebusy-header .x-freebusy-header-body .x-freebusy-timeline-day.x-freebusy-timeline-day-current, |
||
| 588 | .zarafa-freebusy-panel .x-freebusy-timeline-container .x-freebusy-header .x-freebusy-header-body .x-freebusy-timeline-day.x-freebusy-timeline-day-current table, |
||
| 589 | .zarafa-freebusy-panel .x-freebusy-timeline-container .x-freebusy-header .x-freebusy-header-body .x-freebusy-timeline-day.x-freebusy-timeline-day-current table tr.x-freebusy-timeline-day td, |
||
| 590 | /* The date pickers */ |
||
| 591 | .x-date-picker .x-date-inner td.x-date-today a, |
||
| 592 | .x-date-picker .x-date-mp table td.x-date-mp-sel a, |
||
| 593 | .x-date-picker .x-date-mp table tr.x-date-mp-btns td button.x-date-mp-ok { |
||
| 594 | background: {{action-color}} !important; |
||
| 595 | } |
||
| 596 | /* Focused Action button */ |
||
| 597 | .x-btn.zarafa-action.x-btn-focus .x-btn-small, .x-btn.zarafa-action.x-btn-focus .x-btn-medium, .x-btn.zarafa-action.x-btn-focus .x-btn-large { |
||
| 598 | background: {{action-color}} !important; |
||
| 599 | } |
||
| 600 | /* Selected calendar */ |
||
| 601 | .zarafa-calendar-tabarea-stroke.zarafa-calendar-tab-selected { |
||
| 602 | border-top-color: {{action-color}}; |
||
| 603 | } |
||
| 604 | .x-date-picker .x-date-inner td.x-date-weeknumber a, |
||
| 605 | .zarafa-hierarchy-node-total-count span.zarafa-hierarchy-node-counter, |
||
| 606 | .zarafa-hierarchy-node-unread-count span.zarafa-hierarchy-node-counter { |
||
| 607 | color: {{action-color}}; |
||
| 608 | } |
||
| 609 | .x-date-picker .x-date-inner td.x-date-today a { |
||
| 610 | border-color: {{action-color}}; |
||
| 611 | } |
||
| 612 | .zarafa-freebusy-panel .x-freebusy-timeline-container .x-freebusy-header .x-freebusy-header-body .x-freebusy-timeline-day.x-freebusy-timeline-day-current, |
||
| 613 | .zarafa-freebusy-panel .x-freebusy-timeline-container .x-freebusy-body .x-freebusy-background .x-freebusy-timeline-day.x-freebusy-timeline-day-current { |
||
| 614 | border-right-color: {{action-color}}; |
||
| 615 | } |
||
| 616 | .zarafa-freebusy-panel .x-freebusy-timeline-container .x-freebusy-header .x-freebusy-header-body .x-freebusy-timeline-day.x-freebusy-timeline-day-current table tr.x-freebusy-timeline-hour td:first-child, |
||
| 617 | .zarafa-freebusy-panel .x-freebusy-timeline-container .x-freebusy-body .x-freebusy-background .x-freebusy-timeline-day.x-freebusy-timeline-day-current td:first-child { |
||
| 618 | border-left-color: {{action-color}}; |
||
| 619 | } |
||
| 620 | ', |
||
| 621 | |||
| 622 | 'action-color:hover' => ' |
||
| 623 | /* Buttons, hover state */ |
||
| 624 | .x-btn.zarafa-action.x-btn-over .x-btn-small, |
||
| 625 | .x-btn.zarafa-action.x-btn-over .x-btn-medium, |
||
| 626 | .x-btn.zarafa-action.x-btn-over .x-btn-large, |
||
| 627 | /* Special case: Popup, Windows, or Messageboxes */ |
||
| 628 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-small, |
||
| 629 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-medium, |
||
| 630 | .x-window .x-panel-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-large, |
||
| 631 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-small, |
||
| 632 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-medium, |
||
| 633 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-large, |
||
| 634 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-small, |
||
| 635 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-medium, |
||
| 636 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-large, |
||
| 637 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-small, |
||
| 638 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-medium, |
||
| 639 | .x-window .x-window-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-large, |
||
| 640 | /* action button in reminder popout */ |
||
| 641 | .k-reminderpanel .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-over:not(.zarafa-normal) .x-btn-small, |
||
| 642 | /* The date pickers */ |
||
| 643 | .x-date-picker .x-date-mp table tr.x-date-mp-btns td button.x-date-mp-ok:hover { |
||
| 644 | background: {{action-color:hover}} !important; |
||
| 645 | } |
||
| 646 | ', |
||
| 647 | |||
| 648 | 'selection-color' => ' |
||
| 649 | /********************************************************************* |
||
| 650 | * Selected items in grids and trees |
||
| 651 | * ================================= |
||
| 652 | * The background color of the selected items in grids and trees can |
||
| 653 | * be changed to better suit the theme. |
||
| 654 | *********************************************************************/ |
||
| 655 | /* selected item in grids */ |
||
| 656 | .x-grid3-row.x-grid3-row-selected, |
||
| 657 | .x-grid3 .x-grid3-row-selected .zarafa-grid-button-container, |
||
| 658 | /* selected item in tree hierarchies */ |
||
| 659 | .x-tree-node .zarafa-hierarchy-node.x-tree-selected, |
||
| 660 | /* selected items in boxfields (e.g. the recipient fields) */ |
||
| 661 | .x-zarafa-boxfield ul .x-zarafa-boxfield-item-focus, |
||
| 662 | .x-zarafa-boxfield ul .x-zarafa-boxfield-recipient-item.x-zarafa-boxfield-item-focus, |
||
| 663 | /* selected items in card view of Contacts context */ |
||
| 664 | div.zarafa-contact-cardview-selected, |
||
| 665 | /* selected items in icon view of Notes context */ |
||
| 666 | .zarafa-note-iconview-selected, |
||
| 667 | /* selected category in the Settings context */ |
||
| 668 | #zarafa-mainpanel-contentpanel-settings .zarafa-settings-category-panel .zarafa-settings-category-tab-active, |
||
| 669 | /* selected date in date pickers */ |
||
| 670 | .x-date-picker .x-date-inner td.x-date-selected:not(.x-date-today) a, |
||
| 671 | .x-date-picker .x-date-inner td.x-date-selected:not(.x-date-today) a:hover { |
||
| 672 | background-color: {{selection-color}} !important; |
||
| 673 | border-color: {{selection-color}}; |
||
| 674 | } |
||
| 675 | |||
| 676 | /* Selected x-menu */ |
||
| 677 | .x-menu-item-selected { |
||
| 678 | background-color: {{selection-color}}; |
||
| 679 | } |
||
| 680 | |||
| 681 | /********************************************************************* |
||
| 682 | * Extra information about items |
||
| 683 | * ================================= |
||
| 684 | * Sometimes extra information is shown in opened items. (e.g. "You replied |
||
| 685 | * to this message etc"). This can be styled with the following rules. |
||
| 686 | *********************************************************************/ |
||
| 687 | .preview-header-extrainfobox, |
||
| 688 | .preview-header-extrainfobox-item, |
||
| 689 | .k-appointmentcreatetab .zarafa-calendar-appointment-extrainfo div, |
||
| 690 | .k-taskgeneraltab .zarafa-calendar-appointment-extrainfo div, |
||
| 691 | .zarafa-mailcreatepanel > .x-panel-bwrap > .x-panel-body .zarafa-mailcreatepanel-extrainfo div { |
||
| 692 | background: {{selection-color}} !important; |
||
| 693 | } |
||
| 694 | |||
| 695 | /* Selected mail item */ |
||
| 696 | .k-unreadborders .x-grid3-row.x-grid3-row-expanded.mail_read.x-grid3-row-selected > table { |
||
| 697 | border-left: 4px solid {{selection-color}} !important; |
||
| 698 | } |
||
| 699 | |||
| 700 | /* Hover selected item */ |
||
| 701 | .k-unreadborders .x-grid3-row.x-grid3-row-expanded.mail_read.x-grid3-row-selected.x-grid3-row-over > table, |
||
| 702 | .k-unreadborders .x-grid3-row.x-grid3-row-collapsed.mail_read.x-grid3-row-selected > table { |
||
| 703 | border-left: 4px solid {{selection-color}} !important; |
||
| 704 | } |
||
| 705 | ', |
||
| 706 | |||
| 707 | 'selection-text-color' => ' |
||
| 708 | /********************************************************************* |
||
| 709 | * Extra information about items |
||
| 710 | * ================================= |
||
| 711 | * Sometimes extra information is shown in opened items. (e.g. "You replied |
||
| 712 | * to this message etc"). This can be styled with the following rules. |
||
| 713 | *********************************************************************/ |
||
| 714 | .preview-header-extrainfobox, |
||
| 715 | .preview-header-extrainfobox-item, |
||
| 716 | .k-appointmentcreatetab .zarafa-calendar-appointment-extrainfo div, |
||
| 717 | .k-taskgeneraltab .zarafa-calendar-appointment-extrainfo div, |
||
| 718 | .zarafa-mailcreatepanel > .x-panel-bwrap > .x-panel-body .zarafa-mailcreatepanel-extrainfo div { |
||
| 719 | color: {{selection-text-color}}; |
||
| 720 | } |
||
| 721 | ', |
||
| 722 | |||
| 723 | 'focus-color' => ' |
||
| 724 | /********************************************************************* |
||
| 725 | * Focused items |
||
| 726 | * ================================= |
||
| 727 | *********************************************************************/ |
||
| 728 | /* Normal button */ |
||
| 729 | .x-window .x-window-footer .x-toolbar-left-row .x-toolbar-cell:not(.x-hide-offsets) ~ .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-focus:not(.zarafa-action):not(.x-btn-over):not(.x-btn-click) .x-btn-small, |
||
| 730 | .x-window .x-panel-footer .x-toolbar-right-row .x-toolbar-cell:not(.x-hide-offsets) ~ .x-toolbar-cell:not(.x-hide-offsets) .x-btn.x-btn-focus:not(.zarafa-action):not(.x-btn-over):not(.x-btn-click) .x-btn-small, |
||
| 731 | .x-btn.x-btn-focus:not(.zarafa-action):not(.x-btn-click) .x-btn-small, |
||
| 732 | .x-btn.x-btn-focus:not(.zarafa-action):not(.x-btn-click) .x-btn-medium, |
||
| 733 | .x-btn.x-btn-focus:not(.zarafa-action):not(.x-btn-click) .x-btn-large, |
||
| 734 | .x-toolbar .x-btn.x-btn-focus:not(.zarafa-action):not(.x-btn-noicon) .x-btn-small { |
||
| 735 | border: 1px solid {{focus-color}} !important; |
||
| 736 | } |
||
| 737 | /* Login */ |
||
| 738 | body.login #form-container input:focus, |
||
| 739 | #loading-mask #form-container input:focus { |
||
| 740 | border-color: {{focus-color}}; |
||
| 741 | } |
||
| 742 | input:focus { |
||
| 743 | border-color: {{focus-color}}; |
||
| 744 | } |
||
| 745 | /* Form elements */ |
||
| 746 | .x-form-text.x-form-focus:not(.x-trigger-noedit) { |
||
| 747 | border-color: {{focus-color}} !important; |
||
| 748 | } |
||
| 749 | .x-form-field-wrap.x-trigger-wrap-focus:not(.x-freebusy-userlist-container) { |
||
| 750 | border-color: {{focus-color}}; |
||
| 751 | } |
||
| 752 | input.x-form-text.x-form-field.x-form-focus { |
||
| 753 | border-color: {{focus-color}} !important; |
||
| 754 | } |
||
| 755 | .x-form-field-wrap.x-trigger-wrap-focus:not(.x-freebusy-userlist-container) input.x-form-text.x-form-field.x-form-focus { |
||
| 756 | border-color: {{focus-color}} !important; |
||
| 757 | } |
||
| 758 | ', |
||
| 759 | |||
| 760 | 'logo-large' => ' |
||
| 761 | /* The logo in the Login screen. Maximum size of the logo image is 220x60px. */ |
||
| 762 | body.login #form-container #logo, |
||
| 763 | #loading-mask #form-container #logo { |
||
| 764 | background: url({{logo-large}}) no-repeat right center; |
||
| 765 | background-size: contain; |
||
| 766 | } |
||
| 767 | ', |
||
| 768 | |||
| 769 | 'logo-small' => ' |
||
| 770 | /**************************************************************************** |
||
| 771 | * The logo (shown on the right below the top bar) |
||
| 772 | * =============================================== |
||
| 773 | * The maximum height of the image that can be shown is 45px. |
||
| 774 | ****************************************************************************/ |
||
| 775 | .zarafa-maintoolbar { |
||
| 776 | background-image: url({{logo-small}}); |
||
| 777 | background-size: auto 38px; |
||
| 778 | } |
||
| 779 | ', |
||
| 780 | |||
| 781 | 'background-image' => ' |
||
| 782 | /********************************************************************************************* |
||
| 783 | * The Login screen and the Welcome screen |
||
| 784 | * ======================================= |
||
| 785 | ********************************************************************************************/ |
||
| 786 | /* Background image of the login screen */ |
||
| 787 | body.login, |
||
| 788 | #loading-mask, |
||
| 789 | #bg, |
||
| 790 | /* Background image of the Welcome screen */ |
||
| 791 | body.zarafa-welcome { |
||
| 792 | background: url({{background-image}}) no-repeat center center; |
||
| 793 | background-size: cover; |
||
| 794 | } |
||
| 795 | ', |
||
| 796 | |||
| 797 | 'spinner-image' => ' |
||
| 798 | /* The spinner of the login/loading screen */ |
||
| 806 |