Civi\Core\SettingsManager - Don't try to read settings if there's no DB
[civicrm-core.git] / Civi / Core / SettingsManager.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 namespace Civi\Core;
29
30 /**
31 * Class SettingsManager
32 * @package Civi\Core
33 *
34 * The SettingsManager is responsible for tracking settings across various
35 * domains and users.
36 *
37 * Generally, for any given setting, there are three levels where values
38 * can be declared:
39 *
40 * - Mandatory values (which come from a global $civicrm_setting).
41 * - Explicit values (which are chosen by the user and stored in the DB).
42 * - Default values (which come from the settings metadata).
43 *
44 * Note: During the early stages of bootstrap, default values are not be available.
45 * Loading the defaults requires loading metadata from various sources. However,
46 * near the end of bootstrap, one calls SettingsManager::useDefaults() to fetch
47 * and merge the defaults.
48 *
49 * Note: In a typical usage, there will only be one active domain and one
50 * active contact (each having its own bag) within a given request. However,
51 * in some edge-cases, you may need to work with multiple domains/contacts
52 * at the same time.
53 *
54 * @see SettingsManagerTest
55 */
56 class SettingsManager {
57
58 /**
59 * @var \CRM_Utils_Cache_Interface
60 */
61 protected $cache;
62
63 /**
64 * @var
65 * Array (int $id => SettingsBag $bag).
66 */
67 protected $bagsByDomain = array(), $bagsByContact = array();
68
69 /**
70 * @var array
71 * Array(string $entity => array(string $settingName => mixed $value)).
72 * Ex: $mandatory['domain']['uploadDir'].
73 */
74 protected $mandatory = NULL;
75
76 /**
77 * Whether to use defaults.
78 *
79 * @var bool
80 */
81 protected $useDefaults = FALSE;
82
83 /**
84 * @param \CRM_Utils_Cache_Interface $cache
85 * A semi-durable location to store metadata.
86 * @param NULL|array $mandatory
87 * Ex: $mandatory['domain']['uploadDir'].
88 */
89 public function __construct($cache, $mandatory = NULL) {
90 $this->cache = $cache;
91 $this->mandatory = $mandatory;
92 }
93
94 /**
95 * Ensure that all defaults and mandatory values are included with
96 * all current and future bags.
97 *
98 * @return $this
99 */
100 public function useDefaults() {
101 if (!$this->useDefaults) {
102 $this->useDefaults = TRUE;
103
104 if (!empty($this->bagsByDomain)) {
105 foreach ($this->bagsByDomain as $bag) {
106 $bag->loadDefaults($this->getDefaults('domain'));
107 }
108 }
109
110 if (!empty($this->bagsByContact)) {
111 foreach ($this->bagsByContact as $bag) {
112 $bag->loadDefaults($this->getDefaults('contact'));
113 }
114 }
115 }
116
117 return $this;
118 }
119
120 /**
121 * @param int|NULL $domainId
122 * @return SettingsBag
123 */
124 public function getBagByDomain($domainId) {
125 if ($domainId === NULL) {
126 $domainId = \CRM_Core_Config::domainID();
127 }
128
129 if (!isset($this->bagsByDomain[$domainId])) {
130 $this->bagsByDomain[$domainId] = new SettingsBag($domainId, NULL);
131 if (\CRM_Core_Config::singleton()->dsn) {
132 $this->bagsByDomain[$domainId]->loadValues();
133 }
134 $this->bagsByDomain[$domainId]
135 ->loadMandatory($this->getMandatory('domain'))
136 ->loadDefaults($this->getDefaults('domain'));
137 }
138 return $this->bagsByDomain[$domainId];
139 }
140
141 /**
142 * @param int|NULL $domainId
143 * @param int|NULL $contactId
144 * @return SettingsBag
145 */
146 public function getBagByContact($domainId, $contactId) {
147 if ($domainId === NULL) {
148 $domainId = \CRM_Core_Config::domainID();
149 }
150
151 $key = "$domainId:$contactId";
152 if (!isset($this->bagsByContact[$key])) {
153 $this->bagsByContact[$key] = new SettingsBag($domainId, $contactId);
154 if (\CRM_Core_Config::singleton()->dsn) {
155 $this->bagsByContact[$key]->loadValues();
156 }
157 $this->bagsByContact[$key]
158 ->loadDefaults($this->getDefaults('contact'))
159 ->loadMandatory($this->getMandatory('contact'));
160 }
161 return $this->bagsByContact[$key];
162 }
163
164 /**
165 * Determine the default settings.
166 *
167 * @param string $entity
168 * Ex: 'domain' or 'contact'.
169 * @return array
170 * Array(string $settingName => mixed $value).
171 */
172 protected function getDefaults($entity) {
173 if (!$this->useDefaults) {
174 return array();
175 }
176
177 $cacheKey = 'defaults:' . $entity;
178 $defaults = $this->cache->get($cacheKey);
179 if (!is_array($defaults)) {
180 $specs = \CRM_Core_BAO_Setting::getSettingSpecification(NULL, array(
181 'is_contact' => ($entity === 'contact' ? 1 : 0),
182 ));
183 $defaults = array();
184 foreach ($specs as $key => $spec) {
185 $defaults[$key] = \CRM_Utils_Array::value('default', $spec);
186 }
187 $this->cache->set($cacheKey, $defaults);
188 }
189 return $defaults;
190 }
191
192 /**
193 * Get a list of mandatory/overriden settings.
194 *
195 * @param string $entity
196 * Ex: 'domain' or 'contact'.
197 * @return array
198 * Array(string $settingName => mixed $value).
199 */
200 protected function getMandatory($entity) {
201 // Prepare and cache list of all mandatory settings.
202 if ($this->mandatory === NULL) {
203 if (isset($GLOBALS['civicrm_setting'])) {
204 $this->mandatory = self::parseMandatorySettings($GLOBALS['civicrm_setting']);
205 }
206 else {
207 $this->mandatory = array();
208 }
209 }
210
211 return \CRM_Utils_Array::value($entity, $this->mandatory, array());
212 }
213
214 /**
215 * Parse mandatory settings.
216 *
217 * In previous versions, settings were broken down into verbose+dynamic group names, e.g.
218 *
219 * $civicrm_settings['Foo Bar Preferences']['foo'] = 'bar';
220 *
221 * We now simplify to two simple groups, 'domain' and 'contact'.
222 *
223 * $civicrm_settings['domain']['foo'] = 'bar';
224 *
225 * However, the old groups are grand-fathered in as aliases.
226 *
227 * @param array $civicrm_setting
228 * Ex: $civicrm_setting['Group Name']['field'] = 'value'.
229 * Group names are an historical quirk; ignore them.
230 * @return array
231 */
232 public static function parseMandatorySettings($civicrm_setting) {
233 $rewriteGroups = array(
234 \CRM_Core_BAO_Setting::ADDRESS_STANDARDIZATION_PREFERENCES_NAME => 'domain',
235 \CRM_Core_BAO_Setting::CAMPAIGN_PREFERENCES_NAME => 'domain',
236 \CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME => 'domain',
237 \CRM_Core_BAO_Setting::DEVELOPER_PREFERENCES_NAME => 'domain',
238 \CRM_Core_BAO_Setting::DIRECTORY_PREFERENCES_NAME => 'domain',
239 \CRM_Core_BAO_Setting::EVENT_PREFERENCES_NAME => 'domain',
240 \CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME => 'domain',
241 \CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME => 'domain',
242 \CRM_Core_BAO_Setting::MAP_PREFERENCES_NAME => 'domain',
243 \CRM_Core_BAO_Setting::MEMBER_PREFERENCES_NAME => 'domain',
244 \CRM_Core_BAO_Setting::MULTISITE_PREFERENCES_NAME => 'domain',
245 \CRM_Core_BAO_Setting::PERSONAL_PREFERENCES_NAME => 'contact',
246 \CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME => 'domain',
247 \CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME => 'domain',
248 \CRM_Core_BAO_Setting::URL_PREFERENCES_NAME => 'domain',
249 );
250
251 if (is_array($civicrm_setting)) {
252 foreach ($rewriteGroups as $oldGroup => $newGroup) {
253 if (!isset($civicrm_setting[$newGroup])) {
254 $civicrm_setting[$newGroup] = array();
255 }
256 if (isset($civicrm_setting[$oldGroup])) {
257 $civicrm_setting[$newGroup] = array_merge($civicrm_setting[$oldGroup], $civicrm_setting[$newGroup]);
258 unset($civicrm_setting[$oldGroup]);
259 }
260 }
261 }
262 return $civicrm_setting;
263 }
264
265 }