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