3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
18 * @package CiviCRM_Hook
19 * @copyright CiviCRM LLC https://civicrm.org/licensing
24 * The "default" theme adapts based on the latest recommendation from civicrm.org
25 * by switching to DEFAULT_THEME at runtime.
27 const DEFAULT_THEME
= 'greenwich';
30 * Fallback is a pseudotheme which can be included in "search_order".
31 * It locates files in the core/extension (non-theme) codebase.
33 const FALLBACK_THEME
= '_fallback_';
35 const PASSTHRU
= 'PASSTHRU';
41 private $activeThemeKey = NULL;
45 * Array(string $themeKey => array $themeSpec).
47 private $themes = NULL;
50 * @var \CRM_Utils_Cache_Interface
52 private $cache = NULL;
56 * @param \CRM_Utils_Cache_Interface $cache
58 public function __construct($cache = NULL) {
59 $this->cache
= $cache ?
$cache : Civi
::cache('long');
63 * Determine the name of active theme.
68 public function getActiveThemeKey() {
69 if ($this->activeThemeKey
=== NULL) {
70 // Ambivalent: is it better to use $config->userFrameworkFrontend or $template->get('urlIsPublic')?
71 $config = \CRM_Core_Config
::singleton();
72 $settingKey = $config->userSystem
->isFrontEndPage() ?
'theme_frontend' : 'theme_backend';
74 $themeKey = Civi
::settings()->get($settingKey);
75 if ($themeKey === 'default') {
76 $themeKey = self
::DEFAULT_THEME
;
79 \CRM_Utils_Hook
::activeTheme($themeKey, [
81 'page' => \CRM_Utils_System
::currentPath(),
84 $themes = $this->getAll();
85 $this->activeThemeKey
= isset($themes[$themeKey]) ?
$themeKey : self
::DEFAULT_THEME
;
87 return $this->activeThemeKey
;
91 * Get the definition of the theme.
93 * @param string $themeKey
94 * Ex: 'greenwich', 'shoreditch'.
96 * @see CRM_Utils_Hook::themes
98 public function get($themeKey) {
99 $all = $this->getAll();
100 return $all[$themeKey] ??
NULL;
104 * Get a list of all known themes, including hidden base themes.
107 * List of themes, keyed by name. Same format as CRM_Utils_Hook::themes(),
108 * but any default values are filled in.
109 * @see CRM_Utils_Hook::themes
111 public function getAll() {
112 if ($this->themes
=== NULL) {
113 // Cache includes URLs/paths, which change with runtime.
114 $cacheKey = 'theme_list_' . \CRM_Core_Config_Runtime
::getId();
115 $this->themes
= $this->cache
->get($cacheKey);
116 if ($this->themes
=== NULL) {
117 $this->themes
= $this->buildAll();
118 $this->cache
->set($cacheKey, $this->themes
);
121 return $this->themes
;
125 * Get a list of available themes, excluding hidden base themes.
127 * This is the same as getAll(), but abstract themes like "_fallback_"
128 * or "_newyork_base_" are omitted.
132 * Ex: ['greenwich' => 'Greenwich', 'shoreditch' => 'Shoreditch'].
133 * @see CRM_Utils_Hook::themes
135 public function getAvailable() {
137 foreach ($this->getAll() as $key => $theme) {
138 if ($key[0] !== '_') {
139 $result[$key] = $theme['title'];
146 * Get the URL(s) for a themed CSS file.
148 * This implements a prioritized search, in order:
149 * - Check for the specified theme.
150 * - If that doesn't exist, check for the default theme.
151 * - If that doesn't exist, use the 'none' theme.
153 * @param string $active
156 * @param string $cssExt
158 * @param string $cssFile
159 * Ex: 'css/bootstrap.css' or 'css/civicrm.css'.
161 * List of URLs to display.
162 * Ex: array(string $url)
164 public function resolveUrls($active, $cssExt, $cssFile) {
165 $all = $this->getAll();
166 if (!isset($all[$active])) {
170 $cssId = $this->cssId($cssExt, $cssFile);
172 foreach ($all[$active]['search_order'] as $themeKey) {
173 if (isset($all[$themeKey]['excludes']) && in_array($cssId, $all[$themeKey]['excludes'])) {
177 $result = Civi\Core\Resolver
::singleton()
178 ->call($all[$themeKey]['url_callback'], array($this, $themeKey, $cssExt, $cssFile));
181 if ($result !== self
::PASSTHRU
) {
186 throw new \
RuntimeException("Failed to resolve URL. Theme metadata may be incomplete.");
190 * Construct the list of available themes.
193 * List of themes, keyed by name.
194 * @see CRM_Utils_Hook::themes
196 protected function buildAll() {
200 'title' => ts('Automatic'),
201 'help' => ts('Determine a system default automatically'),
202 // This is an alias. url_callback, search_order don't matter.
204 'greenwich' => array(
206 'title' => 'Greenwich',
207 'help' => ts('CiviCRM 4.x look-and-feel'),
211 'title' => ts('None (Unstyled)'),
212 'help' => ts('Disable CiviCRM\'s built-in CSS files.'),
213 'search_order' => array('none', self
::FALLBACK_THEME
),
219 self
::FALLBACK_THEME
=> array(
221 'title' => 'Fallback (Abstract Base Theme)',
222 'url_callback' => '\Civi\Core\Themes\Resolvers::fallback',
223 'search_order' => array(self
::FALLBACK_THEME
),
227 \CRM_Utils_Hook
::themes($themes);
229 foreach (array_keys($themes) as $themeKey) {
230 $themes[$themeKey] = $this->build($themeKey, $themes[$themeKey]);
237 * Apply defaults for a given them.
239 * @param string $themeKey
240 * The name of the theme. Ex: 'greenwich'.
241 * @param array $theme
242 * The original theme definition of the theme (per CRM_Utils_Hook::themes).
244 * The full theme definition of the theme (per CRM_Utils_Hook::themes).
245 * @see CRM_Utils_Hook::themes
247 protected function build($themeKey, $theme) {
250 'url_callback' => '\Civi\Core\Themes\Resolvers::simple',
251 'search_order' => array($themeKey, self
::FALLBACK_THEME
),
253 $theme = array_merge($defaults, $theme);
259 * @param string $cssExt
260 * @param string $cssFile
263 public function cssId($cssExt, $cssFile) {
264 return ($cssExt === 'civicrm') ?
$cssFile : "$cssExt-$cssFile";