ab54ebdebc37688b1374db7e04a62d68ab825559
[civicrm-core.git] / Civi / Core / SettingsBag.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 SettingsBag
32 * @package Civi\Core
33 *
34 * Read and write settings for a given domain (or contact).
35 *
36 * If the target entity does not already have a value for the setting, then
37 * the defaults will be used. If mandatory values are provided, they will
38 * override any defaults or custom settings.
39 *
40 * It's expected that the SettingsBag will have O(50-250) settings -- and that
41 * we'll load the full bag on many page requests. Consequently, we don't
42 * want the full metadata (help text and version history and HTML widgets)
43 * for all 250 settings, but we do need the default values.
44 *
45 * This class is not usually instantiated directly. Instead, use SettingsManager
46 * or Civi::settings().
47 *
48 * @see \Civi::settings()
49 * @see SettingsManagerTest
50 */
51 class SettingsBag {
52
53 protected $domainId;
54
55 protected $contactId;
56
57 /**
58 * @var array
59 * Array(string $settingName => mixed $value).
60 */
61 protected $defaults;
62
63 /**
64 * @var array
65 * Array(string $settingName => mixed $value).
66 */
67 protected $mandatory;
68
69 /**
70 * The result of combining default values, mandatory
71 * values, and user values.
72 *
73 * @var array|NULL
74 * Array(string $settingName => mixed $value).
75 */
76 protected $combined;
77
78 /**
79 * @var array
80 */
81 protected $values;
82
83 protected $filteredValues;
84
85 /**
86 * @param int $domainId
87 * The domain for which we want settings.
88 * @param int|NULL $contactId
89 * The contact for which we want settings. Use NULL for domain settings.
90 */
91 public function __construct($domainId, $contactId) {
92 $this->domainId = $domainId;
93 $this->contactId = $contactId;
94 $this->values = array();
95 $this->filteredValues = array();
96 $this->combined = NULL;
97 }
98
99 /**
100 * Set/replace the default values.
101 *
102 * @param array $defaults
103 * Array(string $settingName => mixed $value).
104 * @return $this
105 */
106 public function loadDefaults($defaults) {
107 $this->defaults = $defaults;
108 $this->filteredValues = array();
109 $this->combined = NULL;
110 return $this;
111 }
112
113 /**
114 * Set/replace the mandatory values.
115 *
116 * @param array $mandatory
117 * Array(string $settingName => mixed $value).
118 * @return $this
119 */
120 public function loadMandatory($mandatory) {
121 $this->mandatory = $mandatory;
122 $this->filteredValues = array();
123 $this->combined = NULL;
124 return $this;
125 }
126
127 /**
128 * Load all explicit settings that apply to this domain or contact.
129 *
130 * @return $this
131 */
132 public function loadValues() {
133 $this->values = array();
134 // Note: Don't use Setting DAO. It requires fields() which requires
135 // translations -- which are keyed off settings!
136 $dao = \CRM_Core_DAO::executeQuery($this->createQuery()->toSQL());
137 while ($dao->fetch()) {
138 $this->values[$dao->name] = ($dao->value !== NULL) ? unserialize($dao->value) : NULL;
139 }
140 $this->combined = NULL;
141 return $this;
142 }
143
144 /**
145 * Add a batch of settings. Save them.
146 *
147 * @param array $settings
148 * Array(string $settingName => mixed $settingValue).
149 * @return $this
150 */
151 public function add(array $settings) {
152 foreach ($settings as $key => $value) {
153 $this->set($key, $value);
154 }
155 return $this;
156 }
157
158 /**
159 * Get a list of all effective settings.
160 *
161 * @return array
162 * Array(string $settingName => mixed $settingValue).
163 */
164 public function all() {
165 if ($this->combined === NULL) {
166 $this->combined = $this->combine(
167 array($this->defaults, $this->values, $this->mandatory)
168 );
169 }
170 return $this->combined;
171 }
172
173 /**
174 * Determine the effective value.
175 *
176 * @param string $key
177 * @return mixed
178 */
179 public function get($key) {
180 $all = $this->all();
181 return isset($all[$key]) ? $all[$key] : NULL;
182 }
183
184 /**
185 * Determine the default value of a setting.
186 *
187 * @param string $key
188 * The simple name of the setting.
189 * @return mixed|NULL
190 */
191 public function getDefault($key) {
192 return isset($this->defaults[$key]) ? $this->defaults[$key] : NULL;
193 }
194
195 /**
196 * Determine the explicitly designated value, regardless of
197 * any default or mandatory values.
198 *
199 * @param string $key
200 * The simple name of the setting.
201 * @return mixed|NULL
202 */
203 public function getExplicit($key) {
204 return (isset($this->values[$key]) ? $this->values[$key] : NULL);
205 }
206
207 /**
208 * Determine the mandatory value of a setting.
209 *
210 * @param string $key
211 * The simple name of the setting.
212 * @return mixed|NULL
213 */
214 public function getMandatory($key) {
215 return isset($this->mandatory[$key]) ? $this->mandatory[$key] : NULL;
216 }
217
218 /**
219 * Determine if the entity has explicitly designated a value.
220 *
221 * Note that get() may still return other values based on
222 * mandatory values or defaults.
223 *
224 * @param string $key
225 * The simple name of the setting.
226 * @return bool
227 */
228 public function hasExplict($key) {
229 // NULL means no designated value.
230 return isset($this->values[$key]);
231 }
232
233 /**
234 * Removes any explicit settings. This restores the default.
235 *
236 * @param string $key
237 * The simple name of the setting.
238 * @return $this
239 */
240 public function revert($key) {
241 // It might be better to DELETE (to avoid long-term leaks),
242 // but setting NULL is simpler for now.
243 return $this->set($key, NULL);
244 }
245
246 /**
247 * Add a single setting. Save it.
248 *
249 * @param string $key
250 * The simple name of the setting.
251 * @param mixed $value
252 * The new, explicit value of the setting.
253 * @return $this
254 */
255 public function set($key, $value) {
256 $this->setDb($key, $value);
257 $this->values[$key] = $value;
258 unset($this->filteredValues[$key]);
259 $this->combined = NULL;
260 return $this;
261 }
262
263 /**
264 * @return \CRM_Utils_SQL_Select
265 */
266 protected function createQuery() {
267 $select = \CRM_Utils_SQL_Select::from('civicrm_setting')
268 ->select('id, group_name, name, value, domain_id, contact_id, is_domain, component_id, created_date, created_id')
269 ->where('domain_id = #id', array(
270 'id' => $this->domainId,
271 ));
272 if ($this->contactId === NULL) {
273 $select->where('is_domain = 1');
274 }
275 else {
276 $select->where('contact_id = #id', array(
277 'id' => $this->contactId,
278 ));
279 $select->where('is_domain = 0');
280 }
281 return $select;
282 }
283
284 /**
285 * Combine a series of arrays, excluding any
286 * null values. Later values override earlier
287 * values.
288 *
289 * @param array $arrays
290 * List of arrays to combine.
291 * @return array
292 */
293 protected function combine($arrays) {
294 $combined = array();
295 foreach ($arrays as $array) {
296 foreach ($array as $k => $v) {
297 if ($v !== NULL) {
298 $combined[$k] = $v;
299 }
300 }
301 }
302 return $combined;
303 }
304
305 /**
306 * Update the DB record for this setting.
307 *
308 * @param string $name
309 * The simple name of the setting.
310 * @param mixed $value
311 * The new value of the setting.
312 */
313 protected function setDb($name, $value) {
314 if (\CRM_Core_BAO_Setting::isUpgradeFromPreFourOneAlpha1()) {
315 // civicrm_setting table is not going to be present.
316 return;
317 }
318
319 $fields = array();
320 $fieldsToSet = \CRM_Core_BAO_Setting::validateSettingsInput(array($name => $value), $fields);
321 //We haven't traditionally validated inputs to setItem, so this breaks things.
322 //foreach ($fieldsToSet as $settingField => &$settingValue) {
323 // self::validateSetting($settingValue, $fields['values'][$settingField]);
324 //}
325
326 $metadata = $fields['values'][$name];
327
328 $dao = new \CRM_Core_DAO_Setting();
329 $dao->name = $name;
330 $dao->domain_id = $this->domainId;
331 if ($this->contactId) {
332 $dao->contact_id = $this->contactId;
333 $dao->is_domain = 0;
334 }
335 else {
336 $dao->is_domain = 1;
337 }
338 $dao->find(TRUE);
339 $dao->group_name = '';
340
341 if (isset($metadata['on_change'])) {
342 foreach ($metadata['on_change'] as $callback) {
343 call_user_func(
344 \Civi\Core\Resolver::singleton()->get($callback),
345 unserialize($dao->value),
346 $value,
347 $metadata,
348 $this->domainId
349 );
350 }
351 }
352
353 if (\CRM_Utils_System::isNull($value)) {
354 $dao->value = 'null';
355 }
356 else {
357 $dao->value = serialize($value);
358 }
359
360 $dao->created_date = \CRM_Utils_Time::getTime('Ymdhis');
361
362 $session = \CRM_Core_Session::singleton();
363 if (\CRM_Contact_BAO_Contact_Utils::isContactId($session->get('userID'))) {
364 $dao->created_id = $session->get('userID');
365 }
366
367 $dao->save();
368 $dao->free();
369 }
370
371 }