Merge pull request #18962 from colemanw/dashboardPerms
[civicrm-core.git] / CRM / Custom / Form / Option.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/**
19 * form to process actions on the field aspect of Custom
20 */
21class CRM_Custom_Form_Option extends CRM_Core_Form {
22
23 /**
100fef9d 24 * The custom field id saved to the session for an update
6a488035
TO
25 *
26 * @var int
6a488035
TO
27 */
28 protected $_fid;
29
30 /**
100fef9d 31 * The custom group id saved to the session for an update
6a488035
TO
32 *
33 * @var int
6a488035
TO
34 */
35 protected $_gid;
36
37 /**
38 * The option group ID
518fa0ee 39 * @var int
6a488035
TO
40 */
41 protected $_optionGroupID = NULL;
42
43 /**
44 * The Option id, used when editing the Option
45 *
46 * @var int
6a488035
TO
47 */
48 protected $_id;
49
50 /**
fe482240 51 * Set variables up before form is built.
6a488035 52 *
6a488035 53 * @return void
6a488035
TO
54 */
55 public function preProcess() {
56 $this->_fid = CRM_Utils_Request::retrieve('fid', 'Positive', $this);
57
58 $this->_gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this);
d06700a7 59
6a488035 60 if (!isset($this->_gid) && $this->_fid) {
7688c6be
DL
61 $this->_gid = CRM_Core_DAO::getFieldValue(
62 'CRM_Core_DAO_CustomField',
6a488035
TO
63 $this->_fid,
64 'custom_group_id'
65 );
66 }
7688c6be 67
6a488035 68 if ($this->_fid) {
7688c6be
DL
69 $this->_optionGroupID = CRM_Core_DAO::getFieldValue(
70 'CRM_Core_DAO_CustomField',
6a488035
TO
71 $this->_fid,
72 'option_group_id'
73 );
74 }
75
7688c6be 76 if ($isReserved = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_gid, 'is_reserved', 'id')) {
79e11805 77 CRM_Core_Error::statusBounce("You cannot add or edit muliple choice options in a reserved custom field-set.");
7688c6be
DL
78 }
79
6a488035
TO
80 $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this);
81 }
82
83 /**
c490a46a 84 * Set default values for the form. Note that in edit/view mode
6a488035
TO
85 * the default values are retrieved from the database
86 *
a6c01b45
CW
87 * @return array
88 * array of default values
6a488035 89 */
00be9182 90 public function setDefaultValues() {
be2fb01f 91 $defaults = $fieldDefaults = [];
6a488035 92 if (isset($this->_id)) {
be2fb01f 93 $params = ['id' => $this->_id];
6a488035
TO
94 CRM_Core_BAO_CustomOption::retrieve($params, $defaults);
95
be2fb01f 96 $paramsField = ['id' => $this->_fid];
6a488035 97 CRM_Core_BAO_CustomField::retrieve($paramsField, $fieldDefaults);
6a488035 98 if ($fieldDefaults['html_type'] == 'CheckBox'
114e8771
AF
99 // Multi-Select
100 || ($fieldDefaults['html_type'] == 'Select' && $fieldDefaults['serialize'] == 1)
6a488035 101 ) {
a7488080 102 if (!empty($fieldDefaults['default_value'])) {
6a488035
TO
103 $defaultCheckValues = explode(CRM_Core_DAO::VALUE_SEPARATOR,
104 substr($fieldDefaults['default_value'], 1, -1)
105 );
106 if (in_array($defaults['value'], $defaultCheckValues)) {
107 $defaults['default_value'] = 1;
108 }
109 }
110 }
111 else {
112 if (CRM_Utils_Array::value('default_value', $fieldDefaults) == CRM_Utils_Array::value('value', $defaults)) {
113 $defaults['default_value'] = 1;
114 }
115 }
116 }
117 else {
118 $defaults['is_active'] = 1;
119 }
120
121 if ($this->_action & CRM_Core_Action::ADD) {
be2fb01f 122 $fieldValues = ['option_group_id' => $this->_optionGroupID];
6a488035
TO
123 $defaults['weight'] = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_OptionValue', $fieldValues);
124 }
125
126 return $defaults;
127 }
128
129 /**
fe482240 130 * Build the form object.
6a488035 131 *
6a488035 132 * @return void
6a488035
TO
133 */
134 public function buildQuickForm() {
135 if ($this->_action == CRM_Core_Action::DELETE) {
be2fb01f 136 $option = civicrm_api3('option_value', 'getsingle', ['id' => $this->_id]);
c31db174 137 $this->assign('label', $option['label']);
be2fb01f 138 $this->addButtons([
518fa0ee
SL
139 [
140 'type' => 'next',
141 'name' => ts('Delete'),
142 'isDefault' => TRUE,
143 ],
144 [
145 'type' => 'cancel',
146 'name' => ts('Cancel'),
147 ],
148 ]);
6a488035
TO
149 }
150 else {
151 // lets trim all the whitespace
152 $this->applyFilter('__ALL__', 'trim');
153
154 // hidden Option Id for validation use
155 $this->add('hidden', 'optionId', $this->_id);
156
157 //hidden field ID for validation use
158 $this->add('hidden', 'fieldId', $this->_fid);
159
160 // label
161 $this->add('text', 'label', ts('Option Label'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'label'), TRUE);
162
163 $this->add('text', 'value', ts('Option Value'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'value'), TRUE);
164
4247b886 165 $this->add('textarea', 'description', ts('Description'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'description'));
6a488035 166 // weight
f20d2b0d 167 $this->add('number', 'weight', ts('Order'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue', 'weight'), TRUE);
6a488035
TO
168 $this->addRule('weight', ts('is a numeric field'), 'numeric');
169
170 // is active ?
171 $this->add('checkbox', 'is_active', ts('Active?'));
172
173 // Set the default value for Custom Field
174 $this->add('checkbox', 'default_value', ts('Default'));
175
176 // add a custom form rule
be2fb01f 177 $this->addFormRule(['CRM_Custom_Form_Option', 'formRule'], $this);
6a488035
TO
178
179 // add buttons
be2fb01f 180 $this->addButtons([
518fa0ee
SL
181 [
182 'type' => 'next',
183 'name' => ts('Save'),
184 'isDefault' => TRUE,
185 ],
186 [
187 'type' => 'next',
188 'name' => ts('Save and New'),
189 'subName' => 'new',
190 ],
191 [
192 'type' => 'cancel',
193 'name' => ts('Cancel'),
194 ],
195 ]);
6a488035 196
6a488035
TO
197 // if view mode pls freeze it with the done button.
198 if ($this->_action & CRM_Core_Action::VIEW) {
199 $this->freeze();
200 $url = CRM_Utils_System::url('civicrm/admin/custom/group/field/option',
201 'reset=1&action=browse&fid=' . $this->_fid . '&gid=' . $this->_gid,
202 TRUE, NULL, FALSE
203 );
cbd83dde 204 $this->addElement('xbutton',
6a488035 205 'done',
98da7d67 206 CRM_Core_Page::crmIcon('fa-times') . ' ' . ts('Done'),
cbd83dde
AH
207 [
208 'type' => 'button',
209 'onclick' => "location.href='$url'",
210 'class' => 'crm-form-submit cancel',
cbd83dde 211 ]
6a488035
TO
212 );
213 }
214 }
215 $this->assign('id', $this->_id);
216 }
217
218 /**
fe482240 219 * Global validation rules for the form.
6a488035 220 *
c4ca4892
TO
221 * @param array $fields
222 * Posted values of the form.
6a488035 223 *
fd31fa4c 224 * @param $files
c490a46a 225 * @param CRM_Core_Form $form
fd31fa4c 226 *
a6c01b45
CW
227 * @return array
228 * list of errors to be posted back to the form
6a488035 229 */
00be9182 230 public static function formRule($fields, $files, $form) {
353ffa53
TO
231 $optionLabel = $fields['label'];
232 $optionValue = $fields['value'];
233 $fieldId = $form->_fid;
6a488035
TO
234 $optionGroupId = $form->_optionGroupID;
235
be2fb01f 236 $temp = [];
6a488035
TO
237 if (empty($form->_id)) {
238 $query = "
361fb6c0 239SELECT count(*)
6a488035
TO
240 FROM civicrm_option_value
241 WHERE option_group_id = %1
242 AND label = %2";
be2fb01f
CW
243 $params = [
244 1 => [$optionGroupId, 'Integer'],
245 2 => [$optionLabel, 'String'],
246 ];
6a488035
TO
247 if (CRM_Core_DAO::singleValueQuery($query, $params) > 0) {
248 $errors['label'] = ts('There is an entry with the same label.');
249 }
250
251 $query = "
361fb6c0 252SELECT count(*)
6a488035
TO
253 FROM civicrm_option_value
254 WHERE option_group_id = %1
255 AND value = %2";
be2fb01f
CW
256 $params = [
257 1 => [$optionGroupId, 'Integer'],
258 2 => [$optionValue, 'String'],
259 ];
6a488035
TO
260 if (CRM_Core_DAO::singleValueQuery($query, $params) > 0) {
261 $errors['value'] = ts('There is an entry with the same value.');
262 }
263 }
264 else {
265 //capture duplicate entries while updating Custom Options
266 $optionId = CRM_Utils_Type::escape($fields['optionId'], 'Integer');
267
268 //check label duplicates within a custom field
269 $query = "
361fb6c0 270SELECT count(*)
6a488035
TO
271 FROM civicrm_option_value
272 WHERE option_group_id = %1
273 AND id != %2
274 AND label = %3";
be2fb01f
CW
275 $params = [
276 1 => [$optionGroupId, 'Integer'],
277 2 => [$optionId, 'Integer'],
278 3 => [$optionLabel, 'String'],
279 ];
6a488035
TO
280 if (CRM_Core_DAO::singleValueQuery($query, $params) > 0) {
281 $errors['label'] = ts('There is an entry with the same label.');
282 }
283
284 //check value duplicates within a custom field
285 $query = "
361fb6c0 286SELECT count(*)
6a488035
TO
287 FROM civicrm_option_value
288 WHERE option_group_id = %1
289 AND id != %2
290 AND value = %3";
be2fb01f
CW
291 $params = [
292 1 => [$optionGroupId, 'Integer'],
293 2 => [$optionId, 'Integer'],
294 3 => [$optionValue, 'String'],
295 ];
6a488035
TO
296 if (CRM_Core_DAO::singleValueQuery($query, $params) > 0) {
297 $errors['value'] = ts('There is an entry with the same value.');
298 }
299 }
300
301 $query = "
361fb6c0 302SELECT data_type
6a488035
TO
303 FROM civicrm_custom_field
304 WHERE id = %1";
be2fb01f 305 $params = [1 => [$fieldId, 'Integer']];
6a488035
TO
306 $dao = CRM_Core_DAO::executeQuery($query, $params);
307 if ($dao->fetch()) {
308 switch ($dao->data_type) {
309 case 'Int':
310 if (!CRM_Utils_Rule::integer($fields["value"])) {
311 $errors['value'] = ts('Please enter a valid integer value.');
312 }
313 break;
314
315 case 'Float':
316 // case 'Money':
317 if (!CRM_Utils_Rule::numeric($fields["value"])) {
5ab78070 318 $errors['value'] = ts('Please enter a valid number.');
6a488035
TO
319 }
320 break;
321
322 case 'Money':
323 if (!CRM_Utils_Rule::money($fields["value"])) {
324 $errors['value'] = ts('Please enter a valid value.');
325 }
326 break;
327
328 case 'Date':
329 if (!CRM_Utils_Rule::date($fields["value"])) {
330 $errors['value'] = ts('Please enter a valid date using YYYY-MM-DD format. Example: 2004-12-31.');
331 }
332 break;
333
334 case 'Boolean':
335 if (!CRM_Utils_Rule::integer($fields["value"]) &&
336 ($fields["value"] != '1' || $fields["value"] != '0')
337 ) {
338 $errors['value'] = ts('Please enter 1 or 0 as value.');
339 }
340 break;
341
342 case 'Country':
343 if (!empty($fields["value"])) {
be2fb01f 344 $params = [1 => [$fields['value'], 'String']];
6a488035
TO
345 $query = "SELECT count(*) FROM civicrm_country WHERE name = %1 OR iso_code = %1";
346 if (CRM_Core_DAO::singleValueQuery($query, $params) <= 0) {
347 $errors['value'] = ts('Invalid default value for country.');
348 }
349 }
350 break;
351
352 case 'StateProvince':
353 if (!empty($fields["value"])) {
be2fb01f 354 $params = [1 => [$fields['value'], 'String']];
6a488035 355 $query = "
361fb6c0 356SELECT count(*)
6a488035
TO
357 FROM civicrm_state_province
358 WHERE name = %1
359 OR abbreviation = %1";
360 if (CRM_Core_DAO::singleValueQuery($query, $params) <= 0) {
361 $errors['value'] = ts('The invalid value for State/Province data type');
362 }
363 }
364 break;
365 }
366 }
367
368 return empty($errors) ? TRUE : $errors;
369 }
370
371 /**
fe482240 372 * Process the form.
6a488035 373 *
6a488035 374 * @return void
6a488035
TO
375 */
376 public function postProcess() {
377 // store the submitted values in an array
378 $params = $this->controller->exportValues('Option');
379
380 if ($this->_action == CRM_Core_Action::DELETE) {
be2fb01f
CW
381 $option = civicrm_api3('option_value', 'getsingle', ['id' => $this->_id]);
382 $fieldValues = ['option_group_id' => $this->_optionGroupID];
c31db174 383 CRM_Utils_Weight::delWeight('CRM_Core_DAO_OptionValue', $this->_id, $fieldValues);
6a488035 384 CRM_Core_BAO_CustomOption::del($this->_id);
be2fb01f 385 CRM_Core_Session::setStatus(ts('Option "%1" has been deleted.', [1 => $option['label']]), ts('Deleted'), 'success');
6a488035
TO
386 return;
387 }
388
389 // set values for custom field properties and save
390 $customOption = new CRM_Core_DAO_OptionValue();
391 $customOption->label = $params['label'];
6a488035 392 $customOption->weight = $params['weight'];
4247b886 393 $customOption->description = $params['description'];
6a488035 394 $customOption->value = $params['value'];
2667017c 395 $customOption->is_active = $params['is_active'] ?? FALSE;
6a488035
TO
396
397 $oldWeight = NULL;
398 if ($this->_id) {
399 $customOption->id = $this->_id;
400 CRM_Core_BAO_CustomOption::updateCustomValues($params);
401 $oldWeight = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id, 'weight', 'id');
402 }
2667017c 403 else {
404 $customOption->name = CRM_Utils_String::titleToVar($params['label']);
405 }
6a488035 406
be2fb01f 407 $fieldValues = ['option_group_id' => $this->_optionGroupID];
7c550ca0
WA
408 $customOption->weight
409 = CRM_Utils_Weight::updateOtherWeights(
361fb6c0
DL
410 'CRM_Core_DAO_OptionValue',
411 $oldWeight,
412 $params['weight'],
413 $fieldValues);
6a488035
TO
414
415 $customOption->option_group_id = $this->_optionGroupID;
416
417 $customField = new CRM_Core_DAO_CustomField();
418 $customField->id = $this->_fid;
361fb6c0
DL
419 if (
420 $customField->find(TRUE) &&
421 (
422 $customField->html_type == 'CheckBox' ||
114e8771
AF
423 // Multi Value Select
424 ($customField->html_type == 'Select' && $customField->serialize == 1)
6a488035
TO
425 )
426 ) {
361fb6c0
DL
427 $defVal = explode(
428 CRM_Core_DAO::VALUE_SEPARATOR,
6a488035
TO
429 substr($customField->default_value, 1, -1)
430 );
a7488080 431 if (!empty($params['default_value'])) {
6a488035
TO
432 if (!in_array($customOption->value, $defVal)) {
433 if (empty($defVal[0])) {
be2fb01f 434 $defVal = [$customOption->value];
6a488035
TO
435 }
436 else {
437 $defVal[] = $customOption->value;
438 }
7c550ca0
WA
439 $customField->default_value
440 = CRM_Core_DAO::VALUE_SEPARATOR .
361fb6c0
DL
441 implode(CRM_Core_DAO::VALUE_SEPARATOR, $defVal) .
442 CRM_Core_DAO::VALUE_SEPARATOR;
6a488035
TO
443 $customField->save();
444 }
445 }
446 elseif (in_array($customOption->value, $defVal)) {
be2fb01f 447 $tempVal = [];
6a488035
TO
448 foreach ($defVal as $v) {
449 if ($v != $customOption->value) {
450 $tempVal[] = $v;
451 }
452 }
453
7c550ca0
WA
454 $customField->default_value
455 = CRM_Core_DAO::VALUE_SEPARATOR .
361fb6c0
DL
456 implode(CRM_Core_DAO::VALUE_SEPARATOR, $tempVal) .
457 CRM_Core_DAO::VALUE_SEPARATOR;
6a488035
TO
458 $customField->save();
459 }
460 }
461 else {
462 switch ($customField->data_type) {
463 case 'Money':
464 $customOption->value = CRM_Utils_Rule::cleanMoney($customOption->value);
465 break;
466
467 case 'Int':
468 $customOption->value = intval($customOption->value);
469 break;
470
471 case 'Float':
472 $customOption->value = floatval($customOption->value);
473 break;
474 }
475
a7488080 476 if (!empty($params['default_value'])) {
6a488035
TO
477 $customField->default_value = $customOption->value;
478 $customField->save();
479 }
480 elseif ($customField->find(TRUE) && $customField->default_value == $customOption->value) {
481 // this is the case where this option is the current default value and we have been reset
482 $customField->default_value = 'null';
483 $customField->save();
484 }
485 }
486
47f4a3ba 487 CRM_Core_BAO_CustomOption::updateValue($customOption->id, $customOption->value);
6a488035
TO
488 $customOption->save();
489
be2fb01f 490 $msg = ts('Your multiple choice option \'%1\' has been saved', [1 => $customOption->label]);
361fb6c0 491 CRM_Core_Session::setStatus($msg, '', 'success');
6a488035
TO
492 $buttonName = $this->controller->getButtonName();
493 $session = CRM_Core_Session::singleton();
494 if ($buttonName == $this->getButtonName('next', 'new')) {
361fb6c0
DL
495 CRM_Core_Session::setStatus(ts('You can add another option.'), '', 'info');
496 $session->replaceUserContext(
497 CRM_Utils_System::url(
498 'civicrm/admin/custom/group/field/option',
6a488035 499 'reset=1&action=add&fid=' . $this->_fid . '&gid=' . $this->_gid
361fb6c0
DL
500 )
501 );
6a488035
TO
502 }
503 }
96025800 504
6a488035 505}