Merge pull request #4806 from civicrm/4.5
[civicrm-core.git] / CRM / Core / BAO / ConfigSetting.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
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 (empty($lcMessages)) {
322 //CRM-11993 - if a single-lang site, use default
323 $lcMessages = CRM_Utils_Array::value('lcMessages', $defaults);
324 }
325
326 // set suffix for table names - use views if more than one language
327 $dbLocale = $multiLang ? "_{$lcMessages}" : '';
328
329 // FIXME: an ugly hack to fix CRM-4041
330 global $tsLocale;
331 $tsLocale = $lcMessages;
332
333 // FIXME: as bad aplace as any to fix CRM-5428
334 // (to be moved to a sane location along with the above)
335 if (function_exists('mb_internal_encoding')) {
336 mb_internal_encoding('UTF-8');
337 }
338 }
339
340 // dont add if its empty
341 if (!empty($defaults)) {
342 // retrieve directory and url preferences also
343 CRM_Core_BAO_Setting::retrieveDirectoryAndURLPreferences($defaults);
344
345 // Pickup enabled-components from settings table if found.
346 $enableComponents = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'enable_components', NULL, array());
347 if (!empty($enableComponents)) {
348 $defaults['enableComponents'] = $enableComponents;
349
350 $components = CRM_Core_Component::getComponents();
351 $enabledComponentIDs = array();
352 foreach ($defaults['enableComponents'] as $name) {
353 $enabledComponentIDs[] = $components[$name]->componentID;
354 }
355 $defaults['enableComponentIDs'] = $enabledComponentIDs;
356 }
357 }
358 }
359
360 /**
361 * @return array
362 */
363 static function getConfigSettings() {
364 $config = CRM_Core_Config::singleton();
365
366 $url = $dir = $siteName = $siteRoot = NULL;
367 if ($config->userFramework == 'Joomla') {
368 $url = preg_replace(
369 '|administrator/components/com_civicrm/civicrm/|',
370 '',
371 $config->userFrameworkResourceURL
372 );
373
374 // lets use imageUploadDir since we dont mess around with its values
375 // in the config object, lets kep it a bit generic since folks
376 // might have different values etc
377
378 //CRM-15365 - Fix preg_replace to handle backslash for Windows File Paths
379 if (DIRECTORY_SEPARATOR == '\\') {
380 $dir = preg_replace(
381 '|civicrm[/\\\\]templates_c[/\\\\].*$|',
382 '',
383 $config->templateCompileDir
384 );
385 }
386 else {
387 $dir = preg_replace(
388 '|civicrm/templates_c/.*$|',
389 '',
390 $config->templateCompileDir
391 );
392 }
393
394 $siteRoot = preg_replace(
395 '|/media/civicrm/.*$|',
396 '',
397 $config->imageUploadDir
398 );
399 }
400 else if ($config->userFramework == 'WordPress') {
401 $url = preg_replace(
402 '|wp-content/plugins/civicrm/civicrm/|',
403 '',
404 $config->userFrameworkResourceURL
405 );
406
407 // lets use imageUploadDir since we dont mess around with its values
408 // in the config object, lets kep it a bit generic since folks
409 // might have different values etc
410
411 //CRM-15365 - Fix preg_replace to handle backslash for Windows File Paths
412 if (DIRECTORY_SEPARATOR == '\\') {
413 $dir = preg_replace(
414 '|civicrm[/\\\\]templates_c[/\\\\].*$|',
415 '',
416 $config->templateCompileDir
417 );
418 }
419 else {
420 $dir = preg_replace(
421 '|civicrm/templates_c/.*$|',
422 '',
423 $config->templateCompileDir
424 );
425 }
426
427 $siteRoot = preg_replace(
428 '|/wp-content/plugins/files/civicrm/.*$|',
429 '',
430 $config->imageUploadDir
431 );
432 }
433 else {
434 $url = preg_replace(
435 '|sites/[\w\.\-\_]+/modules/civicrm/|',
436 '',
437 $config->userFrameworkResourceURL
438 );
439
440 // lets use imageUploadDir since we dont mess around with its values
441 // in the config object, lets kep it a bit generic since folks
442 // might have different values etc
443
444 //CRM-15365 - Fix preg_replace to handle backslash for Windows File Paths
445 if (DIRECTORY_SEPARATOR == '\\') {
446 $dir = preg_replace(
447 '|[/\\\\]files[/\\\\]civicrm[/\\\\].*$|',
448 '\\\\files\\\\',
449 $config->imageUploadDir
450 );
451 }
452 else {
453 $dir = preg_replace(
454 '|/files/civicrm/.*$|',
455 '/files/',
456 $config->imageUploadDir
457 );
458 }
459
460 $matches = array();
461 if (preg_match(
462 '|/sites/([\w\.\-\_]+)/|',
463 $config->imageUploadDir,
464 $matches
465 )) {
466 $siteName = $matches[1];
467 if ($siteName) {
468 $siteName = "/sites/$siteName/";
469 $siteNamePos = strpos($dir, $siteName);
470 if ($siteNamePos !== FALSE) {
471 $siteRoot = substr($dir, 0, $siteNamePos);
472 }
473 }
474 }
475 }
476
477 return array($url, $dir, $siteName, $siteRoot);
478 }
479
480 /**
481 * Return likely default settings
482 * @return array site settings
483 * -$url,
484 * - $dir Base Directory
485 * - $siteName
486 * - $siteRoot
487 */
488 static function getBestGuessSettings() {
489 $config = CRM_Core_Config::singleton();
490
491 //CRM-15365 - Fix preg_replace to handle backslash for Windows File Paths
492 if (DIRECTORY_SEPARATOR == '\\') {
493 $needle = 'civicrm[/\\\\]templates_c[/\\\\].*$';
494 }
495 else {
496 $needle = 'civicrm/templates_c/.*$';
497 }
498
499 $dir = preg_replace(
500 "|$needle|",
501 '',
502 $config->templateCompileDir
503 );
504
505 list($url, $siteName, $siteRoot) = $config->userSystem->getDefaultSiteSettings($dir);
506 return array($url, $dir, $siteName, $siteRoot);
507 }
508
509 /**
510 * @param array $defaultValues
511 *
512 * @return string
513 * @throws Exception
514 */
515 static function doSiteMove($defaultValues = array()) {
516 $moveStatus = ts('Beginning site move process...') . '<br />';
517 // get the current and guessed values
518 list($oldURL, $oldDir, $oldSiteName, $oldSiteRoot) = self::getConfigSettings();
519 list($newURL, $newDir, $newSiteName, $newSiteRoot) = self::getBestGuessSettings();
520
521
522 // retrieve these values from the argument list
523 $variables = array('URL', 'Dir', 'SiteName', 'SiteRoot', 'Val_1', 'Val_2', 'Val_3');
524 $states = array('old', 'new');
525 foreach ($variables as $varSuffix) {
526 foreach ($states as $state) {
527 $var = "{$state}{$varSuffix}";
528 if (!isset($$var)) {
529 if (isset($defaultValues[$var])) {
530 $$var = $defaultValues[$var];
531 }
532 else {
533 $$var = NULL;
534 }
535 }
536 $$var = CRM_Utils_Request::retrieve($var,
537 'String',
538 CRM_Core_DAO::$_nullArray,
539 FALSE,
540 $$var,
541 'REQUEST'
542 );
543 }
544 }
545
546 $from = $to = array();
547 foreach ($variables as $varSuffix) {
548 $oldVar = "old{$varSuffix}";
549 $newVar = "new{$varSuffix}";
550 //skip it if either is empty or both are exactly the same
551 if ($$oldVar &&
552 $$newVar &&
553 $$oldVar != $$newVar
554 ) {
555 $from[] = $$oldVar;
556 $to[] = $$newVar;
557 }
558 }
559
560 $sql = "
561 SELECT config_backend
562 FROM civicrm_domain
563 WHERE id = %1
564 ";
565 $params = array(1 => array(CRM_Core_Config::domainID(), 'Integer'));
566 $configBackend = CRM_Core_DAO::singleValueQuery($sql, $params);
567 if (!$configBackend) {
568 CRM_Core_Error::fatal(ts('Returning early due to unexpected error - civicrm_domain.config_backend column value is NULL. Try visiting CiviCRM Home page.'));
569 }
570 $configBackend = unserialize($configBackend);
571
572 $configBackend = str_replace($from,
573 $to,
574 $configBackend
575 );
576
577 $configBackend = serialize($configBackend);
578 $sql = "
579 UPDATE civicrm_domain
580 SET config_backend = %2
581 WHERE id = %1
582 ";
583 $params[2] = array($configBackend, 'String');
584 CRM_Core_DAO::executeQuery($sql, $params);
585
586 // Apply the changes to civicrm_option_values
587 $optionGroups = array('url_preferences', 'directory_preferences');
588 foreach ($optionGroups as $option) {
589 foreach ($variables as $varSuffix) {
590 $oldVar = "old{$varSuffix}";
591 $newVar = "new{$varSuffix}";
592
593 $from = $$oldVar;
594 $to = $$newVar;
595
596 if ($from && $to && $from != $to) {
597 $sql = '
598 UPDATE civicrm_option_value
599 SET value = REPLACE(value, %1, %2)
600 WHERE option_group_id = (
601 SELECT id
602 FROM civicrm_option_group
603 WHERE name = %3 )
604 ';
605 $params = array(
606 1 => array($from, 'String'),
607 2 => array($to, 'String'),
608 3 => array($option, 'String'),
609 );
610 CRM_Core_DAO::executeQuery($sql, $params);
611 }
612 }
613 }
614
615 $moveStatus .=
616 ts('Directory and Resource URLs have been updated in the moved database to reflect current site location.') .
617 '<br />';
618
619 $config = CRM_Core_Config::singleton();
620
621 // clear the template_c and upload directory also
622 $config->cleanup(3, TRUE);
623 $moveStatus .= ts('Template cache and upload directory have been cleared.') . '<br />';
624
625 // clear all caches
626 CRM_Core_Config::clearDBCache();
627 $moveStatus .= ts('Database cache tables cleared.') . '<br />';
628
629 $resetSessionTable = CRM_Utils_Request::retrieve('resetSessionTable',
630 'Boolean',
631 CRM_Core_DAO::$_nullArray,
632 FALSE,
633 FALSE,
634 'REQUEST'
635 );
636 if ($config->userSystem->is_drupal &&
637 $resetSessionTable
638 ) {
639 db_query("DELETE FROM {sessions} WHERE 1");
640 $moveStatus .= ts('Drupal session table cleared.') . '<br />';
641 }
642 else {
643 $session = CRM_Core_Session::singleton();
644 $session->reset(2);
645 $moveStatus .= ts('Session has been reset.') . '<br />';
646 }
647
648 return $moveStatus;
649 }
650
651 /**
652 * Takes a componentName and enables it in the config
653 * Primarily used during unit testing
654 *
655 * @param string $componentName name of the component to be enabled, needs to be valid
656 *
657 * @return boolean - true if valid component name and enabling succeeds, else false
658 * @static
659 */
660 static function enableComponent($componentName) {
661 $config = CRM_Core_Config::singleton();
662 if (in_array($componentName, $config->enableComponents)) {
663 // component is already enabled
664 return TRUE;
665 }
666
667 // return if component does not exist
668 if (!array_key_exists($componentName, CRM_Core_Component::getComponents())) {
669 return FALSE;
670 }
671
672 // get enabled-components from DB and add to the list
673 $enabledComponents =
674 CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'enable_components', NULL, array());
675 $enabledComponents[] = $componentName;
676
677 self::setEnabledComponents($enabledComponents);
678
679 return TRUE;
680 }
681
682 static function disableComponent($componentName) {
683 $config = CRM_Core_Config::singleton();
684 if (!in_array($componentName, $config->enableComponents) ||
685 !array_key_exists($componentName, CRM_Core_Component::getComponents())
686 ) {
687 // post-condition satisified
688 return TRUE;
689 }
690
691 // get enabled-components from DB and add to the list
692 $enabledComponents =
693 CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'enable_components', NULL, array());
694 $enabledComponents = array_diff($enabledComponents, array($componentName));
695
696 self::setEnabledComponents($enabledComponents);
697
698 return TRUE;
699 }
700
701 public static function setEnabledComponents($enabledComponents) {
702 $config = CRM_Core_Config::singleton();
703 $components = CRM_Core_Component::getComponents();
704
705 $enabledComponentIDs = array();
706 foreach ($enabledComponents as $name) {
707 $enabledComponentIDs[] = $components[$name]->componentID;
708 }
709
710 // fix the config object
711 $config->enableComponents = $enabledComponents;
712 $config->enableComponentIDs = $enabledComponentIDs;
713
714 // also force reset of component array
715 CRM_Core_Component::getEnabledComponents(TRUE);
716
717 // update DB
718 CRM_Core_BAO_Setting::setItem($enabledComponents,
719 CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'enable_components');
720 }
721
722 /**
723 * @return array
724 */
725 static function skipVars() {
726 return array(
727 'dsn',
728 'templateCompileDir',
729 'userFrameworkDSN',
730 'userFramework',
731 'userFrameworkBaseURL',
732 'userFrameworkClass',
733 'userHookClass',
734 'userPermissionClass',
735 'userFrameworkURLVar',
736 'userFrameworkVersion',
737 'newBaseURL',
738 'newBaseDir',
739 'newSiteName',
740 'configAndLogDir',
741 'qfKey',
742 'gettextResourceDir',
743 'cleanURL',
744 'locale_custom_strings',
745 'localeCustomStrings',
746 'autocompleteContactSearch',
747 'autocompleteContactReference',
748 'checksumTimeout',
749 );
750 }
751 }
752