Merge pull request #4087 from robinmitra/master
[civicrm-core.git] / CRM / Core / BAO / ConfigSetting.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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-2014
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 * Create civicrm settings. This is the same as add but it clears the cache and
45 * reloads the config object
46 *
47 * @param 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 * Add civicrm settings
62 *
63 * @param 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 * Fix civicrm setting variables
143 *
144 * @param 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 * 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 * Retrieve the settings values from db
200 *
201 * @param $defaults
202 *
203 * @return array $defaults
204 * @static
205 */
206 static function retrieve(&$defaults) {
207 $domain = new CRM_Core_DAO_Domain();
208
209 //we are initializing config, really can't use, CRM-7863
210 $urlVar = 'q';
211 if (defined('CIVICRM_UF') && CIVICRM_UF == 'Joomla') {
212 $urlVar = 'task';
213 }
214
215 if (CRM_Core_Config::isUpgradeMode()) {
216 $domain->selectAdd('config_backend');
217 }
218 elseif (CRM_Utils_Array::value($urlVar, $_GET) == 'admin/modules/list/confirm') {
219 $domain->selectAdd('config_backend', 'locales');
220 }
221 else {
222 $domain->selectAdd('config_backend, locales, locale_custom_strings');
223 }
224
225 $domain->id = CRM_Core_Config::domainID();
226 $domain->find(TRUE);
227 if ($domain->config_backend) {
228 $defaults = unserialize($domain->config_backend);
229 if ($defaults === FALSE || !is_array($defaults)) {
230 $defaults = array();
231 return;
232 }
233
234 $skipVars = self::skipVars();
235 foreach ($skipVars as $skip) {
236 if (array_key_exists($skip, $defaults)) {
237 unset($defaults[$skip]);
238 }
239 }
240
241 // check if there are any locale strings
242 if ($domain->locale_custom_strings) {
243 $defaults['localeCustomStrings'] = unserialize($domain->locale_custom_strings);
244 }
245 else {
246 $defaults['localeCustomStrings'] = NULL;
247 }
248
249 // are we in a multi-language setup?
250 $multiLang = $domain->locales ? TRUE : FALSE;
251
252 // set the current language
253 $lcMessages = NULL;
254
255 $session = CRM_Core_Session::singleton();
256
257 // on multi-lang sites based on request and civicrm_uf_match
258 if ($multiLang) {
259 $lcMessagesRequest = CRM_Utils_Request::retrieve('lcMessages', 'String', $this);
260 $languageLimit = array();
261 if (array_key_exists('languageLimit', $defaults) && is_array($defaults['languageLimit'])) {
262 $languageLimit = $defaults['languageLimit'];
263 }
264
265 if (in_array($lcMessagesRequest, array_keys($languageLimit))) {
266 $lcMessages = $lcMessagesRequest;
267
268 //CRM-8559, cache navigation do not respect locale if it is changed, so reseting cache.
269 CRM_Core_BAO_Cache::deleteGroup('navigation');
270 }
271 else {
272 $lcMessagesRequest = NULL;
273 }
274
275 if (!$lcMessagesRequest) {
276 $lcMessagesSession = $session->get('lcMessages');
277 if (in_array($lcMessagesSession, array_keys($languageLimit))) {
278 $lcMessages = $lcMessagesSession;
279 }
280 else {
281 $lcMessagesSession = NULL;
282 }
283 }
284
285 if ($lcMessagesRequest) {
286 $ufm = new CRM_Core_DAO_UFMatch();
287 $ufm->contact_id = $session->get('userID');
288 if ($ufm->find(TRUE)) {
289 $ufm->language = $lcMessages;
290 $ufm->save();
291 }
292 $session->set('lcMessages', $lcMessages);
293 }
294
295 if (!$lcMessages and $session->get('userID')) {
296 $ufm = new CRM_Core_DAO_UFMatch();
297 $ufm->contact_id = $session->get('userID');
298 if ($ufm->find(TRUE) &&
299 in_array($ufm->language, array_keys($languageLimit))
300 ) {
301 $lcMessages = $ufm->language;
302 }
303 $session->set('lcMessages', $lcMessages);
304 }
305 }
306 global $dbLocale;
307
308 // try to inherit the language from the hosting CMS
309 if (!empty($defaults['inheritLocale'])) {
310 // FIXME: On multilanguage installs, CRM_Utils_System::getUFLocale() in many cases returns nothing if $dbLocale is not set
311 $dbLocale = $multiLang ? "_{$defaults['lcMessages']}" : '';
312 $lcMessages = CRM_Utils_System::getUFLocale();
313 if ($domain->locales and !in_array($lcMessages, explode(CRM_Core_DAO::VALUE_SEPARATOR,
314 $domain->locales
315 ))
316 ) {
317 $lcMessages = NULL;
318 }
319 }
320
321 if ($lcMessages) {
322 // update config lcMessages - CRM-5027 fixed.
323 $defaults['lcMessages'] = $lcMessages;
324 }
325 else {
326 // if a single-lang site or the above didn't yield a result, use default
327 $lcMessages = CRM_Utils_Array::value('lcMessages', $defaults);
328 }
329
330 // set suffix for table names - use views if more than one language
331 $dbLocale = $multiLang ? "_{$lcMessages}" : '';
332
333 // FIXME: an ugly hack to fix CRM-4041
334 global $tsLocale;
335 $tsLocale = $lcMessages;
336
337 // FIXME: as bad aplace as any to fix CRM-5428
338 // (to be moved to a sane location along with the above)
339 if (function_exists('mb_internal_encoding')) {
340 mb_internal_encoding('UTF-8');
341 }
342 }
343
344 // dont add if its empty
345 if (!empty($defaults)) {
346 // retrieve directory and url preferences also
347 CRM_Core_BAO_Setting::retrieveDirectoryAndURLPreferences($defaults);
348
349 // Pickup enabled-components from settings table if found.
350 $enableComponents = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'enable_components', NULL, array());
351 if (!empty($enableComponents)) {
352 $defaults['enableComponents'] = $enableComponents;
353
354 $components = CRM_Core_Component::getComponents();
355 $enabledComponentIDs = array();
356 foreach ($defaults['enableComponents'] as $name) {
357 $enabledComponentIDs[] = $components[$name]->componentID;
358 }
359 $defaults['enableComponentIDs'] = $enabledComponentIDs;
360 }
361 }
362 }
363
364 /**
365 * @return array
366 */
367 static function getConfigSettings() {
368 $config = CRM_Core_Config::singleton();
369
370 $url = $dir = $siteName = $siteRoot = NULL;
371 if ($config->userFramework == 'Joomla') {
372 $url = preg_replace(
373 '|administrator/components/com_civicrm/civicrm/|',
374 '',
375 $config->userFrameworkResourceURL
376 );
377
378 // lets use imageUploadDir since we dont mess around with its values
379 // in the config object, lets kep it a bit generic since folks
380 // might have different values etc
381
382 //CRM-15365 - Fix preg_replace to handle backslash for Windows File Paths
383 if (DIRECTORY_SEPARATOR == '\\') {
384 $dir = preg_replace(
385 '|civicrm[/\\\\]templates_c[/\\\\].*$|',
386 '',
387 $config->templateCompileDir
388 );
389 }
390 else {
391 $dir = preg_replace(
392 '|civicrm/templates_c/.*$|',
393 '',
394 $config->templateCompileDir
395 );
396 }
397
398 $siteRoot = preg_replace(
399 '|/media/civicrm/.*$|',
400 '',
401 $config->imageUploadDir
402 );
403 }
404 else if ($config->userFramework == 'WordPress') {
405 $url = preg_replace(
406 '|wp-content/plugins/civicrm/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
415 //CRM-15365 - Fix preg_replace to handle backslash for Windows File Paths
416 if (DIRECTORY_SEPARATOR == '\\') {
417 $dir = preg_replace(
418 '|civicrm[/\\\\]templates_c[/\\\\].*$|',
419 '',
420 $config->templateCompileDir
421 );
422 }
423 else {
424 $dir = preg_replace(
425 '|civicrm/templates_c/.*$|',
426 '',
427 $config->templateCompileDir
428 );
429 }
430
431 $siteRoot = preg_replace(
432 '|/wp-content/plugins/files/civicrm/.*$|',
433 '',
434 $config->imageUploadDir
435 );
436 }
437 else {
438 $url = preg_replace(
439 '|sites/[\w\.\-\_]+/modules/civicrm/|',
440 '',
441 $config->userFrameworkResourceURL
442 );
443
444 // lets use imageUploadDir since we dont mess around with its values
445 // in the config object, lets kep it a bit generic since folks
446 // might have different values etc
447
448 //CRM-15365 - Fix preg_replace to handle backslash for Windows File Paths
449 if (DIRECTORY_SEPARATOR == '\\') {
450 $dir = preg_replace(
451 '|[/\\\\]files[/\\\\]civicrm[/\\\\].*$|',
452 '\\\\files\\\\',
453 $config->imageUploadDir
454 );
455 }
456 else {
457 $dir = preg_replace(
458 '|/files/civicrm/.*$|',
459 '/files/',
460 $config->imageUploadDir
461 );
462 }
463
464 $matches = array();
465 if (preg_match(
466 '|/sites/([\w\.\-\_]+)/|',
467 $config->imageUploadDir,
468 $matches
469 )) {
470 $siteName = $matches[1];
471 if ($siteName) {
472 $siteName = "/sites/$siteName/";
473 $siteNamePos = strpos($dir, $siteName);
474 if ($siteNamePos !== FALSE) {
475 $siteRoot = substr($dir, 0, $siteNamePos);
476 }
477 }
478 }
479 }
480
481 return array($url, $dir, $siteName, $siteRoot);
482 }
483
484 /**
485 * Return likely default settings
486 * @return array site settings
487 * -$url,
488 * - $dir Base Directory
489 * - $siteName
490 * - $siteRoot
491 */
492 static function getBestGuessSettings() {
493 $config = CRM_Core_Config::singleton();
494
495 //CRM-15365 - Fix preg_replace to handle backslash for Windows File Paths
496 if (DIRECTORY_SEPARATOR == '\\') {
497 $needle = 'civicrm[/\\\\]templates_c[/\\\\].*$';
498 }
499 else {
500 $needle = 'civicrm/templates_c/.*$';
501 }
502
503 $dir = preg_replace(
504 "|$needle|",
505 '',
506 $config->templateCompileDir
507 );
508
509 list($url, $siteName, $siteRoot) = $config->userSystem->getDefaultSiteSettings($dir);
510 return array($url, $dir, $siteName, $siteRoot);
511 }
512
513 /**
514 * @param array $defaultValues
515 *
516 * @return string
517 * @throws Exception
518 */
519 static function doSiteMove($defaultValues = array()) {
520 $moveStatus = ts('Beginning site move process...') . '<br />';
521 // get the current and guessed values
522 list($oldURL, $oldDir, $oldSiteName, $oldSiteRoot) = self::getConfigSettings();
523 list($newURL, $newDir, $newSiteName, $newSiteRoot) = self::getBestGuessSettings();
524
525
526 // retrieve these values from the argument list
527 $variables = array('URL', 'Dir', 'SiteName', 'SiteRoot', 'Val_1', 'Val_2', 'Val_3');
528 $states = array('old', 'new');
529 foreach ($variables as $varSuffix) {
530 foreach ($states as $state) {
531 $var = "{$state}{$varSuffix}";
532 if (!isset($$var)) {
533 if (isset($defaultValues[$var])) {
534 $$var = $defaultValues[$var];
535 }
536 else {
537 $$var = NULL;
538 }
539 }
540 $$var = CRM_Utils_Request::retrieve($var,
541 'String',
542 CRM_Core_DAO::$_nullArray,
543 FALSE,
544 $$var,
545 'REQUEST'
546 );
547 }
548 }
549
550 $from = $to = array();
551 foreach ($variables as $varSuffix) {
552 $oldVar = "old{$varSuffix}";
553 $newVar = "new{$varSuffix}";
554 //skip it if either is empty or both are exactly the same
555 if ($$oldVar &&
556 $$newVar &&
557 $$oldVar != $$newVar
558 ) {
559 $from[] = $$oldVar;
560 $to[] = $$newVar;
561 }
562 }
563
564 $sql = "
565 SELECT config_backend
566 FROM civicrm_domain
567 WHERE id = %1
568 ";
569 $params = array(1 => array(CRM_Core_Config::domainID(), 'Integer'));
570 $configBackend = CRM_Core_DAO::singleValueQuery($sql, $params);
571 if (!$configBackend) {
572 CRM_Core_Error::fatal(ts('Returning early due to unexpected error - civicrm_domain.config_backend column value is NULL. Try visiting CiviCRM Home page.'));
573 }
574 $configBackend = unserialize($configBackend);
575
576 $configBackend = str_replace($from,
577 $to,
578 $configBackend
579 );
580
581 $configBackend = serialize($configBackend);
582 $sql = "
583 UPDATE civicrm_domain
584 SET config_backend = %2
585 WHERE id = %1
586 ";
587 $params[2] = array($configBackend, 'String');
588 CRM_Core_DAO::executeQuery($sql, $params);
589
590 // Apply the changes to civicrm_option_values
591 $optionGroups = array('url_preferences', 'directory_preferences');
592 foreach ($optionGroups as $option) {
593 foreach ($variables as $varSuffix) {
594 $oldVar = "old{$varSuffix}";
595 $newVar = "new{$varSuffix}";
596
597 $from = $$oldVar;
598 $to = $$newVar;
599
600 if ($from && $to && $from != $to) {
601 $sql = '
602 UPDATE civicrm_option_value
603 SET value = REPLACE(value, %1, %2)
604 WHERE option_group_id = (
605 SELECT id
606 FROM civicrm_option_group
607 WHERE name = %3 )
608 ';
609 $params = array(
610 1 => array($from, 'String'),
611 2 => array($to, 'String'),
612 3 => array($option, 'String'),
613 );
614 CRM_Core_DAO::executeQuery($sql, $params);
615 }
616 }
617 }
618
619 $moveStatus .=
620 ts('Directory and Resource URLs have been updated in the moved database to reflect current site location.') .
621 '<br />';
622
623 $config = CRM_Core_Config::singleton();
624
625 // clear the template_c and upload directory also
626 $config->cleanup(3, TRUE);
627 $moveStatus .= ts('Template cache and upload directory have been cleared.') . '<br />';
628
629 // clear all caches
630 CRM_Core_Config::clearDBCache();
631 $moveStatus .= ts('Database cache tables cleared.') . '<br />';
632
633 $resetSessionTable = CRM_Utils_Request::retrieve('resetSessionTable',
634 'Boolean',
635 CRM_Core_DAO::$_nullArray,
636 FALSE,
637 FALSE,
638 'REQUEST'
639 );
640 if ($config->userSystem->is_drupal &&
641 $resetSessionTable
642 ) {
643 db_query("DELETE FROM {sessions} WHERE 1");
644 $moveStatus .= ts('Drupal session table cleared.') . '<br />';
645 }
646 else {
647 $session = CRM_Core_Session::singleton();
648 $session->reset(2);
649 $moveStatus .= ts('Session has been reset.') . '<br />';
650 }
651
652 return $moveStatus;
653 }
654
655 /**
656 * Takes a componentName and enables it in the config
657 * Primarily used during unit testing
658 *
659 * @param string $componentName name of the component to be enabled, needs to be valid
660 *
661 * @return boolean - true if valid component name and enabling succeeds, else false
662 * @static
663 */
664 static function enableComponent($componentName) {
665 $config = CRM_Core_Config::singleton();
666 if (in_array($componentName, $config->enableComponents)) {
667 // component is already enabled
668 return TRUE;
669 }
670
671 // return if component does not exist
672 if (!array_key_exists($componentName, CRM_Core_Component::getComponents())) {
673 return FALSE;
674 }
675
676 // get enabled-components from DB and add to the list
677 $enabledComponents =
678 CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'enable_components', NULL, array());
679 $enabledComponents[] = $componentName;
680
681 self::setEnabledComponents($enabledComponents);
682
683 return TRUE;
684 }
685
686 static function disableComponent($componentName) {
687 $config = CRM_Core_Config::singleton();
688 if (!in_array($componentName, $config->enableComponents) ||
689 !array_key_exists($componentName, CRM_Core_Component::getComponents())
690 ) {
691 // post-condition satisified
692 return TRUE;
693 }
694
695 // get enabled-components from DB and add to the list
696 $enabledComponents =
697 CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'enable_components', NULL, array());
698 $enabledComponents = array_diff($enabledComponents, array($componentName));
699
700 self::setEnabledComponents($enabledComponents);
701
702 return TRUE;
703 }
704
705 public static function setEnabledComponents($enabledComponents) {
706 $config = CRM_Core_Config::singleton();
707 $components = CRM_Core_Component::getComponents();
708
709 $enabledComponentIDs = array();
710 foreach ($enabledComponents as $name) {
711 $enabledComponentIDs[] = $components[$name]->componentID;
712 }
713
714 // fix the config object
715 $config->enableComponents = $enabledComponents;
716 $config->enableComponentIDs = $enabledComponentIDs;
717
718 // also force reset of component array
719 CRM_Core_Component::getEnabledComponents(TRUE);
720
721 // update DB
722 CRM_Core_BAO_Setting::setItem($enabledComponents,
723 CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'enable_components');
724 }
725
726 /**
727 * @return array
728 */
729 static function skipVars() {
730 return array(
731 'dsn',
732 'templateCompileDir',
733 'userFrameworkDSN',
734 'userFramework',
735 'userFrameworkBaseURL',
736 'userFrameworkClass',
737 'userHookClass',
738 'userPermissionClass',
739 'userFrameworkURLVar',
740 'userFrameworkVersion',
741 'newBaseURL',
742 'newBaseDir',
743 'newSiteName',
744 'configAndLogDir',
745 'qfKey',
746 'gettextResourceDir',
747 'cleanURL',
748 'locale_custom_strings',
749 'localeCustomStrings',
750 'autocompleteContactSearch',
751 'autocompleteContactReference',
752 'checksumTimeout',
753 );
754 }
755 }
756