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