CRM_Extension_System - Make it a little easier to skim.
[civicrm-core.git] / Civi / Core / SettingsBag.php
CommitLineData
3a84c0ab
TO
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
28namespace 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 */
51class 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
9fd040ae
TO
83 protected $filteredValues;
84
3a84c0ab
TO
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 * @param array $defaults
91 * Array(string $settingName => mixed $value).
92 * @param array $mandatory
93 * Array(string $settingName => mixed $value).
94 */
95 public function __construct($domainId, $contactId, $defaults, $mandatory) {
96 $this->domainId = $domainId;
97 $this->contactId = $contactId;
98 $this->defaults = $defaults;
99 $this->mandatory = $mandatory;
100 $this->combined = NULL;
101 }
102
103 /**
104 * Load all settings that apply to this domain or contact.
105 *
106 * @return $this
107 */
108 public function load() {
109 $this->values = array();
110 $dao = $this->createDao();
111 $dao->find();
112 while ($dao->fetch()) {
9fd040ae 113 $this->values[$dao->name] = ($dao->value !== NULL) ? unserialize($dao->value) : NULL;
3a84c0ab
TO
114 }
115 $this->combined = NULL;
116 return $this;
117 }
118
119 /**
120 * Add a batch of settings. Save them.
121 *
122 * @param array $settings
123 * Array(string $settingName => mixed $settingValue).
124 * @return $this
125 */
126 public function add(array $settings) {
127 foreach ($settings as $key => $value) {
128 $this->set($key, $value);
129 }
130 return $this;
131 }
132
133 /**
134 * Get a list of all effective settings.
135 *
136 * @return array
137 * Array(string $settingName => mixed $settingValue).
138 */
139 public function all() {
140 if ($this->combined === NULL) {
141 $this->combined = $this->combine(
142 array($this->defaults, $this->values, $this->mandatory)
143 );
144 }
145 return $this->combined;
146 }
147
148 /**
149 * Determine the effective value.
150 *
151 * @param string $key
152 * @return mixed
153 */
154 public function get($key) {
155 $all = $this->all();
156 return isset($all[$key]) ? $all[$key] : NULL;
157 }
158
9fd040ae
TO
159 /**
160 * Get the value of a setting, formatted as a path.
161 *
162 * @param string $key
163 * @return string|NULL
164 * Absolute path.
165 */
166 public function getPath($key) {
167 if (!isset($this->filteredValues[$key])) {
7a77331e 168 $this->filteredValues[$key] = $this->filterPath($this->get($key));
9fd040ae
TO
169 }
170 return $this->filteredValues[$key];
171 }
172
173 /**
174 * Get the value of a setting, formatted as a URL.
175 *
176 * @param string $key
177 * @param bool $preferFormat
178 * The preferred format ('absolute', 'relative').
179 * The result data may not meet the preference -- if the setting
180 * refers to an external domain, then the result will be
181 * absolute (regardless of preference).
182 * @parma bool|NULL $ssl
183 * NULL to autodetect. TRUE to force to SSL.
184 * @return string|NULL
185 * URL.
186 */
187 public function getUrl($key, $preferFormat, $ssl = NULL) {
188 if (!isset($this->filteredValues[$key][$preferFormat][$ssl])) {
189 $value = $this->filterUrl($this->get($key), $preferFormat, $ssl);
190 $this->filteredValues[$key][$preferFormat][$ssl] = $value;
191 }
192 return $this->filteredValues[$key][$preferFormat][$ssl];
193 }
194
3a84c0ab
TO
195 /**
196 * Determine the explicitly designated value, regardless of
197 * any default or mandatory values.
198 *
199 * @param string $key
200 * @return null
201 */
202 public function getExplicit($key) {
203 return (isset($this->values[$key]) ? $this->values[$key] : NULL);
204 }
205
206 /**
207 * Determine if the entity has explicitly designated a value.
208 *
209 * Note that get() may still return other values based on
210 * mandatory values or defaults.
211 *
212 * @param string $key
213 * @return bool
214 */
215 public function hasExplict($key) {
216 // NULL means no designated value.
217 return isset($this->values[$key]);
218 }
219
220 /**
221 * Removes any explicit settings. This restores the default.
222 *
223 * @param string $key
224 * @return $this
225 */
226 public function revert($key) {
227 // It might be better to DELETE (to avoid long-term leaks),
228 // but setting NULL is simpler for now.
229 return $this->set($key, NULL);
230 }
231
232 /**
233 * Add a single setting. Save it.
234 *
235 * @param string $key
236 * @param mixed $value
237 * @return $this
238 */
239 public function set($key, $value) {
240 $this->setDb($key, $value);
241 $this->values[$key] = $value;
9fd040ae 242 unset($this->filteredValues[$key]);
3a84c0ab
TO
243 $this->combined = NULL;
244 return $this;
245 }
246
9fd040ae
TO
247 /**
248 * @param string $key
249 * @param string $value
250 * Absolute path.
251 * @return $this
252 */
253 public function setPath($key, $value) {
254 $this->set($key, \CRM_Utils_File::relativeDirectory($value));
255 return $this;
256 }
257
258 /**
259 * @param string $key
260 * @param string $value
261 * Absolute URL.
262 * @return $this
263 */
264 public function setUrl($key, $value) {
265 $this->set($key, \CRM_Utils_System::relativeURL($value));
266 return $this;
267 }
268
3a84c0ab
TO
269 /**
270 * @return \CRM_Core_DAO_Setting
271 */
272 protected function createDao() {
273 $dao = new \CRM_Core_DAO_Setting();
274 $dao->domain_id = $this->domainId;
275 if ($this->contactId === NULL) {
276 $dao->is_domain = 1;
277 }
278 else {
279 $dao->contact_id = $this->contactId;
280 $dao->is_domain = 0;
281 }
282 return $dao;
283 }
284
285 /**
286 * Combine a series of arrays, excluding any
287 * null values. Later values override earlier
288 * values.
289 *
290 * @param $arrays
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 * @param $key
307 * @param $value
308 */
309 protected function setDb($name, $value) {
310 $fields = array();
311 $fieldsToSet = \CRM_Core_BAO_Setting::validateSettingsInput(array($name => $value), $fields);
312 //We haven't traditionally validated inputs to setItem, so this breaks things.
313 //foreach ($fieldsToSet as $settingField => &$settingValue) {
314 // self::validateSetting($settingValue, $fields['values'][$settingField]);
315 //}
316 // NOTE: We don't have any notion of createdID
317 \CRM_Core_BAO_Setting::_setItem($fields['values'][$name], $value, '', $name, NULL, $this->contactId, NULL, $this->domainId);
318
319 //$dao = $this->createDao();
320 //$dao->name = $key;
321 //$dao->group_name = '';
322 //$dao->find();
323 //$serializedValue = ($value === NULL ? 'null' : serialize($value));
324 //if ($dao->value !== $serializedValue) {
325 // $dao->created_date = \CRM_Utils_Time::getTime('Ymdhis');
326 // $dao->value = $serializedValue;
327 // $dao->save();
328 //}
329 }
330
9fd040ae
TO
331 /**
332 * Filter a URL, the same way that it would be if it were read from settings.
333 *
334 * @param $value
335 * @param $preferFormat
336 * @param $ssl
337 * @return mixed|string
338 */
339 public function filterUrl($value, $preferFormat, $ssl = NULL) {
340 if ($value) {
341 $value = \CRM_Utils_System::absoluteURL($value, TRUE);
342 }
343 if ($preferFormat === 'relative' && $value) {
344 $parsed = parse_url($value);
345 if (isset($_SERVER['HTTP_HOST']) && isset($parsed['host']) && $_SERVER['HTTP_HOST'] == $parsed['host']) {
346 $value = $parsed['path'];
347 }
348 }
349
350 if ($value) {
351 if ($ssl || ($ssl === NULL && \CRM_Utils_System::isSSL())) {
352 $value = str_replace('http://', 'https://', $value);
9fd040ae 353 }
9fd040ae
TO
354 }
355 return $value;
356 }
357
7a77331e
TO
358 /**
359 * @param string $value
360 * @return bool|string
361 */
362 public function filterPath($value) {
363 if ($value) {
364 return \CRM_Utils_File::absoluteDirectory($value);
365 }
366 else {
367 return FALSE;
368 }
369 }
370
3a84c0ab 371}