Allow override of inherited CMS language when in CiviCRM
[civicrm-core.git] / CRM / Admin / Form / Setting / Localization.php
... / ...
CommitLineData
1<?php
2/*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 |
9 +--------------------------------------------------------------------+
10 */
11
12/**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18/**
19 * This class generates form components for Localization.
20 */
21class CRM_Admin_Form_Setting_Localization extends CRM_Admin_Form_Setting {
22
23 protected $_settings = [
24 'contact_default_language' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
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,
39 'uiLanguages' => CRM_Core_BAO_Setting::LOCALIZATION_PREFERENCES_NAME,
40 ];
41
42 /**
43 * Build the form object.
44 */
45 public function buildQuickForm() {
46 $config = CRM_Core_Config::singleton();
47
48 CRM_Utils_System::setTitle(ts('Settings - Localization'));
49
50 $warningTitle = json_encode(ts("Warning"));
51 $defaultLocaleOptions = CRM_Admin_Form_Setting_Localization::getDefaultLocaleOptions();
52
53 if (CRM_Core_I18n::isMultiLingual()) {
54 // add language limiter and language adder
55 $this->addCheckBox('languageLimit', ts('Available Languages'), array_flip($defaultLocaleOptions), NULL, NULL, NULL, NULL, ' &nbsp; ');
56 $this->addElement('select', 'addLanguage', ts('Add Language'), array_merge(['' => ts('- select -')], array_diff(CRM_Core_I18n::languages(), $defaultLocaleOptions)));
57
58 // add the ability to return to single language
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).');
60 $this->assign('warning', $warning);
61 $warning = json_encode($warning);
62 $this->addElement('checkbox', 'makeSinglelingual', ts('Return to Single Language'),
63 NULL, ['onChange' => "if (this.checked) CRM.alert($warning, $warningTitle)"]
64 );
65 }
66 else {
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.');
68 $this->assign('warning', $warning);
69 $warning = json_encode($warning);
70 $validTriggerPermission = CRM_Core_DAO::checkTriggerViewPermission(TRUE);
71
72 if ($validTriggerPermission &&
73 !\Civi::settings()->get('logging')
74 ) {
75 $this->addElement('checkbox', 'makeMultilingual', ts('Enable Multiple Languages'),
76 NULL, ['onChange' => "if (this.checked) CRM.alert($warning, $warningTitle)"]
77 );
78 }
79 }
80 $this->addElement('select', 'contact_default_language', ts('Default Language for users'),
81 CRM_Admin_Form_Setting_Localization::getDefaultLanguageOptions());
82
83 $includeCurrency = &$this->addElement('advmultiselect', 'currencyLimit',
84 ts('Available Currencies') . ' ', self::getCurrencySymbols(),
85 [
86 'size' => 5,
87 'style' => 'width:150px',
88 'class' => 'advmultiselect',
89 ]
90 );
91
92 $includeCurrency->setButtonAttributes('add', ['value' => ts('Add >>')]);
93 $includeCurrency->setButtonAttributes('remove', ['value' => ts('<< Remove')]);
94
95 $this->addFormRule(['CRM_Admin_Form_Setting_Localization', 'formRule']);
96
97 parent::buildQuickForm();
98 }
99
100 /**
101 * @param $fields
102 *
103 * @return array|bool
104 */
105 public static function formRule($fields) {
106 $errors = [];
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
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']) &&
129 (!empty($fields['countryLimit']) &&
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.');
134 }
135
136 return empty($errors) ? TRUE : $errors;
137 }
138
139 /**
140 * Set the default values for the form.
141 *
142 * @return array
143 */
144 public function setDefaultValues() {
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
151 $this->_defaults['languageLimit'] = Civi::settings()->get('languageLimit');
152
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
162 //cache contact fields retaining localized titles
163 //though we changed localization, so reseting cache.
164 Civi::cache('fields')->flush();
165
166 //CRM-8559, cache navigation do not respect locale if it is changed, so reseting cache.
167 Civi::cache('navigation')->flush();
168 // reset ACL and System caches
169 CRM_Core_BAO_Cache::resetCaches();
170
171 // we do this only to initialize monetary decimal point and thousand separator
172 $config = CRM_Core_Config::singleton();
173
174 // save enabled currencies and default currency in option group 'currencies_enabled'
175 // CRM-1496
176 if (empty($values['currencyLimit'])) {
177 $values['currencyLimit'] = [$values['defaultCurrency']];
178 }
179 elseif (!in_array($values['defaultCurrency'], $values['currencyLimit'])) {
180 $values['currencyLimit'][] = $values['defaultCurrency'];
181 }
182
183 self::updateEnabledCurrencies($values['currencyLimit'], $values['defaultCurrency']);
184
185 // unset currencyLimit so we dont store there
186 unset($values['currencyLimit']);
187
188 // make the site multi-lang if requested
189 if (!empty($values['makeMultilingual'])) {
190 CRM_Core_I18n_Schema::makeMultilingual($values['lcMessages']);
191 $values['languageLimit'][$values['lcMessages']] = 1;
192 // make the site single-lang if requested
193 }
194 elseif (!empty($values['makeSinglelingual'])) {
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
200 if (empty($values['makeSinglelingual']) && !empty($values['addLanguage'])) {
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
209 // current language should be in the ui list
210 if (!in_array($values['lcMessages'], $values['uiLanguages'])) {
211 $values['uiLanguages'][] = $values['lcMessages'];
212 }
213
214 // if we manipulated the language list, return to the localization admin screen
215 $return = (bool) (CRM_Utils_Array::value('makeMultilingual', $values) or CRM_Utils_Array::value('addLanguage', $values));
216
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
225 // save all the settings
226 parent::commonProcess($filteredValues);
227
228 if ($return) {
229 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/setting/localization', 'reset=1'));
230 }
231 }
232
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
245 $options = [];
246
247 $currencySymbols = CRM_Admin_Form_Setting_Localization::getCurrencySymbols();
248 for ($i = 0; $i < count($currencies); $i++) {
249 $options[] = [
250 'label' => $currencySymbols[$currencies[$i]],
251 'value' => $currencies[$i],
252 'weight' => $i + 1,
253 'is_active' => 1,
254 'is_default' => $currencies[$i] == $default,
255 ];
256 }
257
258 $dontCare = NULL;
259 CRM_Core_OptionGroup::createAssoc('currencies_enabled', $options, $dontCare);
260
261 }
262
263 /**
264 * @return array
265 */
266 public static function getAvailableCountries() {
267 $i18n = CRM_Core_I18n::singleton();
268 $country = [];
269 CRM_Core_PseudoConstant::populate($country, 'CRM_Core_DAO_Country', TRUE, 'name', 'is_active');
270 $i18n->localizeArray($country, ['context' => 'country']);
271 asort($country);
272 return $country;
273 }
274
275 /**
276 * Get the default locale options.
277 *
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
286 $defaultLocaleOptions = [];
287 foreach ($locales as $loc => $lang) {
288 if (substr_count($domain->locales, $loc)) {
289 $defaultLocaleOptions[$loc] = $lang;
290 }
291 }
292 }
293 else {
294 $defaultLocaleOptions = $locales;
295 }
296 return $defaultLocaleOptions;
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() {
306 $symbols = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'currency', [
307 'labelColumn' => 'symbol',
308 'orderColumn' => TRUE,
309 ]);
310 $_currencySymbols = [];
311 foreach ($symbols as $key => $value) {
312 $_currencySymbols[$key] = "$key";
313 if ($value) {
314 $_currencySymbols[$key] .= " ($value)";
315 }
316 }
317 return $_currencySymbols;
318 }
319
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 */
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
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)) {
354 $currencies = [$values['defaultCurrency']];
355 }
356 else {
357 $currencies[] = $newCurrency;
358 }
359
360 CRM_Admin_Form_Setting_Localization::updateEnabledCurrencies($currencies, $newCurrency);
361 }
362
363 }
364
365 /**
366 * @return array
367 */
368 public static function getDefaultLanguageOptions() {
369 return [
370 '*default*' => ts('Use default site language'),
371 'undefined' => ts('Leave undefined'),
372 'current_site_language' => ts('Use language in use at the time'),
373 ];
374 }
375
376}