CRM-12674
[civicrm-core.git] / CRM / Core / BAO / ConfigSetting.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
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 *
30 *
31 * @package CRM
32 * @copyright CiviCRM LLC (c) 2004-2013
33 * $Id$
34 *
35 */
36
37 /**
38 * file contains functions used in civicrm configuration
39 *
40 */
41 class CRM_Core_BAO_ConfigSetting {
42
43 /**
44 * Function to create civicrm settings. This is the same as add but it clears the cache and
45 * reloads the config object
46 *
47 * @params array $params associated array of civicrm variables
48 *
49 * @return null
50 * @static
51 */
52 static function create($params) {
53 self::add($params);
54 $cache = CRM_Utils_Cache::singleton();
55 $cache->delete('CRM_Core_Config');
56 $cache->delete('CRM_Core_Config' . CRM_Core_Config::domainID());
57 $config = CRM_Core_Config::singleton(TRUE, TRUE);
58 }
59
60 /**
61 * Function to add civicrm settings
62 *
63 * @params array $params associated array of civicrm variables
64 *
65 * @return null
66 * @static
67 */
68 static function add(&$params) {
69 self::fixParams($params);
70
71 // also set a template url so js files can use this
72 // CRM-6194
73 $params['civiRelativeURL'] = CRM_Utils_System::url('CIVI_BASE_TEMPLATE');
74 $params['civiRelativeURL'] =
75 str_replace(
76 'CIVI_BASE_TEMPLATE',
77 '',
78 $params['civiRelativeURL']
79 );
80
81 // also add the version number for use by template / js etc
82 $params['civiVersion'] = CRM_Utils_System::version();
83
84 $domain = new CRM_Core_DAO_Domain();
85 $domain->id = CRM_Core_Config::domainID();
86 $domain->find(TRUE);
87 if ($domain->config_backend) {
88 $values = unserialize($domain->config_backend);
89 self::formatParams($params, $values);
90 }
91
92 // CRM-6151
93 if (isset($params['localeCustomStrings']) &&
94 is_array($params['localeCustomStrings'])
95 ) {
96 $domain->locale_custom_strings = serialize($params['localeCustomStrings']);
97 }
98
99 // unset any of the variables we read from file that should not be stored in the database
100 // the username and certpath are stored flat with _test and _live
101 // check CRM-1470
102 $skipVars = self::skipVars();
103 foreach ($skipVars as $var) {
104 unset($params[$var]);
105 }
106
107 CRM_Core_BAO_Setting::fixAndStoreDirAndURL($params);
108
109 // also skip all Dir Params, we dont need to store those in the DB!
110 foreach ($params as $name => $val) {
111 if (substr($name, -3) == 'Dir') {
112 unset($params[$name]);
113 }
114 }
115
116 //keep user preferred language upto date, CRM-7746
117 $session = CRM_Core_Session::singleton();
118 $lcMessages = CRM_Utils_Array::value('lcMessages', $params);
119 if ($lcMessages && $session->get('userID')) {
120 $languageLimit = CRM_Utils_Array::value('languageLimit', $params);
121 if (is_array($languageLimit) &&
122 !in_array($lcMessages, array_keys($languageLimit))
123 ) {
124 $lcMessages = $session->get('lcMessages');
125 }
126
127 $ufm = new CRM_Core_DAO_UFMatch();
128 $ufm->contact_id = $session->get('userID');
129 if ($lcMessages && $ufm->find(TRUE)) {
130 $ufm->language = $lcMessages;
131 $ufm->save();
132 $session->set('lcMessages', $lcMessages);
133 $params['lcMessages'] = $lcMessages;
134 }
135 }
136
137 $domain->config_backend = serialize($params);
138 $domain->save();
139 }
140
141 /**
142 * Function to fix civicrm setting variables
143 *
144 * @params array $params associated array of civicrm variables
145 *
146 * @return null
147 * @static
148 */
149 static function fixParams(&$params) {
150 // in our old civicrm.settings.php we were using ISO code for country and
151 // province limit, now we have changed it to use ids
152
153 $countryIsoCodes = CRM_Core_PseudoConstant::countryIsoCode();
154
155 $specialArray = array('countryLimit', 'provinceLimit');
156
157 foreach ($params as $key => $value) {
158 if (in_array($key, $specialArray) && is_array($value)) {
159 foreach ($value as $k => $val) {
160 if (!is_numeric($val)) {
161 $params[$key][$k] = array_search($val, $countryIsoCodes);
162 }
163 }
164 }
165 elseif ($key == 'defaultContactCountry') {
166 if (!is_numeric($value)) {
167 $params[$key] = array_search($value, $countryIsoCodes);
168 }
169 }
170 }
171 }
172
173 /**
174 * Function to format the array containing before inserting in db
175 *
176 * @param array $params associated array of civicrm variables(submitted)
177 * @param array $values associated array of civicrm variables stored in db
178 *
179 * @return null
180 * @static
181 */
182 static function formatParams(&$params, &$values) {
183 if (empty($params) ||
184 !is_array($params)
185 ) {
186 $params = $values;
187 }
188 else {
189 foreach ($params as $key => $val) {
190 if (array_key_exists($key, $values)) {
191 unset($values[$key]);
192 }
193 }
194 $params = array_merge($params, $values);
195 }
196 }
197
198 /**
199 * Function to retrieve the settings values from db
200 *
201 * @return array $defaults
202 * @static
203 */
204 static function retrieve(&$defaults) {
205 $domain = new CRM_Core_DAO_Domain();
206
207 //we are initializing config, really can't use, CRM-7863
208 $urlVar = 'q';
209 if (defined('CIVICRM_UF') && CIVICRM_UF == 'Joomla') {
210 $urlVar = 'task';
211 }
212
213 if (CRM_Core_Config::isUpgradeMode()) {
214 $domain->selectAdd('config_backend');
215 }
216 elseif (CRM_Utils_Array::value($urlVar, $_GET) == 'admin/modules/list/confirm') {
217 $domain->selectAdd('config_backend', 'locales');
218 }
219 else {
220 $domain->selectAdd('config_backend, locales, locale_custom_strings');
221 }
222
223 $domain->id = CRM_Core_Config::domainID();
224 $domain->find(TRUE);
225 if ($domain->config_backend) {
226 $defaults = unserialize($domain->config_backend);
227 if ($defaults === FALSE || !is_array($defaults)) {
228 $defaults = array();
229 return;
230 }
231
232 $skipVars = self::skipVars();
233 foreach ($skipVars as $skip) {
234 if (array_key_exists($skip, $defaults)) {
235 unset($defaults[$skip]);
236 }
237 }
238
239 // since language field won't be present before upgrade.
240 if (CRM_Core_Config::isUpgradeMode()) {
241 // dont add if its empty
242 if (!empty($defaults)) {
243 // retrieve directory and url preferences also
244 CRM_Core_BAO_Setting::retrieveDirectoryAndURLPreferences($defaults);
245 }
246 return;
247 }
248
249 // check if there are any locale strings
250 if ($domain->locale_custom_strings) {
251 $defaults['localeCustomStrings'] = unserialize($domain->locale_custom_strings);
252 }
253 else {
254 $defaults['localeCustomStrings'] = NULL;
255 }
256
257 // are we in a multi-language setup?
258 $multiLang = $domain->locales ? TRUE : FALSE;
259
260 // set the current language
261 $lcMessages = NULL;
262
263 $session = CRM_Core_Session::singleton();
264
265 // on multi-lang sites based on request and civicrm_uf_match
266 if ($multiLang) {
267 $lcMessagesRequest = CRM_Utils_Request::retrieve('lcMessages', 'String', $this);
268 $languageLimit = array();
269 if (array_key_exists('languageLimit', $defaults) && is_array($defaults['languageLimit'])) {
270 $languageLimit = $defaults['languageLimit'];
271 }
272
273 if (in_array($lcMessagesRequest, array_keys($languageLimit))) {
274 $lcMessages = $lcMessagesRequest;
275
276 //CRM-8559, cache navigation do not respect locale if it is changed, so reseting cache.
277 CRM_Core_BAO_Cache::deleteGroup('navigation');
278 }
279 else {
280 $lcMessagesRequest = NULL;
281 }
282
283 if (!$lcMessagesRequest) {
284 $lcMessagesSession = $session->get('lcMessages');
285 if (in_array($lcMessagesSession, array_keys($languageLimit))) {
286 $lcMessages = $lcMessagesSession;
287 }
288 else {
289 $lcMessagesSession = NULL;
290 }
291 }
292
293 if ($lcMessagesRequest) {
294 $ufm = new CRM_Core_DAO_UFMatch();
295 $ufm->contact_id = $session->get('userID');
296 if ($ufm->find(TRUE)) {
297 $ufm->language = $lcMessages;
298 $ufm->save();
299 }
300 $session->set('lcMessages', $lcMessages);
301 }
302
303 if (!$lcMessages and $session->get('userID')) {
304 $ufm = new CRM_Core_DAO_UFMatch();
305 $ufm->contact_id = $session->get('userID');
306 if ($ufm->find(TRUE) &&
307 in_array($ufm->language, array_keys($languageLimit))
308 ) {
309 $lcMessages = $ufm->language;
310 }
311 $session->set('lcMessages', $lcMessages);
312 }
313 }
314 global $dbLocale;
315
316 // try to inherit the language from the hosting CMS
317 if (CRM_Utils_Array::value('inheritLocale', $defaults)) {
318 // FIXME: On multilanguage installs, CRM_Utils_System::getUFLocale() in many cases returns nothing if $dbLocale is not set
319 $dbLocale = $multiLang ? "_{$defaults['lcMessages']}" : '';
320 $lcMessages = CRM_Utils_System::getUFLocale();
321 if ($domain->locales and !in_array($lcMessages, explode(CRM_Core_DAO::VALUE_SEPARATOR,
322 $domain->locales
323 ))) {
324 $lcMessages = NULL;
325 }
326 }
327
328 if ($lcMessages) {
329 // update config lcMessages - CRM-5027 fixed.
330 $defaults['lcMessages'] = $lcMessages;
331 }
332 else {
333 // if a single-lang site or the above didn't yield a result, use default
334 $lcMessages = CRM_Utils_Array::value( 'lcMessages', $defaults );
335 }
336
337 // set suffix for table names - use views if more than one language
338 $dbLocale = $multiLang ? "_{$lcMessages}" : '';
339
340 // FIXME: an ugly hack to fix CRM-4041
341 global $tsLocale;
342 $tsLocale = $lcMessages;
343
344 // FIXME: as bad aplace as any to fix CRM-5428
345 // (to be moved to a sane location along with the above)
346 if (function_exists('mb_internal_encoding')) {
347 mb_internal_encoding('UTF-8');
348 }
349 }
350
351 // dont add if its empty
352 if (!empty($defaults)) {
353 // retrieve directory and url preferences also
354 CRM_Core_BAO_Setting::retrieveDirectoryAndURLPreferences($defaults);
355 }
356 }
357
358 static function getConfigSettings() {
359 $config = CRM_Core_Config::singleton();
360
361 $url = $dir = $siteName = $siteRoot = NULL;
362 if ($config->userFramework == 'Joomla') {
363 $url = preg_replace(
364 '|administrator/components/com_civicrm/civicrm/|',
365 '',
366 $config->userFrameworkResourceURL
367 );
368
369 // lets use imageUploadDir since we dont mess around with its values
370 // in the config object, lets kep it a bit generic since folks
371 // might have different values etc
372 $dir = preg_replace(
373 '|civicrm/templates_c/.*$|',
374 '',
375 $config->templateCompileDir
376 );
377 $siteRoot = preg_replace(
378 '|/media/civicrm/.*$|',
379 '',
380 $config->imageUploadDir
381 );
382 }
383 else if ($config->userFramework == 'WordPress') {
384 $url = preg_replace(
385 '|wp-content/plugins/civicrm/civicrm/|',
386 '',
387 $config->userFrameworkResourceURL
388 );
389
390 // lets use imageUploadDir since we dont mess around with its values
391 // in the config object, lets kep it a bit generic since folks
392 // might have different values etc
393 $dir = preg_replace(
394 '|civicrm/templates_c/.*$|',
395 '',
396 $config->templateCompileDir
397 );
398 $siteRoot = preg_replace(
399 '|/wp-content/plugins/files/civicrm/.*$|',
400 '',
401 $config->imageUploadDir
402 );
403 }
404 else {
405 $url = preg_replace(
406 '|sites/[\w\.\-\_]+/modules/civicrm/|',
407 '',
408 $config->userFrameworkResourceURL
409 );
410
411 // lets use imageUploadDir since we dont mess around with its values
412 // in the config object, lets kep it a bit generic since folks
413 // might have different values etc
414 $dir = preg_replace(
415 '|/files/civicrm/.*$|',
416 '/files/',
417 $config->imageUploadDir
418 );
419
420 $matches = array();
421 if (preg_match(
422 '|/sites/([\w\.\-\_]+)/|',
423 $config->imageUploadDir,
424 $matches
425 )) {
426 $siteName = $matches[1];
427 if ($siteName) {
428 $siteName = "/sites/$siteName/";
429 $siteNamePos = strpos($dir, $siteName);
430 if ($siteNamePos !== FALSE) {
431 $siteRoot = substr($dir, 0, $siteNamePos);
432 }
433 }
434 }
435 }
436
437
438 return array($url, $dir, $siteName, $siteRoot);
439 }
440
441 static function getBestGuessSettings() {
442 $config = CRM_Core_Config::singleton();
443
444 $url = $config->userFrameworkBaseURL;
445 $siteName = $siteRoot = NULL;
446 if ($config->userFramework == 'Joomla') {
447 $url = preg_replace(
448 '|/administrator|',
449 '',
450 $config->userFrameworkBaseURL
451 );
452 $siteRoot = preg_replace(
453 '|/media/civicrm/.*$|',
454 '',
455 $config->imageUploadDir
456 );
457 }
458
459 $dir = preg_replace(
460 '|civicrm/templates_c/.*$|',
461 '',
462 $config->templateCompileDir
463 );
464
465 if ($config->userFramework->is_drupal) {
466 $matches = array();
467 if (preg_match(
468 '|/sites/([\w\.\-\_]+)/|',
469 $config->templateCompileDir,
470 $matches
471 )) {
472 $siteName = $matches[1];
473 if ($siteName) {
474 $siteName = "/sites/$siteName/";
475 $siteNamePos = strpos($dir, $siteName);
476 if ($siteNamePos !== FALSE) {
477 $siteRoot = substr($dir, 0, $siteNamePos);
478 }
479 }
480 }
481 }
482
483 return array($url, $dir, $siteName, $siteRoot);
484 }
485
486 static function doSiteMove($defaultValues = array() ) {
487 $moveStatus = ts('Beginning site move process...') . '<br />';
488 // get the current and guessed values
489 list($oldURL, $oldDir, $oldSiteName, $oldSiteRoot) = self::getConfigSettings();
490 list($newURL, $newDir, $newSiteName, $newSiteRoot) = self::getBestGuessSettings();
491
492
493 // retrieve these values from the argument list
494 $variables = array('URL', 'Dir', 'SiteName', 'SiteRoot', 'Val_1', 'Val_2', 'Val_3');
495 $states = array('old', 'new');
496 foreach ($variables as $varSuffix) {
497 foreach ($states as $state) {
498 $var = "{$state}{$varSuffix}";
499 if (!isset($$var)) {
500 if (isset($defaultValues[$var])) {
501 $$var = $defaultValues[$var];
502 }
503 else {
504 $$var = NULL;
505 }
506 }
507 $$var = CRM_Utils_Request::retrieve($var,
508 'String',
509 CRM_Core_DAO::$_nullArray,
510 FALSE,
511 $$var,
512 'REQUEST'
513 );
514 }
515 }
516
517 $from = $to = array();
518 foreach ($variables as $varSuffix) {
519 $oldVar = "old{$varSuffix}";
520 $newVar = "new{$varSuffix}";
521 //skip it if either is empty or both are exactly the same
522 if ($$oldVar &&
523 $$newVar &&
524 $$oldVar != $$newVar
525 ) {
526 $from[] = $$oldVar;
527 $to[] = $$newVar;
528 }
529 }
530
531 $sql = "
532 SELECT config_backend
533 FROM civicrm_domain
534 WHERE id = %1
535 ";
536 $params = array(1 => array(CRM_Core_Config::domainID(), 'Integer'));
537 $configBackend = CRM_Core_DAO::singleValueQuery($sql, $params);
538 if (!$configBackend) {
539 CRM_Core_Error::fatal(ts('Returning early due to unexpected error - civicrm_domain.config_backend column value is NULL. Try visiting CiviCRM Home page.'));
540 }
541 $configBackend = unserialize($configBackend);
542
543 $configBackend = str_replace($from,
544 $to,
545 $configBackend
546 );
547
548 $configBackend = serialize($configBackend);
549 $sql = "
550 UPDATE civicrm_domain
551 SET config_backend = %2
552 WHERE id = %1
553 ";
554 $params[2] = array($configBackend, 'String');
555 CRM_Core_DAO::executeQuery($sql, $params);
556
557 // Apply the changes to civicrm_option_values
558 $optionGroups = array('url_preferences', 'directory_preferences');
559 foreach ($optionGroups as $option) {
560 foreach ($variables as $varSuffix) {
561 $oldVar = "old{$varSuffix}";
562 $newVar = "new{$varSuffix}";
563
564 $from = $$oldVar;
565 $to = $$newVar;
566
567 if ($from && $to && $from != $to) {
568 $sql = '
569 UPDATE civicrm_option_value
570 SET value = REPLACE(value, %1, %2)
571 WHERE option_group_id = (
572 SELECT id
573 FROM civicrm_option_group
574 WHERE name = %3 )
575 ';
576 $params = array(1 => array($from, 'String'),
577 2 => array($to, 'String'),
578 3 => array($option, 'String'),
579 );
580 CRM_Core_DAO::executeQuery($sql, $params);
581 }
582 }
583 }
584
585 $moveStatus .= ts('Directory and Resource URLs have been updated in the moved database to reflect current site location.') . '<br />';
586
587 $config = CRM_Core_Config::singleton();
588
589 // clear the template_c and upload directory also
590 $config->cleanup(3, TRUE);
591 $moveStatus .= ts('Template cache and upload directory have been cleared.') . '<br />';
592
593 // clear all caches
594 CRM_Core_Config::clearDBCache();
595 $moveStatus .= ts('Database cache tables cleared.') . '<br />';
596
597 $resetSessionTable = CRM_Utils_Request::retrieve('resetSessionTable',
598 'Boolean',
599 CRM_Core_DAO::$_nullArray,
600 FALSE,
601 FALSE,
602 'REQUEST'
603 );
604 if ($config->userSystem->is_drupal &&
605 $resetSessionTable
606 ) {
607 db_query("DELETE FROM {sessions} WHERE 1");
608 $moveStatus .= ts('Drupal session table cleared.') . '<br />';
609 }
610 else {
611 $session = CRM_Core_Session::singleton();
612 $session->reset(2);
613 $moveStatus .= ts('Session has been reset.') . '<br />';
614 }
615
616 return $moveStatus;
617 }
618
619 /**
620 * takes a componentName and enables it in the config
621 * Primarily used during unit testing
622 *
623 * @param string $componentName name of the component to be enabled, needs to be valid
624 *
625 * @return boolean - true if valid component name and enabling succeeds, else false
626 * @static
627 */
628 static function enableComponent($componentName) {
629 $config = CRM_Core_Config::singleton();
630 if (in_array($componentName, $config->enableComponents)) {
631 // component is already enabled
632 return TRUE;
633 }
634 $components = CRM_Core_Component::getComponents();
635
636 // return if component does not exist
637 if (!array_key_exists($componentName, $components)) {
638 return FALSE;
639 }
640
641 // get config_backend value
642 $sql = "
643 SELECT config_backend
644 FROM civicrm_domain
645 WHERE id = %1
646 ";
647 $params = array(1 => array(CRM_Core_Config::domainID(), 'Integer'));
648 $configBackend = CRM_Core_DAO::singleValueQuery($sql, $params);
649
650 if (!$configBackend) {
651 static $alreadyVisited = FALSE;
652 if ($alreadyVisited) {
653 CRM_Core_Error::fatal(ts('Returning early due to unexpected error - civicrm_domain.config_backend column value is NULL. Try visiting CiviCRM Home page.'));
654 }
655
656 $alreadyVisited = TRUE;
657
658 // try to recreate the config backend
659 $config = CRM_Core_Config::singleton(TRUE, TRUE);
660 return self::enableComponent($componentName);
661 }
662 $configBackend = unserialize($configBackend);
663
664 $configBackend['enableComponents'][] = $componentName;
665 $configBackend['enableComponentIDs'][] = $components[$componentName]->componentID;
666
667 // fix the config object
668 $config->enableComponents = $configBackend['enableComponents'];
669 $config->enableComponentIDs = $configBackend['enableComponentIDs'];
670
671 // also force reset of component array
672 CRM_Core_Component::getEnabledComponents(TRUE);
673
674 // check if component is already there, is so return
675 $configBackend = serialize($configBackend);
676 $sql = "
677 UPDATE civicrm_domain
678 SET config_backend = %2
679 WHERE id = %1
680 ";
681 $params[2] = array($configBackend, 'String');
682 CRM_Core_DAO::executeQuery($sql, $params);
683
684 return TRUE;
685 }
686
687 static function skipVars() {
688 return array(
689 'dsn', 'templateCompileDir',
690 'userFrameworkDSN',
691 'userFramework',
692 'userFrameworkBaseURL', 'userFrameworkClass', 'userHookClass',
693 'userPermissionClass', 'userFrameworkURLVar', 'userFrameworkVersion',
694 'newBaseURL', 'newBaseDir', 'newSiteName', 'configAndLogDir',
695 'qfKey', 'gettextResourceDir', 'cleanURL',
696 'locale_custom_strings', 'localeCustomStrings',
697 'autocompleteContactSearch',
698 'autocompleteContactReference',
699 'checksumTimeout',
700 );
701 }
702 }
703