Allow override of inherited CMS language when in CiviCRM
[civicrm-core.git] / CRM / Admin / Form / Setting / Localization.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18/**
ce064e4f 19 * This class generates form components for Localization.
6a488035
TO
20 */
21class CRM_Admin_Form_Setting_Localization extends CRM_Admin_Form_Setting {
8a61528e 22
be2fb01f 23 protected $_settings = [
9c325795 24 'contact_default_language' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
8a61528e
TO
25 'countryLimit' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
26 'customTranslateFunction' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
27 'defaultContactCountry' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
28 'defaultContactStateProvince' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
29 'defaultCurrency' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
30 'fieldSeparator' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
31 'inheritLocale' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
32 'lcMessages' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
33 'legacyEncoding' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
34 'monetaryThousandSeparator' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
35 'monetaryDecimalPoint' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
36 'moneyformat' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
37 'moneyvalueformat' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
38 'provinceLimit' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
fe810d04 39 'uiLanguages' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
be2fb01f 40 ];
6a488035
TO
41
42 /**
eceb18cc 43 * Build the form object.
6a488035
TO
44 */
45 public function buildQuickForm() {
46 $config = CRM_Core_Config::singleton();
47
6a488035
TO
48 CRM_Utils_System::setTitle(ts('Settings - Localization'));
49
fe25a292 50 $warningTitle = json_encode(ts("Warning"));
b72b5fc0 51 $defaultLocaleOptions = CRM_Admin_Form_Setting_Localization::getDefaultLocaleOptions();
8a61528e 52
921ed8ae 53 if (CRM_Core_I18n::isMultiLingual()) {
6a488035 54 // add language limiter and language adder
b72b5fc0 55 $this->addCheckBox('languageLimit', ts('Available Languages'), array_flip($defaultLocaleOptions), NULL, NULL, NULL, NULL, ' &nbsp; ');
be2fb01f 56 $this->addElement('select', 'addLanguage', ts('Add Language'), array_merge(['' => ts('- select -')], array_diff(CRM_Core_I18n::languages(), $defaultLocaleOptions)));
6a488035
TO
57
58 // add the ability to return to single language
fe25a292 59 $warning = ts('This will make your CiviCRM installation a single-language one again. THIS WILL DELETE ALL DATA RELATED TO LANGUAGES OTHER THAN THE DEFAULT ONE SELECTED ABOVE (and only that language will be preserved).');
6a488035 60 $this->assign('warning', $warning);
fe25a292 61 $warning = json_encode($warning);
6a488035 62 $this->addElement('checkbox', 'makeSinglelingual', ts('Return to Single Language'),
be2fb01f 63 NULL, ['onChange' => "if (this.checked) CRM.alert($warning, $warningTitle)"]
6a488035
TO
64 );
65 }
66 else {
fe25a292 67 $warning = ts('Enabling multiple languages changes the schema of your database, so make sure you know what you are doing when enabling this function; making a database backup is strongly recommended.');
6a488035 68 $this->assign('warning', $warning);
fe25a292 69 $warning = json_encode($warning);
6a488035
TO
70 $validTriggerPermission = CRM_Core_DAO::checkTriggerViewPermission(TRUE);
71
72 if ($validTriggerPermission &&
344b05bc 73 !\Civi::settings()->get('logging')
6a488035
TO
74 ) {
75 $this->addElement('checkbox', 'makeMultilingual', ts('Enable Multiple Languages'),
be2fb01f 76 NULL, ['onChange' => "if (this.checked) CRM.alert($warning, $warningTitle)"]
6a488035
TO
77 );
78 }
79 }
9c325795
TO
80 $this->addElement('select', 'contact_default_language', ts('Default Language for users'),
81 CRM_Admin_Form_Setting_Localization::getDefaultLanguageOptions());
6a488035
TO
82
83 $includeCurrency = &$this->addElement('advmultiselect', 'currencyLimit',
8a61528e 84 ts('Available Currencies') . ' ', self::getCurrencySymbols(),
be2fb01f 85 [
6a488035
TO
86 'size' => 5,
87 'style' => 'width:150px',
88 'class' => 'advmultiselect',
be2fb01f 89 ]
6a488035
TO
90 );
91
be2fb01f
CW
92 $includeCurrency->setButtonAttributes('add', ['value' => ts('Add >>')]);
93 $includeCurrency->setButtonAttributes('remove', ['value' => ts('<< Remove')]);
6a488035 94
be2fb01f 95 $this->addFormRule(['CRM_Admin_Form_Setting_Localization', 'formRule']);
6a488035
TO
96
97 parent::buildQuickForm();
98 }
99
e0ef6999
EM
100 /**
101 * @param $fields
102 *
103 * @return array|bool
104 */
00be9182 105 public static function formRule($fields) {
be2fb01f 106 $errors = [];
6a488035
TO
107 if (CRM_Utils_Array::value('monetaryThousandSeparator', $fields) ==
108 CRM_Utils_Array::value('monetaryDecimalPoint', $fields)
109 ) {
110 $errors['monetaryThousandSeparator'] = ts('Thousands Separator and Decimal Delimiter can not be the same.');
111 }
112
113 if (strlen($fields['monetaryThousandSeparator']) == 0) {
114 $errors['monetaryThousandSeparator'] = ts('Thousands Separator can not be empty. You can use a space character instead.');
115 }
116
6a488035
TO
117 if (strlen($fields['monetaryDecimalPoint']) > 1) {
118 $errors['monetaryDecimalPoint'] = ts('Decimal Delimiter can not have more than 1 character.');
119 }
120
121 if (trim($fields['customTranslateFunction']) &&
122 !function_exists(trim($fields['customTranslateFunction']))
123 ) {
124 $errors['customTranslateFunction'] = ts('Please define the custom translation function first.');
125 }
126
127 // CRM-7962, CRM-7713, CRM-9004
128 if (!empty($fields['defaultContactCountry']) &&
8cc574cf 129 (!empty($fields['countryLimit']) &&
6a488035
TO
130 (!in_array($fields['defaultContactCountry'], $fields['countryLimit']))
131 )
132 ) {
133 $errors['defaultContactCountry'] = ts('Please select a default country that is in the list of available countries.');
b05e28de 134 }
6a488035
TO
135
136 return empty($errors) ? TRUE : $errors;
137 }
138
f2ac86d1 139 /**
140 * Set the default values for the form.
141 *
142 * @return array
143 */
00be9182 144 public function setDefaultValues() {
6a488035
TO
145 parent::setDefaultValues();
146
147 // CRM-1496
148 // retrieve default values for currencyLimit
149 $this->_defaults['currencyLimit'] = array_keys(CRM_Core_OptionGroup::values('currencies_enabled'));
150
b72b5fc0
TO
151 $this->_defaults['languageLimit'] = Civi::settings()->get('languageLimit');
152
6a488035
TO
153 // CRM-5111: unset these two unconditionally, we don’t want them to stick – ever
154 unset($this->_defaults['makeMultilingual']);
155 unset($this->_defaults['makeSinglelingual']);
156 return $this->_defaults;
157 }
158
159 public function postProcess() {
160 $values = $this->exportValues();
161
6a488035
TO
162 //cache contact fields retaining localized titles
163 //though we changed localization, so reseting cache.
9cdf85c1 164 Civi::cache('fields')->flush();
6a488035
TO
165
166 //CRM-8559, cache navigation do not respect locale if it is changed, so reseting cache.
96689db3
SL
167 Civi::cache('navigation')->flush();
168 // reset ACL and System caches
169 CRM_Core_BAO_Cache::resetCaches();
6a488035
TO
170
171 // we do this only to initialize monetary decimal point and thousand separator
172 $config = CRM_Core_Config::singleton();
173
ece6501c 174 // save enabled currencies and default currency in option group 'currencies_enabled'
6a488035
TO
175 // CRM-1496
176 if (empty($values['currencyLimit'])) {
be2fb01f 177 $values['currencyLimit'] = [$values['defaultCurrency']];
6a488035 178 }
ece6501c 179 elseif (!in_array($values['defaultCurrency'], $values['currencyLimit'])) {
6a488035
TO
180 $values['currencyLimit'][] = $values['defaultCurrency'];
181 }
182
ece6501c 183 self::updateEnabledCurrencies($values['currencyLimit'], $values['defaultCurrency']);
6a488035
TO
184
185 // unset currencyLimit so we dont store there
186 unset($values['currencyLimit']);
187
188 // make the site multi-lang if requested
a7488080 189 if (!empty($values['makeMultilingual'])) {
6a488035
TO
190 CRM_Core_I18n_Schema::makeMultilingual($values['lcMessages']);
191 $values['languageLimit'][$values['lcMessages']] = 1;
192 // make the site single-lang if requested
193 }
a7488080 194 elseif (!empty($values['makeSinglelingual'])) {
6a488035
TO
195 CRM_Core_I18n_Schema::makeSinglelingual($values['lcMessages']);
196 $values['languageLimit'] = '';
197 }
198
199 // add a new db locale if the requested language is not yet supported by the db
de6c59ca 200 if (empty($values['makeSinglelingual']) && !empty($values['addLanguage'])) {
6a488035
TO
201 $domain = new CRM_Core_DAO_Domain();
202 $domain->find(TRUE);
203 if (!substr_count($domain->locales, $values['addLanguage'])) {
204 CRM_Core_I18n_Schema::addLocale($values['addLanguage'], $values['lcMessages']);
205 }
206 $values['languageLimit'][$values['addLanguage']] = 1;
207 }
208
921ed8ae
AS
209 // current language should be in the ui list
210 if (!in_array($values['lcMessages'], $values['uiLanguages'])) {
211 $values['uiLanguages'][] = $values['lcMessages'];
212 }
213
6a488035 214 // if we manipulated the language list, return to the localization admin screen
02fc859b 215 $return = (bool) (CRM_Utils_Array::value('makeMultilingual', $values) or CRM_Utils_Array::value('addLanguage', $values));
6a488035 216
b72b5fc0
TO
217 $filteredValues = $values;
218 unset($filteredValues['makeMultilingual']);
219 unset($filteredValues['makeSinglelingual']);
220 unset($filteredValues['addLanguage']);
221 unset($filteredValues['languageLimit']);
222
223 Civi::settings()->set('languageLimit', CRM_Utils_Array::value('languageLimit', $values));
224
6a488035 225 // save all the settings
b72b5fc0 226 parent::commonProcess($filteredValues);
6a488035
TO
227
228 if ($return) {
229 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/setting/localization', 'reset=1'));
230 }
231 }
96025800 232
ece6501c
ML
233 /**
234 * Replace available currencies by the ones provided
235 *
236 * @param $currencies array of currencies ['USD', 'CAD']
237 * @param $default default currency
238 */
239 public static function updateEnabledCurrencies($currencies, $default) {
240
241 // sort so that when we display drop down, weights have right value
242 sort($currencies);
243
244 // get labels for all the currencies
be2fb01f 245 $options = [];
ece6501c
ML
246
247 $currencySymbols = CRM_Admin_Form_Setting_Localization::getCurrencySymbols();
248 for ($i = 0; $i < count($currencies); $i++) {
be2fb01f 249 $options[] = [
ece6501c
ML
250 'label' => $currencySymbols[$currencies[$i]],
251 'value' => $currencies[$i],
252 'weight' => $i + 1,
253 'is_active' => 1,
254 'is_default' => $currencies[$i] == $default,
be2fb01f 255 ];
ece6501c
ML
256 }
257
258 $dontCare = NULL;
259 CRM_Core_OptionGroup::createAssoc('currencies_enabled', $options, $dontCare);
260
261 }
262
8a61528e
TO
263 /**
264 * @return array
265 */
266 public static function getAvailableCountries() {
267 $i18n = CRM_Core_I18n::singleton();
be2fb01f 268 $country = [];
8a61528e 269 CRM_Core_PseudoConstant::populate($country, 'CRM_Core_DAO_Country', TRUE, 'name', 'is_active');
be2fb01f 270 $i18n->localizeArray($country, ['context' => 'country']);
8a61528e
TO
271 asort($country);
272 return $country;
273 }
274
275 /**
8246bca4 276 * Get the default locale options.
277 *
8a61528e
TO
278 * @return array
279 */
280 public static function getDefaultLocaleOptions() {
281 $domain = new CRM_Core_DAO_Domain();
282 $domain->find(TRUE);
283 $locales = CRM_Core_I18n::languages();
284 if ($domain->locales) {
285 // for multi-lingual sites, populate default language drop-down with available languages
be2fb01f 286 $defaultLocaleOptions = [];
8a61528e
TO
287 foreach ($locales as $loc => $lang) {
288 if (substr_count($domain->locales, $loc)) {
b72b5fc0 289 $defaultLocaleOptions[$loc] = $lang;
8a61528e
TO
290 }
291 }
292 }
293 else {
b72b5fc0 294 $defaultLocaleOptions = $locales;
8a61528e 295 }
b72b5fc0 296 return $defaultLocaleOptions;
8a61528e
TO
297 }
298
299 /**
300 * Get a list of currencies (with their symbols).
301 *
302 * @return array
303 * Array('USD' => 'USD ($)').
304 */
305 public static function getCurrencySymbols() {
be2fb01f 306 $symbols = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'currency', [
8a61528e
TO
307 'labelColumn' => 'symbol',
308 'orderColumn' => TRUE,
be2fb01f
CW
309 ]);
310 $_currencySymbols = [];
8a61528e
TO
311 foreach ($symbols as $key => $value) {
312 $_currencySymbols[$key] = "$key";
313 if ($value) {
314 $_currencySymbols[$key] .= " ($value)";
315 }
316 }
317 return $_currencySymbols;
318 }
319
f2ac86d1 320 /**
321 * Update session and uf_match table when the locale is updated.
322 *
323 * @param string $oldLocale
324 * @param string $newLocale
325 * @param array $metadata
326 * @param int $domainID
327 */
b72b5fc0
TO
328 public static function onChangeLcMessages($oldLocale, $newLocale, $metadata, $domainID) {
329 if ($oldLocale == $newLocale) {
330 return;
331 }
332
333 $session = CRM_Core_Session::singleton();
334 if ($newLocale && $session->get('userID')) {
335 $ufm = new CRM_Core_DAO_UFMatch();
336 $ufm->contact_id = $session->get('userID');
337 if ($newLocale && $ufm->find(TRUE)) {
338 $ufm->language = $newLocale;
339 $ufm->save();
340 $session->set('lcMessages', $newLocale);
341 }
342 }
343 }
344
ece6501c
ML
345 public static function onChangeDefaultCurrency($oldCurrency, $newCurrency, $metadata) {
346 if ($oldCurrency == $newCurrency) {
347 return;
348 }
349
350 // ensure that default currency is always in the list of enabled currencies
351 $currencies = array_keys(CRM_Core_OptionGroup::values('currencies_enabled'));
352 if (!in_array($newCurrency, $currencies)) {
353 if (empty($currencies)) {
be2fb01f 354 $currencies = [$values['defaultCurrency']];
ece6501c
ML
355 }
356 else {
357 $currencies[] = $newCurrency;
358 }
359
360 CRM_Admin_Form_Setting_Localization::updateEnabledCurrencies($currencies, $newCurrency);
361 }
362
363 }
364
9c325795
TO
365 /**
366 * @return array
367 */
368 public static function getDefaultLanguageOptions() {
be2fb01f 369 return [
9c325795
TO
370 '*default*' => ts('Use default site language'),
371 'undefined' => ts('Leave undefined'),
372 'current_site_language' => ts('Use language in use at the time'),
be2fb01f 373 ];
9c325795
TO
374 }
375
6a488035 376}