Merge pull request #4607 from samuelsov/CRM-15637
[civicrm-core.git] / CRM / Custom / Form / Field.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
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 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35
36 /**
37 * form to process actions on the field aspect of Custom
38 */
39 class CRM_Custom_Form_Field extends CRM_Core_Form {
40
41 /**
42 * Constants for number of options for data types of multiple option.
43 */
44 const NUM_OPTION = 11;
45
46 /**
47 * The custom group id saved to the session for an update
48 *
49 * @var int
50 */
51 protected $_gid;
52
53 /**
54 * The field id, used when editing the field
55 *
56 * @var int
57 */
58 protected $_id;
59
60 /**
61 * The default custom data/input types, when editing the field
62 *
63 * @var array
64 */
65 protected $_defaultDataType;
66
67 /**
68 * Array of custom field values if update mode
69 */
70 protected $_values;
71
72 /**
73 * Array for valid combinations of data_type & html_type
74 *
75 * @var array
76 * @static
77 */
78 private static $_dataTypeValues = NULL;
79 private static $_dataTypeKeys = NULL;
80
81 private static $_dataToHTML = NULL;
82
83 private static $_dataToLabels = NULL;
84
85 /**
86 * Set variables up before form is built
87 *
88 * @param null
89 *
90 * @return void
91 */
92 public function preProcess() {
93 if (!(self::$_dataTypeKeys)) {
94 self::$_dataTypeKeys = array_keys(CRM_Core_BAO_CustomField::dataType());
95 self::$_dataTypeValues = array_values(CRM_Core_BAO_CustomField::dataType());
96 }
97
98 if (!self::$_dataToHTML) {
99 self::$_dataToHTML = CRM_Core_BAO_CustomField::dataToHtml();
100 }
101
102 //custom field id
103 $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this);
104
105 $this->_values = array();
106 //get the values form db if update.
107 if ($this->_id) {
108 $params = array('id' => $this->_id);
109 CRM_Core_BAO_CustomField::retrieve($params, $this->_values);
110 // note_length is an alias for the text_length field
111 $this->_values['note_length'] = CRM_Utils_Array::value('text_length', $this->_values);
112 // custom group id
113 $this->_gid = $this->_values['custom_group_id'];
114 }
115 else {
116 // custom group id
117 $this->_gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this);
118 }
119
120 if ($isReserved = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_gid, 'is_reserved', 'id')) {
121 CRM_Core_Error::fatal("You cannot add or edit fields in a reserved custom field-set.");
122 }
123
124 if ($this->_gid) {
125 $url = CRM_Utils_System::url('civicrm/admin/custom/group/field',
126 "reset=1&action=browse&gid={$this->_gid}"
127 );
128
129 $session = CRM_Core_Session::singleton();
130 $session->pushUserContext($url);
131 }
132
133 if (self::$_dataToLabels == NULL) {
134 self::$_dataToLabels = array(
135 array('Text' => ts('Text'), 'Select' => ts('Select'),
136 'Radio' => ts('Radio'), 'CheckBox' => ts('CheckBox'), 'Multi-Select' => ts('Multi-Select'),
137 'AdvMulti-Select' => ts('Adv Multi-Select (obsolete)'),
138 'Autocomplete-Select' => ts('Autocomplete-Select'),
139 ),
140 array('Text' => ts('Text'), 'Select' => ts('Select'),
141 'Radio' => ts('Radio'),
142 ),
143 array('Text' => ts('Text'), 'Select' => ts('Select'),
144 'Radio' => ts('Radio'),
145 ),
146 array('Text' => ts('Text'), 'Select' => ts('Select'),
147 'Radio' => ts('Radio'),
148 ),
149 array('TextArea' => ts('TextArea'), 'RichTextEditor' => ts('Rich Text Editor')),
150 array('Date' => ts('Select Date')),
151 array('Radio' => ts('Radio')),
152 array('StateProvince' => ts('Select State/Province'), 'Multi-Select' => ts('Multi-Select State/Province')),
153 array('Country' => ts('Select Country'), 'Multi-Select' => ts('Multi-Select Country')),
154 array('File' => ts('Select File')),
155 array('Link' => ts('Link')),
156 array('ContactReference' => ts('Autocomplete-Select')),
157 );
158 }
159 }
160
161 /**
162 * Set default values for the form. Note that in edit/view mode
163 * the default values are retrieved from the database
164 *
165 * @param null
166 *
167 * @return array array of default values
168 */
169 public function setDefaultValues() {
170 $defaults = $this->_values;
171
172 if ($this->_id) {
173 $this->assign('id', $this->_id);
174 $this->_gid = $defaults['custom_group_id'];
175
176 //get the value for state or country
177 if ($defaults['data_type'] == 'StateProvince' &&
178 $stateId = CRM_Utils_Array::value('default_value', $defaults)
179 ) {
180 $defaults['default_value'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_StateProvince', $stateId);
181 }
182 elseif ($defaults['data_type'] == 'Country' &&
183 $countryId = CRM_Utils_Array::value('default_value', $defaults)
184 ) {
185 $defaults['default_value'] = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_Country', $countryId);
186 }
187
188 if ($defaults['data_type'] == 'ContactReference' && !empty($defaults['filter'])) {
189 $contactRefFilter = 'Advance';
190 if (strpos($defaults['filter'], 'action=lookup') !== FALSE &&
191 strpos($defaults['filter'], 'group=') !== FALSE
192 ) {
193 $filterParts = explode('&', $defaults['filter']);
194
195 if (count($filterParts) == 2) {
196 $contactRefFilter = 'Group';
197 foreach ($filterParts as $part) {
198 if (strpos($part, 'group=') === FALSE) {
199 continue;
200 }
201 $groups = substr($part, strpos($part, '=') + 1);
202 foreach (explode(',', $groups) as $grp) {
203 if (CRM_Utils_Rule::positiveInteger($grp)) {
204 $defaults['group_id'][] = $grp;
205 }
206 }
207 }
208 }
209 }
210 $defaults['filter_selected'] = $contactRefFilter;
211 }
212
213 if (!empty($defaults['data_type'])) {
214 $defaultDataType = array_search($defaults['data_type'],
215 self::$_dataTypeKeys
216 );
217 $defaultHTMLType = array_search($defaults['html_type'],
218 self::$_dataToHTML[$defaultDataType]
219 );
220 $defaults['data_type'] = array(
221 '0' => $defaultDataType,
222 '1' => $defaultHTMLType,
223 );
224 $this->_defaultDataType = $defaults['data_type'];
225 }
226
227 $defaults['option_type'] = 2;
228
229 $this->assign('changeFieldType', CRM_Custom_Form_ChangeFieldType::fieldTypeTransitions($this->_values['data_type'], $this->_values['html_type']));
230 }
231 else {
232 $defaults['is_active'] = 1;
233 $defaults['option_type'] = 1;
234 }
235
236 // set defaults for weight.
237 for ($i = 1; $i <= self::NUM_OPTION; $i++) {
238 $defaults['option_status[' . $i . ']'] = 1;
239 $defaults['option_weight[' . $i . ']'] = $i;
240 }
241
242 if ($this->_action & CRM_Core_Action::ADD) {
243 $fieldValues = array('custom_group_id' => $this->_gid);
244 $defaults['weight'] = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_CustomField', $fieldValues);
245
246 $defaults['text_length'] = 255;
247 $defaults['note_columns'] = 60;
248 $defaults['note_rows'] = 4;
249 $defaults['is_view'] = 0;
250 }
251
252 if (!empty($defaults['html_type'])) {
253 $dontShowLink = substr($defaults['html_type'], -14) == 'State/Province' || substr($defaults['html_type'], -7) == 'Country' ? 1 : 0;
254 }
255
256 if (isset($dontShowLink)) {
257 $this->assign('dontShowLink', $dontShowLink);
258 }
259 if ($this->_action & CRM_Core_Action::ADD &&
260 CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_gid, 'is_multiple', 'id')) {
261 $defaults['in_selector'] = 1;
262 }
263
264 return $defaults;
265 }
266
267 /**
268 * Build the form object
269 *
270 * @param null
271 *
272 * @return void
273 */
274 public function buildQuickForm() {
275 if ($this->_gid) {
276 $this->_title = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_gid, 'title');
277 CRM_Utils_System::setTitle($this->_title . ' - ' . ($this->_id ? ts('Edit Field') : ts('New Field')));
278 $this->assign('gid', $this->_gid);
279 }
280
281 // lets trim all the whitespace
282 $this->applyFilter('__ALL__', 'trim');
283
284 $attributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_CustomField');
285
286 // label
287 $this->add('text',
288 'label',
289 ts('Field Label'),
290 $attributes['label'],
291 TRUE
292 );
293
294 $dt = &self::$_dataTypeValues;
295 $it = array();
296 foreach ($dt as $key => $value) {
297 $it[$key] = self::$_dataToLabels[$key];
298 }
299 $sel = &$this->addElement('hierselect',
300 'data_type',
301 ts('Data and Input Field Type'),
302 'onclick="clearSearchBoxes();custom_option_html_type(this.form)"; onBlur="custom_option_html_type(this.form)";',
303 '&nbsp;&nbsp;&nbsp;'
304 );
305 $sel->setOptions(array($dt, $it));
306
307 if (CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->_gid, 'is_multiple')) {
308 $this->add('checkbox', 'in_selector', ts('Display in Table?'));
309 }
310
311 if ($this->_action == CRM_Core_Action::UPDATE) {
312 $this->freeze('data_type');
313 }
314 $includeFieldIds = NULL;
315 if ($this->_action == CRM_Core_Action::UPDATE) {
316 $includeFieldIds = $this->_values['id'];
317 }
318 $optionGroups = CRM_Core_BAO_CustomField::customOptionGroup($includeFieldIds);
319 $emptyOptGroup = FALSE;
320 if (empty($optionGroups)) {
321 $emptyOptGroup = TRUE;
322 $optionTypes = array('1' => ts('Create a new set of options'));
323 }
324 else {
325 $optionTypes = array('1' => ts('Create a new set of options'),
326 '2' => ts('Reuse an existing set'),
327 );
328
329 $this->add('select',
330 'option_group_id',
331 ts('Multiple Choice Option Sets'),
332 array(
333 '' => ts('- select -')) + $optionGroups
334 );
335 }
336
337 $element = &$this->addRadio('option_type',
338 ts('Option Type'),
339 $optionTypes,
340 array(
341 'onclick' => "showOptionSelect();"), '<br/>'
342 );
343
344
345 $contactGroups = CRM_Core_PseudoConstant::group();
346 asort($contactGroups);
347
348 $this->add('select',
349 'group_id',
350 ts('Limit List to Group'),
351 $contactGroups,
352 FALSE,
353 array('multiple' => 'multiple')
354 );
355
356 $this->add('text',
357 'filter',
358 ts('Advanced Filter'),
359 $attributes['filter']
360 );
361
362 $this->add('hidden', 'filter_selected', 'Group', array('id' => 'filter_selected'));
363
364 //if empty option group freeze the option type.
365 if ($emptyOptGroup) {
366 $element->freeze();
367 }
368
369 // form fields of Custom Option rows
370 $defaultOption = array();
371 $_showHide = new CRM_Core_ShowHideBlocks('', '');
372 for ($i = 1; $i <= self::NUM_OPTION; $i++) {
373
374 //the show hide blocks
375 $showBlocks = 'optionField_' . $i;
376 if ($i > 2) {
377 $_showHide->addHide($showBlocks);
378 if ($i == self::NUM_OPTION) {
379 $_showHide->addHide('additionalOption');
380 }
381 }
382 else {
383 $_showHide->addShow($showBlocks);
384 }
385
386 $optionAttributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_OptionValue');
387 // label
388 $this->add('text', 'option_label[' . $i . ']', ts('Label'),
389 $optionAttributes['label']
390 );
391
392 // value
393 $this->add('text', 'option_value[' . $i . ']', ts('Value'),
394 $optionAttributes['value']
395 );
396
397 // weight
398 $this->add('text', "option_weight[$i]", ts('Order'),
399 $optionAttributes['weight']
400 );
401
402 // is active ?
403 $this->add('checkbox', "option_status[$i]", ts('Active?'));
404
405 $defaultOption[$i] = $this->createElement('radio', NULL, NULL, NULL, $i);
406
407 //for checkbox handling of default option
408 $this->add('checkbox', "default_checkbox_option[$i]", NULL);
409 }
410
411 //default option selection
412 $this->addGroup($defaultOption, 'default_option');
413
414 $_showHide->addToTemplate();
415
416 // text length for alpha numeric data types
417 $this->add('text',
418 'text_length',
419 ts('Database field length'),
420 $attributes['text_length'],
421 FALSE
422 );
423 $this->addRule('text_length', ts('Value should be a positive number'), 'integer');
424
425 $this->add('text',
426 'start_date_years',
427 ts('Dates may be up to'),
428 $attributes['start_date_years'],
429 FALSE
430 );
431 $this->add('text',
432 'end_date_years',
433 ts('Dates may be up to'),
434 $attributes['end_date_years'],
435 FALSE
436 );
437
438 $this->addRule('start_date_years', ts('Value should be a positive number'), 'integer');
439 $this->addRule('end_date_years', ts('Value should be a positive number'), 'integer');
440
441 $this->add('select', 'date_format', ts('Date Format'),
442 array('' => ts('- select -')) + CRM_Core_SelectValues::getDatePluginInputFormats()
443 );
444
445 $this->add('select', 'time_format', ts('Time'),
446 array('' => ts('- none -')) + CRM_Core_SelectValues::getTimeFormats()
447 );
448
449 // for Note field
450 $this->add('text',
451 'note_columns',
452 ts('Width (columns)') . ' ',
453 $attributes['note_columns'],
454 FALSE
455 );
456 $this->add('text',
457 'note_rows',
458 ts('Height (rows)') . ' ',
459 $attributes['note_rows'],
460 FALSE
461 );
462 $this->add('text',
463 'note_length',
464 ts('Maximum length') . ' ',
465 $attributes['text_length'], // note_length is an alias for the text-length field
466 FALSE
467 );
468
469 $this->addRule('note_columns', ts('Value should be a positive number'), 'positiveInteger');
470 $this->addRule('note_rows', ts('Value should be a positive number'), 'positiveInteger');
471 $this->addRule('note_length', ts('Value should be a positive number'), 'positiveInteger');
472
473 // weight
474 $this->add('text', 'weight', ts('Order'),
475 $attributes['weight'],
476 TRUE
477 );
478 $this->addRule('weight', ts('is a numeric field'), 'numeric');
479
480 // is required ?
481 $this->add('checkbox', 'is_required', ts('Required?'));
482
483 // checkbox / radio options per line
484 $this->add('text', 'options_per_line', ts('Options Per Line'));
485 $this->addRule('options_per_line', ts('must be a numeric value'), 'numeric');
486
487 // default value, help pre, help post, mask, attributes, javascript ?
488 $this->add('text', 'default_value', ts('Default Value'),
489 $attributes['default_value']
490 );
491 $this->add('textarea', 'help_pre', ts('Field Pre Help'),
492 $attributes['help_pre']
493 );
494 $this->add('textarea', 'help_post', ts('Field Post Help'),
495 $attributes['help_post']
496 );
497 $this->add('text', 'mask', ts('Mask'),
498 $attributes['mask']
499 );
500
501 // is active ?
502 $this->add('checkbox', 'is_active', ts('Active?'));
503
504 // is active ?
505 $this->add('checkbox', 'is_view', ts('View Only?'));
506
507 // is searchable ?
508 $this->addElement('checkbox',
509 'is_searchable',
510 ts('Is this Field Searchable?'),
511 NULL, array('onclick' => "showSearchRange(this)")
512 );
513
514 // is searchable by range?
515 $searchRange = array();
516 $searchRange[] = $this->createElement('radio', NULL, NULL, ts('Yes'), '1');
517 $searchRange[] = $this->createElement('radio', NULL, NULL, ts('No'), '0');
518
519 $this->addGroup($searchRange, 'is_search_range', ts('Search by Range?'));
520
521 // add buttons
522 $this->addButtons(array(
523 array(
524 'type' => 'done',
525 'name' => ts('Save'),
526 'isDefault' => TRUE,
527 ),
528 array(
529 'type' => 'next',
530 'name' => ts('Save and New'),
531 'subName' => 'new',
532 ),
533 array(
534 'type' => 'cancel',
535 'name' => ts('Cancel'),
536 ),
537 )
538 );
539
540 // add a form rule to check default value
541 $this->addFormRule(array('CRM_Custom_Form_Field', 'formRule'), $this);
542
543 // if view mode pls freeze it with the done button.
544 if ($this->_action & CRM_Core_Action::VIEW) {
545 $this->freeze();
546 $url = CRM_Utils_System::url('civicrm/admin/custom/group/field', 'reset=1&action=browse&gid=' . $this->_gid);
547 $this->addElement('button',
548 'done',
549 ts('Done'),
550 array('onclick' => "location.href='$url'")
551 );
552 }
553 }
554
555 /**
556 * Global validation rules for the form
557 *
558 * @param array $fields posted values of the form
559 *
560 * @param $files
561 * @param $self
562 *
563 * @return array if errors then list of errors to be posted back to the form,
564 * true otherwise
565 * @static
566 */
567 public static function formRule($fields, $files, $self) {
568 $default = CRM_Utils_Array::value('default_value', $fields);
569
570 $errors = array();
571
572 //validate field label as well as name.
573 $title = $fields['label'];
574 $name = CRM_Utils_String::munge($title, '_', 64);
575 $gId = $self->_gid; // CRM-7564
576 $query = 'select count(*) from civicrm_custom_field where ( name like %1 OR label like %2 ) and id != %3 and custom_group_id = %4';
577 $fldCnt = CRM_Core_DAO::singleValueQuery($query, array(1 => array($name, 'String'),
578 2 => array($title, 'String'),
579 3 => array((int)$self->_id, 'Integer'),
580 4 => array($gId, 'Integer'),
581 ));
582 if ($fldCnt) {
583 $errors['label'] = ts('Custom field \'%1\' already exists in Database.', array(1 => $title));
584 }
585
586 //checks the given custom field name doesnot start with digit
587 if (!empty($title)) {
588 // gives the ascii value
589 $asciiValue = ord($title{0});
590 if ($asciiValue >= 48 && $asciiValue <= 57) {
591 $errors['label'] = ts("Name cannot not start with a digit");
592 }
593 }
594
595 // ensure that the label is not 'id'
596 if (strtolower($title) == 'id') {
597 $errors['label'] = ts("You cannot use 'id' as a field label.");
598 }
599
600 if (!isset($fields['data_type'][0]) || !isset($fields['data_type'][1])) {
601 $errors['_qf_default'] = ts('Please enter valid - Data and Input Field Type.');
602 }
603
604 $dataType = self::$_dataTypeKeys[$fields['data_type'][0]];
605
606 if ($default || $dataType == 'ContactReference') {
607 switch ($dataType) {
608 case 'Int':
609 if (!CRM_Utils_Rule::integer($default)) {
610 $errors['default_value'] = ts('Please enter a valid integer.');
611 }
612 break;
613
614 case 'Float':
615 if (!CRM_Utils_Rule::numeric($default)) {
616 $errors['default_value'] = ts('Please enter a valid number.');
617 }
618 break;
619
620 case 'Money':
621 if (!CRM_Utils_Rule::money($default)) {
622 $errors['default_value'] = ts('Please enter a valid number.');
623 }
624 break;
625
626 case 'Link':
627 if (!CRM_Utils_Rule::url($default)) {
628 $errors['default_value'] = ts('Please enter a valid link.');
629 }
630 break;
631
632 case 'Date':
633 if (!CRM_Utils_Rule::date($default)) {
634 $errors['default_value'] = ts('Please enter a valid date as default value using YYYY-MM-DD format. Example: 2004-12-31.');
635 }
636 break;
637
638 case 'Boolean':
639 if ($default != '1' && $default != '0') {
640 $errors['default_value'] = ts('Please enter 1 (for Yes) or 0 (for No) if you want to set a default value.');
641 }
642 break;
643
644 case 'Country':
645 if (!empty($default)) {
646 $query = "SELECT count(*) FROM civicrm_country WHERE name = %1 OR iso_code = %1";
647 $params = array(1 => array($fields['default_value'], 'String'));
648 if (CRM_Core_DAO::singleValueQuery($query, $params) <= 0) {
649 $errors['default_value'] = ts('Invalid default value for country.');
650 }
651 }
652 break;
653
654 case 'StateProvince':
655 if (!empty($default)) {
656 $query = "
657 SELECT count(*)
658 FROM civicrm_state_province
659 WHERE name = %1
660 OR abbreviation = %1";
661 $params = array(1 => array($fields['default_value'], 'String'));
662 if (CRM_Core_DAO::singleValueQuery($query, $params) <= 0) {
663 $errors['default_value'] = ts('The invalid default value for State/Province data type');
664 }
665 }
666 break;
667
668 case 'ContactReference':
669 if ($fields['filter_selected'] == 'Advance' && !empty($fields['filter'])) {
670 if (strpos($fields['filter'], 'entity=') !== FALSE) {
671 $errors['filter'] = ts("Please do not include entity parameter (entity is always 'contact')");
672 }
673 elseif (strpos($fields['filter'], 'action=get') === FALSE) {
674 $errors['filter'] = ts("Only 'get' action is supported.");
675 }
676 }
677 $self->setDefaults(array('filter_selected', $fields['filter_selected']));
678 break;
679 }
680 }
681
682 if (self::$_dataTypeKeys[$fields['data_type'][0]] == 'Date') {
683 if (!$fields['date_format']) {
684 $errors['date_format'] = ts('Please select a date format.');
685 }
686 }
687
688 /** Check the option values entered
689 * Appropriate values are required for the selected datatype
690 * Incomplete row checking is also required.
691 */
692 $_flagOption = $_rowError = 0;
693 $_showHide = new CRM_Core_ShowHideBlocks('', '');
694 $dataType = self::$_dataTypeKeys[$fields['data_type'][0]];
695 if (isset($fields['data_type'][1])) {
696 $dataField = $fields['data_type'][1];
697 }
698 $optionFields = array('Select', 'Multi-Select', 'CheckBox', 'Radio', 'AdvMulti-Select');
699
700 if (isset($fields['option_type']) && $fields['option_type'] == 1) {
701 //capture duplicate Custom option values
702 if (!empty($fields['option_value'])) {
703 $countValue = count($fields['option_value']);
704 $uniqueCount = count(array_unique($fields['option_value']));
705
706 if ($countValue > $uniqueCount) {
707
708 $start = 1;
709 while ($start < self::NUM_OPTION) {
710 $nextIndex = $start + 1;
711 while ($nextIndex <= self::NUM_OPTION) {
712 if ($fields['option_value'][$start] == $fields['option_value'][$nextIndex] &&
713 !empty($fields['option_value'][$nextIndex])
714 ) {
715 $errors['option_value[' . $start . ']'] = ts('Duplicate Option values');
716 $errors['option_value[' . $nextIndex . ']'] = ts('Duplicate Option values');
717 $_flagOption = 1;
718 }
719 $nextIndex++;
720 }
721 $start++;
722 }
723 }
724 }
725
726 //capture duplicate Custom Option label
727 if (!empty($fields['option_label'])) {
728 $countValue = count($fields['option_label']);
729 $uniqueCount = count(array_unique($fields['option_label']));
730
731 if ($countValue > $uniqueCount) {
732 $start = 1;
733 while ($start < self::NUM_OPTION) {
734 $nextIndex = $start + 1;
735 while ($nextIndex <= self::NUM_OPTION) {
736 if ($fields['option_label'][$start] == $fields['option_label'][$nextIndex] &&
737 !empty($fields['option_label'][$nextIndex])
738 ) {
739 $errors['option_label[' . $start . ']'] = ts('Duplicate Option label');
740 $errors['option_label[' . $nextIndex . ']'] = ts('Duplicate Option label');
741 $_flagOption = 1;
742 }
743 $nextIndex++;
744 }
745 $start++;
746 }
747 }
748 }
749
750 for ($i = 1; $i <= self::NUM_OPTION; $i++) {
751 if (!$fields['option_label'][$i]) {
752 if ($fields['option_value'][$i]) {
753 $errors['option_label[' . $i . ']'] = ts('Option label cannot be empty');
754 $_flagOption = 1;
755 }
756 else {
757 $_emptyRow = 1;
758 }
759 }
760 else {
761 if (!strlen(trim($fields['option_value'][$i]))) {
762 if (!$fields['option_value'][$i]) {
763 $errors['option_value[' . $i . ']'] = ts('Option value cannot be empty');
764 $_flagOption = 1;
765 }
766 }
767 }
768
769 if ($fields['option_value'][$i] && $dataType != 'String') {
770 if ($dataType == 'Int') {
771 if (!CRM_Utils_Rule::integer($fields['option_value'][$i])) {
772 $_flagOption = 1;
773 $errors['option_value[' . $i . ']'] = ts('Please enter a valid integer.');
774 }
775 }
776 elseif ($dataType == 'Money') {
777 if (!CRM_Utils_Rule::money($fields['option_value'][$i])) {
778 $_flagOption = 1;
779 $errors['option_value[' . $i . ']'] = ts('Please enter a valid money value.');
780 }
781 }
782 else {
783 if (!CRM_Utils_Rule::numeric($fields['option_value'][$i])) {
784 $_flagOption = 1;
785 $errors['option_value[' . $i . ']'] = ts('Please enter a valid number.');
786 }
787 }
788 }
789
790 $showBlocks = 'optionField_' . $i;
791 if ($_flagOption) {
792 $_showHide->addShow($showBlocks);
793 $_rowError = 1;
794 }
795
796 if (!empty($_emptyRow)) {
797 $_showHide->addHide($showBlocks);
798 }
799 else {
800 $_showHide->addShow($showBlocks);
801 }
802 if ($i == self::NUM_OPTION) {
803 $hideBlock = 'additionalOption';
804 $_showHide->addHide($hideBlock);
805 }
806
807 $_flagOption = $_emptyRow = 0;
808 }
809 }
810 elseif (isset($dataField) &&
811 in_array($dataField, $optionFields) &&
812 !in_array($dataType, array('Boolean', 'Country', 'StateProvince'))
813 ) {
814 if (!$fields['option_group_id']) {
815 $errors['option_group_id'] = ts('You must select a Multiple Choice Option set if you chose Reuse an existing set.');
816 }
817 else {
818 $query = "
819 SELECT count(*)
820 FROM civicrm_custom_field
821 WHERE data_type != %1
822 AND option_group_id = %2";
823 $params = array(
824 1 => array(self::$_dataTypeKeys[$fields['data_type'][0]],
825 'String',
826 ),
827 2 => array($fields['option_group_id'], 'Integer'),
828 );
829 $count = CRM_Core_DAO::singleValueQuery($query, $params);
830 if ($count > 0) {
831 $errors['option_group_id'] = ts('The data type of the multiple choice option set you\'ve selected does not match the data type assigned to this field.');
832 }
833 }
834 }
835
836 $assignError = new CRM_Core_Page();
837 if ($_rowError) {
838 $_showHide->addToTemplate();
839 $assignError->assign('optionRowError', $_rowError);
840 }
841 else {
842 if (isset($fields['data_type'][1])) {
843 switch (self::$_dataToHTML[$fields['data_type'][0]][$fields['data_type'][1]]) {
844 case 'Radio':
845 $_fieldError = 1;
846 $assignError->assign('fieldError', $_fieldError);
847 break;
848
849 case 'Checkbox':
850 $_fieldError = 1;
851 $assignError->assign('fieldError', $_fieldError);
852 break;
853
854 case 'Select':
855 $_fieldError = 1;
856 $assignError->assign('fieldError', $_fieldError);
857 break;
858
859 default:
860 $_fieldError = 0;
861 $assignError->assign('fieldError', $_fieldError);
862 }
863 }
864
865 for ($idx = 1; $idx <= self::NUM_OPTION; $idx++) {
866 $showBlocks = 'optionField_' . $idx;
867 if (!empty($fields['option_label'][$idx])) {
868 $_showHide->addShow($showBlocks);
869 }
870 else {
871 $_showHide->addHide($showBlocks);
872 }
873 }
874 $_showHide->addToTemplate();
875 }
876
877 // we can not set require and view at the same time.
878 if (!empty($fields['is_required']) && !empty($fields['is_view'])) {
879 $errors['is_view'] = ts('Can not set this field Required and View Only at the same time.');
880 }
881
882 return empty($errors) ? TRUE : $errors;
883 }
884
885 /**
886 * Process the form
887 *
888 * @param null
889 *
890 * @return void
891 */
892 public function postProcess() {
893 // store the submitted values in an array
894 $params = $this->controller->exportValues($this->_name);
895 if ($this->_action == CRM_Core_Action::UPDATE) {
896 $dataTypeKey = $this->_defaultDataType[0];
897 $params['data_type'] = self::$_dataTypeKeys[$this->_defaultDataType[0]];
898 $params['html_type'] = self::$_dataToHTML[$this->_defaultDataType[0]][$this->_defaultDataType[1]];
899 }
900 else {
901 $dataTypeKey = $params['data_type'][0];
902 $params['html_type'] = self::$_dataToHTML[$params['data_type'][0]][$params['data_type'][1]];
903 $params['data_type'] = self::$_dataTypeKeys[$params['data_type'][0]];
904 }
905
906 //fix for 'is_search_range' field.
907 if (in_array($dataTypeKey, array(
908 1, 2, 3, 5))) {
909 if (empty($params['is_searchable'])) {
910 $params['is_search_range'] = 0;
911 }
912 }
913 else {
914 $params['is_search_range'] = 0;
915 }
916
917 $filter = 'null';
918 if ($dataTypeKey == 11 && !empty($params['filter_selected'])) {
919 if ($params['filter_selected'] == 'Advance' && trim(CRM_Utils_Array::value('filter', $params))) {
920 $filter = trim($params['filter']);
921 }
922 elseif ($params['filter_selected'] == 'Group' && !empty($params['group_id'])) {
923
924 $filter = 'action=lookup&group=' . implode(',', $params['group_id']);
925 }
926 }
927 $params['filter'] = $filter;
928
929 // fix for CRM-316
930 $oldWeight = NULL;
931 if ($this->_action & (CRM_Core_Action::UPDATE | CRM_Core_Action::ADD)) {
932 $fieldValues = array('custom_group_id' => $this->_gid);
933 if ($this->_id) {
934 $oldWeight = $this->_values['weight'];
935 }
936 $params['weight'] = CRM_Utils_Weight::updateOtherWeights('CRM_Core_DAO_CustomField', $oldWeight, $params['weight'], $fieldValues);
937 }
938
939 $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
940
941 //store the primary key for State/Province or Country as default value.
942 if (strlen(trim($params['default_value']))) {
943 switch ($params['data_type']) {
944 case 'StateProvince':
945 $fieldStateProvince = $strtolower($params['default_value']);
946 $query = "
947 SELECT id
948 FROM civicrm_state_province
949 WHERE LOWER(name) = '$fieldStateProvince'
950 OR abbreviation = '$fieldStateProvince'";
951 $dao = CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
952 if ($dao->fetch()) {
953 $params['default_value'] = $dao->id;
954 }
955 break;
956
957 case 'Country':
958 $fieldCountry = $strtolower($params['default_value']);
959 $query = "
960 SELECT id
961 FROM civicrm_country
962 WHERE LOWER(name) = '$fieldCountry'
963 OR iso_code = '$fieldCountry'";
964 $dao = CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
965 if ($dao->fetch()) {
966 $params['default_value'] = $dao->id;
967 }
968 break;
969 }
970 }
971
972 // The text_length attribute for Memo fields is in a different input as there
973 // are different label, help text and default value than for other type fields
974 if ($params['data_type'] == "Memo") {
975 $params['text_length'] = $params['note_length'];
976 }
977
978 // need the FKEY - custom group id
979 $params['custom_group_id'] = $this->_gid;
980
981 if ($this->_action & CRM_Core_Action::UPDATE) {
982 $params['id'] = $this->_id;
983 }
984 $customField = CRM_Core_BAO_CustomField::create($params);
985 $this->_id = $customField->id;
986
987 // reset the cache
988 CRM_Core_BAO_Cache::deleteGroup('contact fields');
989
990 $msg = '<p>' . ts("Custom field '%1' has been saved.", array(1 => $customField->label)) . '</p>';
991
992 $buttonName = $this->controller->getButtonName();
993 $session = CRM_Core_Session::singleton();
994 if ($buttonName == $this->getButtonName('next', 'new')) {
995 $msg .= '<p>' . ts("Ready to add another.") . '</p>';
996 $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/custom/group/field/add',
997 'reset=1&action=add&gid=' . $this->_gid
998 ));
999 }
1000 else {
1001 $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/custom/group/field',
1002 'reset=1&action=browse&gid=' . $this->_gid
1003 ));
1004 }
1005 $session->setStatus($msg, ts('Saved'), 'success');
1006
1007 // Add data when in ajax contect
1008 $this->ajaxResponse['customField'] = $customField->toArray();
1009 }
1010 }