Merge pull request #10108 from eileenmcnaughton/localizable
[civicrm-core.git] / CRM / Core / Config.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2017 |
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 /**
29 * Config handles all the run time configuration changes that the system needs to deal with.
30 *
31 * Typically we'll have different values for a user's sandbox, a qa sandbox and a production area.
32 * The default values in general, should reflect production values (minimizes chances of screwing up)
33 *
34 * @package CRM
35 * @copyright CiviCRM LLC (c) 2004-2017
36 */
37
38 require_once 'Log.php';
39 require_once 'Mail.php';
40
41 require_once 'api/api.php';
42
43 /**
44 * Class CRM_Core_Config
45 *
46 * @property CRM_Utils_System_Base $userSystem
47 * @property array $enableComponents
48 * @property array $languageLimit
49 * @property bool $debug
50 * @property bool $doNotResetCache
51 * @property string $maxFileSize
52 * @property string $defaultCurrency
53 * @property string $defaultCurrencySymbol
54 * @property string $lcMessages
55 * @property string $fieldSeparator
56 * @property string $userFramework
57 * @property string $verpSeparator
58 * @property string $dateFormatFull
59 * @property string $resourceBase
60 * @property string $dsn
61 * @property string $customTemplateDir
62 * @property string $defaultContactCountry
63 * @property string $defaultContactStateProvince
64 * @property string $monetaryDecimalPoint
65 * @property string $monetaryThousandSeparator
66 */
67 class CRM_Core_Config extends CRM_Core_Config_MagicMerge {
68
69 /**
70 * The handle to the log that we are using
71 * @var object
72 */
73 private static $_log = NULL;
74
75 /**
76 * We only need one instance of this object. So we use the singleton
77 * pattern and cache the instance in this variable
78 *
79 * @var CRM_Core_Config
80 */
81 private static $_singleton = NULL;
82
83 /**
84 * The constructor. Sets domain id if defined, otherwise assumes
85 * single instance installation.
86 */
87 public function __construct() {
88 parent::__construct();
89 }
90
91 /**
92 * Singleton function used to manage this object.
93 *
94 * @param bool $loadFromDB
95 * whether to load from the database.
96 * @param bool $force
97 * whether to force a reconstruction.
98 *
99 * @return CRM_Core_Config
100 */
101 public static function &singleton($loadFromDB = TRUE, $force = FALSE) {
102 if (self::$_singleton === NULL || $force) {
103 $GLOBALS['civicrm_default_error_scope'] = CRM_Core_TemporaryErrorScope::create(array('CRM_Core_Error', 'handle'));
104 $errorScope = CRM_Core_TemporaryErrorScope::create(array('CRM_Core_Error', 'simpleHandler'));
105
106 if (defined('E_DEPRECATED')) {
107 error_reporting(error_reporting() & ~E_DEPRECATED);
108 }
109
110 self::$_singleton = new CRM_Core_Config();
111 \Civi\Core\Container::boot($loadFromDB);
112 if ($loadFromDB && self::$_singleton->dsn) {
113 $domain = \CRM_Core_BAO_Domain::getDomain();
114 \CRM_Core_BAO_ConfigSetting::applyLocale(\Civi::settings($domain->id), $domain->locales);
115
116 unset($errorScope);
117
118 CRM_Utils_Hook::config(self::$_singleton);
119 self::$_singleton->authenticate();
120
121 // Extreme backward compat: $config binds to active domain at moment of setup.
122 self::$_singleton->getSettings();
123
124 Civi::service('settings_manager')->useDefaults();
125
126 self::$_singleton->handleFirstRun();
127 }
128 }
129 return self::$_singleton;
130 }
131
132 /**
133 * Returns the singleton logger for the application.
134 *
135 * @deprecated
136 * @return object
137 * @see Civi::log()
138 */
139 static public function &getLog() {
140 if (!isset(self::$_log)) {
141 self::$_log = Log::singleton('display');
142 }
143
144 return self::$_log;
145 }
146
147 /**
148 * Retrieve a mailer to send any mail from the application.
149 *
150 * @return Mail
151 * @deprecated
152 * @see Civi::service()
153 */
154 public static function getMailer() {
155 return Civi::service('pear_mail');
156 }
157
158 /**
159 * Deletes the web server writable directories.
160 *
161 * @param int $value
162 * 1: clean templates_c, 2: clean upload, 3: clean both
163 * @param bool $rmdir
164 */
165 public function cleanup($value, $rmdir = TRUE) {
166 $value = (int ) $value;
167
168 if ($value & 1) {
169 // clean templates_c
170 CRM_Utils_File::cleanDir($this->templateCompileDir, $rmdir);
171 CRM_Utils_File::createDir($this->templateCompileDir);
172 }
173 if ($value & 2) {
174 // clean upload dir
175 CRM_Utils_File::cleanDir($this->uploadDir);
176 CRM_Utils_File::createDir($this->uploadDir);
177 }
178
179 // Whether we delete/create or simply preserve directories, we should
180 // certainly make sure the restrictions are enforced.
181 foreach (array(
182 $this->templateCompileDir,
183 $this->uploadDir,
184 $this->configAndLogDir,
185 $this->customFileUploadDir,
186 ) as $dir) {
187 if ($dir && is_dir($dir)) {
188 CRM_Utils_File::restrictAccess($dir);
189 }
190 }
191 }
192
193 /**
194 * Verify that the needed parameters are not null in the config.
195 *
196 * @param CRM_Core_Config $config (reference) the system config object
197 * @param array $required (reference) the parameters that need a value
198 *
199 * @return bool
200 */
201 public static function check(&$config, &$required) {
202 foreach ($required as $name) {
203 if (CRM_Utils_System::isNull($config->$name)) {
204 return FALSE;
205 }
206 }
207 return TRUE;
208 }
209
210 /**
211 * Reset the serialized array and recompute.
212 * use with care
213 */
214 public function reset() {
215 $query = "UPDATE civicrm_domain SET config_backend = null";
216 CRM_Core_DAO::executeQuery($query);
217 }
218
219 /**
220 * This method should initialize auth sources.
221 */
222 public function authenticate() {
223 // make sure session is always initialised
224 $session = CRM_Core_Session::singleton();
225
226 // for logging purposes, pass the userID to the db
227 $userID = $session->get('userID');
228 if ($userID) {
229 CRM_Core_DAO::executeQuery('SET @civicrm_user_id = %1',
230 array(1 => array($userID, 'Integer'))
231 );
232 }
233
234 if ($session->get('userID') && !$session->get('authSrc')) {
235 $session->set('authSrc', CRM_Core_Permission::AUTH_SRC_LOGIN);
236 }
237
238 // checksum source
239 CRM_Contact_BAO_Contact_Permission::initChecksumAuthSrc();
240 }
241
242 /**
243 * One function to get domain ID.
244 *
245 * @param int $domainID
246 * @param bool $reset
247 *
248 * @return int|null
249 */
250 public static function domainID($domainID = NULL, $reset = FALSE) {
251 static $domain;
252 if ($domainID) {
253 $domain = $domainID;
254 }
255 if ($reset || empty($domain)) {
256 $domain = defined('CIVICRM_DOMAIN_ID') ? CIVICRM_DOMAIN_ID : 1;
257 }
258
259 return $domain;
260 }
261
262 /**
263 * Do general cleanup of caches, temp directories and temp tables
264 * CRM-8739
265 *
266 * @param bool $sessionReset
267 */
268 public function cleanupCaches($sessionReset = TRUE) {
269 // cleanup templates_c directory
270 $this->cleanup(1, FALSE);
271
272 // clear all caches
273 self::clearDBCache();
274 CRM_Utils_System::flushCache();
275
276 if ($sessionReset) {
277 $session = CRM_Core_Session::singleton();
278 $session->reset(2);
279 }
280 }
281
282 /**
283 * Do general cleanup of module permissions.
284 */
285 public function cleanupPermissions() {
286 $module_files = CRM_Extension_System::singleton()->getMapper()->getActiveModuleFiles();
287 if ($this->userPermissionClass->isModulePermissionSupported()) {
288 // Can store permissions -- so do it!
289 $this->userPermissionClass->upgradePermissions(
290 CRM_Core_Permission::basicPermissions()
291 );
292 }
293 else {
294 // Cannot store permissions -- warn if any modules require them
295 $modules_with_perms = array();
296 foreach ($module_files as $module_file) {
297 $perms = $this->userPermissionClass->getModulePermissions($module_file['prefix']);
298 if (!empty($perms)) {
299 $modules_with_perms[] = $module_file['prefix'];
300 }
301 }
302 if (!empty($modules_with_perms)) {
303 CRM_Core_Session::setStatus(
304 ts('Some modules define permissions, but the CMS cannot store them: %1', array(1 => implode(', ', $modules_with_perms))),
305 ts('Permission Error'),
306 'error'
307 );
308 }
309 }
310 }
311
312 /**
313 * Flush information about loaded modules.
314 */
315 public function clearModuleList() {
316 CRM_Extension_System::singleton()->getCache()->flush();
317 CRM_Utils_Hook::singleton(TRUE);
318 CRM_Core_PseudoConstant::getModuleExtensions(TRUE);
319 CRM_Core_Module::getAll(TRUE);
320 }
321
322 /**
323 * Clear db cache.
324 */
325 public static function clearDBCache() {
326 $queries = array(
327 'TRUNCATE TABLE civicrm_acl_cache',
328 'TRUNCATE TABLE civicrm_acl_contact_cache',
329 'TRUNCATE TABLE civicrm_cache',
330 'TRUNCATE TABLE civicrm_prevnext_cache',
331 'UPDATE civicrm_group SET cache_date = NULL',
332 'TRUNCATE TABLE civicrm_group_contact_cache',
333 'TRUNCATE TABLE civicrm_menu',
334 'UPDATE civicrm_setting SET value = NULL WHERE name="navigation" AND contact_id IS NOT NULL',
335 'DELETE FROM civicrm_setting WHERE name="modulePaths"', // CRM-10543
336 );
337
338 foreach ($queries as $query) {
339 CRM_Core_DAO::executeQuery($query);
340 }
341
342 // also delete all the import and export temp tables
343 self::clearTempTables();
344 }
345
346 /**
347 * Clear leftover temporary tables.
348 *
349 * This is called on upgrade, during tests and site move, from the cron and via clear caches in the UI.
350 *
351 * Currently the UI clear caches does not pass a time interval - which may need review as it does risk
352 * ripping the tables out from underneath a current action. This was considered but
353 * out-of-scope for CRM-16167
354 *
355 * @param string|bool $timeInterval
356 * Optional time interval for mysql date function.g '2 day'. This can be used to prevent
357 * tables created recently from being deleted.
358 */
359 public static function clearTempTables($timeInterval = FALSE) {
360
361 $dao = new CRM_Core_DAO();
362 $query = "
363 SELECT TABLE_NAME as tableName
364 FROM INFORMATION_SCHEMA.TABLES
365 WHERE TABLE_SCHEMA = %1
366 AND (
367 TABLE_NAME LIKE 'civicrm_import_job_%'
368 OR TABLE_NAME LIKE 'civicrm_export_temp%'
369 OR TABLE_NAME LIKE 'civicrm_task_action_temp%'
370 OR TABLE_NAME LIKE 'civicrm_report_temp%'
371 )
372 ";
373 if ($timeInterval) {
374 $query .= " AND CREATE_TIME < DATE_SUB(NOW(), INTERVAL {$timeInterval})";
375 }
376
377 $tableDAO = CRM_Core_DAO::executeQuery($query, array(1 => array($dao->database(), 'String')));
378 $tables = array();
379 while ($tableDAO->fetch()) {
380 $tables[] = $tableDAO->tableName;
381 }
382 if (!empty($tables)) {
383 $table = implode(',', $tables);
384 // drop leftover temporary tables
385 CRM_Core_DAO::executeQuery("DROP TABLE $table");
386 }
387 }
388
389 /**
390 * Check if running in upgrade mode.
391 *
392 * @param string $path
393 *
394 * @return bool
395 */
396 public static function isUpgradeMode($path = NULL) {
397 if (defined('CIVICRM_UPGRADE_ACTIVE')) {
398 return TRUE;
399 }
400
401 if (!$path) {
402 // note: do not re-initialize config here, since this function is part of
403 // config initialization itself
404 $urlVar = 'q';
405 if (defined('CIVICRM_UF') && CIVICRM_UF == 'Joomla') {
406 $urlVar = 'task';
407 }
408
409 $path = CRM_Utils_Array::value($urlVar, $_GET);
410 }
411
412 if ($path && preg_match('/^civicrm\/upgrade(\/.*)?$/', $path)) {
413 return TRUE;
414 }
415
416 if ($path && preg_match('/^civicrm\/ajax\/l10n-js/', $path)
417 && !empty($_SERVER['HTTP_REFERER'])
418 ) {
419 $ref = parse_url($_SERVER['HTTP_REFERER']);
420 if (
421 (!empty($ref['path']) && preg_match('/civicrm\/upgrade/', $ref['path'])) ||
422 (!empty($ref['query']) && preg_match('/civicrm\/upgrade/', urldecode($ref['query'])))
423 ) {
424 return TRUE;
425 }
426 }
427
428 return FALSE;
429 }
430
431 /**
432 * Is back office credit card processing enabled for this site - ie are there any installed processors that support
433 * it?
434 * This function is used for determining whether to show the submit credit card link, not for determining which processors to show, hence
435 * it is a config var
436 * @return bool
437 */
438 public static function isEnabledBackOfficeCreditCardPayments() {
439 return CRM_Financial_BAO_PaymentProcessor::hasPaymentProcessorSupporting(array('BackOffice'));
440 }
441
442 /**
443 * @deprecated
444 */
445 public function addressSequence() {
446 return CRM_Utils_Address::sequence(Civi::settings()->get('address_format'));
447 }
448
449 /**
450 * @deprecated
451 */
452 public function defaultContactCountry() {
453 return CRM_Core_BAO_Country::defaultContactCountry();
454 }
455
456 /**
457 * @deprecated
458 */
459 public function defaultContactCountryName() {
460 return CRM_Core_BAO_Country::defaultContactCountryName();
461 }
462
463 /**
464 * @deprecated
465 *
466 * @param string $defaultCurrency
467 *
468 * @return string
469 */
470 public function defaultCurrencySymbol($defaultCurrency = NULL) {
471 return CRM_Core_BAO_Country::defaultCurrencySymbol($defaultCurrency);
472 }
473
474 /**
475 * Resets the singleton, so that the next call to CRM_Core_Config::singleton()
476 * reloads completely.
477 *
478 * While normally we could call the singleton function with $force = TRUE,
479 * this function addresses a very specific use-case in the CiviCRM installer,
480 * where we cannot yet force a reload, but we want to make sure that the next
481 * call to this object gets a fresh start (ex: to initialize the DAO).
482 */
483 public function free() {
484 self::$_singleton = NULL;
485 }
486
487 /**
488 * Conditionally fire an event during the first page run.
489 *
490 * The install system is currently implemented several times, so it's hard to add
491 * new installation logic. We use a poor-man's method to detect the first run.
492 *
493 * Situations to test:
494 * - New installation
495 * - Upgrade from an old version (predating first-run tracker)
496 * - Upgrade from an old version (with first-run tracking)
497 */
498 public function handleFirstRun() {
499 // Ordinarily, we prefetch settings en masse and find that the system is already installed.
500 // No extra SQL queries required.
501 if (Civi::settings()->get('installed')) {
502 return;
503 }
504
505 // Q: How should this behave during testing?
506 if (defined('CIVICRM_TEST')) {
507 return;
508 }
509
510 // If schema hasn't been loaded yet, then do nothing. Don't want to interfere
511 // with the existing installers. NOTE: If we change the installer pageflow,
512 // then we may want to modify this behavior.
513 if (!CRM_Core_DAO::checkTableExists('civicrm_domain')) {
514 return;
515 }
516
517 // If we're handling an upgrade, then the system has already been used, so this
518 // is not the first run.
519 if (CRM_Core_Config::isUpgradeMode()) {
520 return;
521 }
522 $dao = CRM_Core_DAO::executeQuery('SELECT version FROM civicrm_domain');
523 while ($dao->fetch()) {
524 if ($dao->version && version_compare($dao->version, CRM_Utils_System::version(), '<')) {
525 return;
526 }
527 }
528
529 // The installation flag is stored in civicrm_setting, which is domain-aware. The
530 // flag could have been stored under a different domain.
531 $dao = CRM_Core_DAO::executeQuery('
532 SELECT domain_id, value FROM civicrm_setting
533 WHERE is_domain = 1 AND name = "installed"
534 ');
535 while ($dao->fetch()) {
536 $value = unserialize($dao->value);
537 if (!empty($value)) {
538 Civi::settings()->set('installed', 1);
539 return;
540 }
541 }
542
543 // OK, this looks new.
544 Civi::service('dispatcher')->dispatch(\Civi\Core\Event\SystemInstallEvent::EVENT_NAME, new \Civi\Core\Event\SystemInstallEvent());
545 Civi::settings()->set('installed', 1);
546 }
547
548 }