Merge pull request #13134 from agileware/CIVICRM-1006
[civicrm-core.git] / CRM / Admin / Form / SettingTrait.php
CommitLineData
946389fb 1<?php
2/*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
946389fb 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 * @package CRM
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
946389fb 32 */
33
34/**
35 * This trait allows us to consolidate Preferences & Settings forms.
36 *
37 * It is intended mostly as part of a refactoring process to get rid of having 2.
38 */
39trait CRM_Admin_Form_SettingTrait {
40
51f35276
FG
41 /**
42 * The setting page filter.
43 *
44 * @var string
45 */
46 private $_filter;
47
946389fb 48 /**
49 * @var array
50 */
51 protected $settingsMetadata;
52
53 /**
54 * Get default entity.
55 *
56 * @return string
57 */
58 public function getDefaultEntity() {
59 return 'Setting';
60 }
61
62 /**
63 * Get the metadata relating to the settings on the form, ordered by the keys in $this->_settings.
64 *
65 * @return array
66 */
67 protected function getSettingsMetaData() {
68 if (empty($this->settingsMetadata)) {
69 $allSettingMetaData = civicrm_api3('setting', 'getfields', []);
70 $this->settingsMetadata = array_intersect_key($allSettingMetaData['values'], $this->_settings);
71 // This array_merge re-orders to the key order of $this->_settings.
72 $this->settingsMetadata = array_merge($this->_settings, $this->settingsMetadata);
73 }
74 return $this->settingsMetadata;
75 }
76
77 /**
78 * Get the settings which can be stored based on metadata.
79 *
80 * @param array $params
81 * @return array
82 */
83 protected function getSettingsToSetByMetadata($params) {
74f89a9f 84 $setValues = array_intersect_key($params, $this->_settings);
85 // Checkboxes will be unset rather than empty so we need to add them back in.
86 // Handle quickform hateability just once, right here right now.
87 $unsetValues = array_diff_key($this->_settings, $params);
88 foreach ($unsetValues as $key => $unsetValue) {
89 if ($this->getQuickFormType($this->getSettingMetadata($key)) === 'CheckBox') {
90 $setValues[$key] = [$key => 0];
91 }
92 }
93 return $setValues;
946389fb 94 }
95
96 /**
97 * @param $params
98 */
99 protected function filterParamsSetByMetadata(&$params) {
100 foreach ($this->getSettingsToSetByMetadata($params) as $setting => $settingGroup) {
101 //@todo array_diff this
102 unset($params[$setting]);
103 }
104 }
105
6821aa1d 106 /**
107 * Get the metadata for a particular field.
108 *
109 * @param $setting
110 * @return mixed
111 */
112 protected function getSettingMetadata($setting) {
113 return $this->getSettingsMetaData()[$setting];
114 }
115
116 /**
117 * Get the metadata for a particular field for a particular item.
118 *
119 * e.g get 'serialize' key, if exists, for a field.
120 *
121 * @param $setting
122 * @return mixed
123 */
124 protected function getSettingMetadataItem($setting, $item) {
125 return CRM_Utils_Array::value($item, $this->getSettingsMetaData()[$setting]);
126 }
127
51f35276
FG
128 /**
129 * @return string
130 */
131 protected function getSettingPageFilter() {
132 if (!isset($this->_filter)) {
133 // Get the last URL component without modifying the urlPath property.
134 $urlPath = array_values($this->urlPath);
135 $this->_filter = end($urlPath);
136 }
137 return $this->_filter;
138 }
139
140 /**
141 * Returns a re-keyed copy of the settings, ordered by weight.
142 *
143 * @return array
144 */
145 protected function getSettingsOrderedByWeight() {
146 $settingMetaData = $this->getSettingsMetaData();
147 $filter = $this->getSettingPageFilter();
148
149 usort($settingMetaData, function ($a, $b) use ($filter) {
150 // Handle cases in which a comparison is impossible. Such will be considered ties.
151 if (
152 // A comparison can't be made unless both setting weights are declared.
153 !isset($a['settings_pages'][$filter]['weight'], $b['settings_pages'][$filter]['weight'])
154 // A pair of settings might actually have the same weight.
155 || $a['settings_pages'][$filter]['weight'] === $b['settings_pages'][$filter]['weight']
156 ) {
157 return 0;
158 }
159
160 return $a['settings_pages'][$filter]['weight'] > $b['settings_pages'][$filter]['weight'] ? 1 : -1;
161 });
162
163 return $settingMetaData;
164 }
165
e894ae15 166 /**
167 * Add fields in the metadata to the template.
168 */
169 protected function addFieldsDefinedInSettingsMetadata() {
170 $settingMetaData = $this->getSettingsMetaData();
171 $descriptions = [];
172 foreach ($settingMetaData as $setting => $props) {
c5af8245 173 $quickFormType = $this->getQuickFormType($props);
174 if (isset($quickFormType)) {
719eda4a 175 $options = CRM_Utils_Array::value('options', $props);
e894ae15 176 if (isset($props['pseudoconstant'])) {
177 $options = civicrm_api3('Setting', 'getoptions', [
178 'field' => $setting,
c89a43b3 179 ])['values'];
e894ae15 180 }
c89a43b3 181 if ($props['type'] === 'Boolean') {
182 $options = [$props['title'] => $props['name']];
e894ae15 183 }
c89a43b3 184
e894ae15 185 //Load input as readonly whose values are overridden in civicrm.settings.php.
186 if (Civi::settings()->getMandatory($setting)) {
187 $props['html_attributes']['readonly'] = TRUE;
188 $this->includesReadOnlyFields = TRUE;
189 }
190
c5af8245 191 $add = 'add' . $quickFormType;
e894ae15 192 if ($add == 'addElement') {
193 $this->$add(
194 $props['html_type'],
195 $setting,
196 ts($props['title']),
c89a43b3 197 ($options !== NULL) ? $options : CRM_Utils_Array::value('html_attributes', $props, []),
e894ae15 198 ($options !== NULL) ? CRM_Utils_Array::value('html_attributes', $props, []) : NULL
199 );
200 }
201 elseif ($add == 'addSelect') {
c89a43b3 202 $this->addElement('select', $setting, ts($props['title']), $options, CRM_Utils_Array::value('html_attributes', $props));
e894ae15 203 }
204 elseif ($add == 'addCheckBox') {
969afb18 205 $this->addCheckBox($setting, '', $options, NULL, CRM_Utils_Array::value('html_attributes', $props), NULL, NULL, ['&nbsp;&nbsp;']);
e894ae15 206 }
a55c9b35 207 elseif ($add == 'addCheckBoxes') {
c89a43b3 208 $options = array_flip($options);
a55c9b35 209 $newOptions = [];
210 foreach ($options as $key => $val) {
211 $newOptions[$key] = $val;
212 }
213 $this->addCheckBox($setting,
214 $props['title'],
215 $newOptions,
216 NULL, NULL, NULL, NULL,
217 ['&nbsp;&nbsp;', '&nbsp;&nbsp;', '<br/>']
218 );
219 }
e894ae15 220 elseif ($add == 'addChainSelect') {
221 $this->addChainSelect($setting, [
222 'label' => ts($props['title']),
223 ]);
224 }
225 elseif ($add == 'addMonthDay') {
226 $this->add('date', $setting, ts($props['title']), CRM_Core_SelectValues::date(NULL, 'M d'));
227 }
eba92929 228 elseif ($add === 'addEntityRef') {
229 $this->$add($setting, ts($props['title']), $props['entity_reference_options']);
230 }
c7cd4e2c 231 elseif ($add === 'addYesNo' && ($props['type'] === 'Boolean')) {
232 $this->addRadio($setting, ts($props['title']), array(1 => 'Yes', 0 => 'No'), NULL, '&nbsp;&nbsp;');
233 }
e894ae15 234 else {
c89a43b3 235 $this->$add($setting, ts($props['title']), $options);
e894ae15 236 }
237 // Migrate to using an array as easier in smart...
f8857611 238 $description = CRM_Utils_Array::value('description', $props);
239 $descriptions[$setting] = $description;
240 $this->assign("{$setting}_description", $description);
e894ae15 241 if ($setting == 'max_attachments') {
242 //temp hack @todo fix to get from metadata
243 $this->addRule('max_attachments', ts('Value should be a positive number'), 'positiveInteger');
244 }
245 if ($setting == 'maxFileSize') {
246 //temp hack
247 $this->addRule('maxFileSize', ts('Value should be a positive number'), 'positiveInteger');
248 }
249
250 }
251 }
252 // setting_description should be deprecated - see Mail.tpl for metadata based tpl.
253 $this->assign('setting_descriptions', $descriptions);
254 $this->assign('settings_fields', $settingMetaData);
51f35276 255 $this->assign('fields', $this->getSettingsOrderedByWeight());
e894ae15 256 }
257
c5af8245 258 /**
259 * Get the quickform type for the given html type.
260 *
261 * @param array $spec
262 *
263 * @return string
264 */
265 protected function getQuickFormType($spec) {
0e700ee7 266 if (isset($spec['quick_form_type']) &&
267 !($spec['quick_form_type'] === 'Element' && !empty($spec['html_type']))) {
5c33bd6b 268 // This is kinda transitional
c5af8245 269 return $spec['quick_form_type'];
270 }
5c33bd6b 271
272 // The spec for settings has been updated for consistency - we provide deprecation notices for sites that have
273 // not made this change.
274 $htmlType = $spec['html_type'];
275 if ($htmlType !== strtolower($htmlType)) {
276 CRM_Core_Error::deprecatedFunctionWarning(ts('Settings fields html_type should be lower case - see https://docs.civicrm.org/dev/en/latest/framework/setting/ - this needs to be fixed for ' . $spec['name']));
277 $htmlType = strtolower($spec['html_type']);
278 }
c5af8245 279 $mapping = [
280 'checkboxes' => 'CheckBoxes',
c89a43b3 281 'checkbox' => 'CheckBox',
c5af8245 282 'radio' => 'Radio',
b70c6629 283 'select' => 'Select',
7399a0a6 284 'textarea' => 'Element',
a7e15692 285 'text' => 'Element',
eba92929 286 'entity_reference' => 'EntityRef',
5c33bd6b 287 'advmultiselect' => 'Element',
c5af8245 288 ];
5c33bd6b 289 return $mapping[$htmlType];
c5af8245 290 }
601361a3 291 /**
292 * Get the defaults for all fields defined in the metadata.
293 *
294 * All others are pending conversion.
295 */
296 protected function setDefaultsForMetadataDefinedFields() {
297 CRM_Core_BAO_ConfigSetting::retrieve($this->_defaults);
6821aa1d 298 foreach (array_keys($this->_settings) as $setting) {
601361a3 299 $this->_defaults[$setting] = civicrm_api3('setting', 'getvalue', ['name' => $setting]);
6821aa1d 300 $spec = $this->getSettingsMetadata()[$setting];
301 if (!empty($spec['serialize'])) {
302 $this->_defaults[$setting] = CRM_Core_DAO::unSerializeField($this->_defaults[$setting], $spec['serialize']);
303 }
c5af8245 304 if ($this->getQuickFormType($spec) === 'CheckBoxes') {
6821aa1d 305 $this->_defaults[$setting] = array_fill_keys($this->_defaults[$setting], 1);
306 }
74f89a9f 307 if ($this->getQuickFormType($spec) === 'CheckBox') {
308 $this->_defaults[$setting] = [$setting => $this->_defaults[$setting]];
309 }
6821aa1d 310 }
311 }
312
313 /**
314 * @param $params
315 *
316 */
317 protected function saveMetadataDefinedSettings($params) {
318 $settings = $this->getSettingsToSetByMetadata($params);
319 foreach ($settings as $setting => $settingValue) {
2e94f477 320 if ($this->getQuickFormType($this->getSettingMetadata($setting)) === 'CheckBoxes') {
6821aa1d 321 $settings[$setting] = array_keys($settingValue);
322 }
74f89a9f 323 if ($this->getQuickFormType($this->getSettingMetadata($setting)) === 'CheckBox') {
324 // This will be an array with one value.
325 $settings[$setting] = (int) reset($settings[$setting]);
326 }
601361a3 327 }
6821aa1d 328 civicrm_api3('setting', 'create', $settings);
601361a3 329 }
330
946389fb 331}