Merge pull request #3124 from adamwight/CRM-14473-setnull
[civicrm-core.git] / CRM / UF / Form / Field.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
06b69b18 4 | CiviCRM version 4.5 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
6a488035
TO
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
06b69b18 31 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
32 * $Id$
33 *
34 */
35
36/**
37 * form to process actions on the field aspect of Custom
38 */
39class CRM_UF_Form_Field extends CRM_Core_Form {
40
41 /**
42 * the uf group id saved to the session for an update
43 *
44 * @var int
45 * @access protected
46 */
47 protected $_gid;
48
49 /**
50 * The field id, used when editing the field
51 *
52 * @var int
53 * @access protected
54 */
55 protected $_id;
56
57 /**
58 * The set of fields that we can view/edit in the user field framework
59 *
60 * @var array
61 * @access protected
62 */
63 protected $_fields;
64
65 /**
66 * the title for field
67 *
68 * @var int
69 * @access protected
70 */
71 protected $_title;
72
73 /**
74 * The set of fields sent to the select element
75 *
76 * @var array
77 * @access protected
78 */
79 protected $_selectFields;
80
81 /**
82 * to store fields with if locationtype exits status
83 *
84 * @var array
85 * @access protected
86 */
87 protected $_hasLocationTypes;
88
89 /**
90 * is this profile has searchable field
91 * or is any field having in selector true.
92 *
93 * @var boolean.
94 * @access protected
95 */
96 protected $_hasSearchableORInSelector;
97
98 /**
99 * Function to set variables up before form is built
100 *
101 * @return void
102 * @access public
103 */
104 public function preProcess() {
105 $this->_gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this);
106 $this->_id = CRM_Utils_Request::retrieve('id', 'Positive', $this);
107 if ($this->_gid) {
108 $this->_title = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $this->_gid, 'title');
109 CRM_Utils_System::setTitle($this->_title . ' - ' . ts('CiviCRM Profile Fields'));
110
111 $url = CRM_Utils_System::url('civicrm/admin/uf/group/field',
112 "reset=1&action=browse&gid={$this->_gid}"
113 );
114
115 $session = CRM_Core_Session::singleton();
116 $session->pushUserContext($url);
117 $breadCrumb = array(array('title' => ts('CiviCRM Profile Fields'),
118 'url' => $url,
119 ));
120 CRM_Utils_System::appendBreadCrumb($breadCrumb);
121 }
122
123 $showBestResult = CRM_Utils_Request::retrieve('sbr', 'Positive', CRM_Core_DAO::$_nullArray);
124 if ($showBestResult) {
125 $this->assign('showBestResult', $showBestResult);
126 }
127
128 $this->_fields = CRM_Contact_BAO_Contact::importableFields('All', TRUE, TRUE, TRUE, TRUE, TRUE);
129 $this->_fields = array_merge(CRM_Activity_BAO_Activity::exportableFields('Activity'), $this->_fields);
130
131 //unset campaign related fields.
132 if (isset($this->_fields['activity_campaign_id'])) {
133 $this->_fields['activity_campaign_id']['title'] = ts('Campaign');
134 if (isset($this->_fields['activity_campaign'])) {
135 unset($this->_fields['activity_campaign']);
136 }
137 }
138
139 if (CRM_Core_Permission::access('CiviContribute')) {
140 $this->_fields = array_merge(CRM_Contribute_BAO_Contribution::getContributionFields(FALSE), $this->_fields);
141 $this->_fields = array_merge(CRM_Core_BAO_UFField::getContribBatchEntryFields(), $this->_fields);
142 }
143
144 if (CRM_Core_Permission::access('CiviMember')) {
145 $this->_fields = array_merge(CRM_Member_BAO_Membership::getMembershipFields(), $this->_fields);
146 }
147
148 if (CRM_Core_Permission::access('CiviEvent')) {
149 $this->_fields = array_merge(CRM_Event_BAO_Query::getParticipantFields(), $this->_fields);
150 }
151
1cb28d5d 152 if (CRM_Core_Permission::access('CiviCase')) {
153 $this->_fields = array_merge(CRM_Case_BAO_Query::getFields(), $this->_fields);
154 }
155
61750d67
DS
156 $this->_fields = array_merge($this->_fields, CRM_Contact_BAO_Query_Hook::singleton()->getFields());
157
6a488035
TO
158 $this->_selectFields = array();
159 foreach ($this->_fields as $name => $field) {
160 // lets skip note for now since we dont support it
161 if ($name == 'note') {
162 continue;
163 }
164 $this->_selectFields[$name] = $field['title'];
165 $this->_hasLocationTypes[$name] = CRM_Utils_Array::value('hasLocationType', $field);
166 }
167
168 // lets add group, tag and current_employer to this list
169 $this->_selectFields['group'] = ts('Group(s)');
170 $this->_selectFields['tag'] = ts('Tag(s)');
171 $this->_selectFields['current_employer'] = ts('Current Employer');
172 $this->_selectFields['phone_and_ext'] = ts('Phone and Extension');
173
174 //CRM-4363 check for in selector or searchable fields.
175 $this->_hasSearchableORInSelector = CRM_Core_BAO_UFField::checkSearchableORInSelector($this->_gid);
176
177 $this->assign('fieldId', $this->_id);
178 if ($this->_id) {
179 $fieldTitle = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFField', $this->_id, 'label');
180 $this->assign('fieldTitle', $fieldTitle);
181 }
182 }
183
184 /**
185 * Function to actually build the form
186 *
187 * @return void
188 * @access public
189 */
190 public function buildQuickForm() {
191 if ($this->_action & CRM_Core_Action::DELETE) {
192 $this->addButtons(array(
193 array(
194 'type' => 'next',
195 'name' => ts('Delete Profile Field'),
196 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
197 'isDefault' => TRUE,
198 ),
199 array(
200 'type' => 'cancel',
201 'name' => ts('Cancel'),
202 ),
203 )
204 );
205 return;
206 }
207
208 if (isset($this->_id)) {
209 $params = array('id' => $this->_id);
210 CRM_Core_BAO_UFField::retrieve($params, $defaults);
211
212 // set it to null if so (avoids crappy E_NOTICE errors below
213 $defaults['location_type_id'] = CRM_Utils_Array::value('location_type_id', $defaults);
214
215 $specialFields = CRM_Core_BAO_UFGroup::getLocationFields();
216
217 if (!$defaults['location_type_id'] &&
218 $defaults["field_type"] != "Formatting" &&
219 in_array($defaults['field_name'], $specialFields)
220 ) {
221 $defaults['location_type_id'] = 0;
222 }
223
224 $defaults['field_name'] = array(
225 $defaults['field_type'],
226 ($defaults['field_type'] == "Formatting" ? "" : $defaults['field_name']),
227 $defaults['location_type_id'],
228 CRM_Utils_Array::value('phone_type_id', $defaults),
229 );
230 $this->_gid = $defaults['uf_group_id'];
231 }
232 else {
233 $defaults['is_active'] = 1;
234 }
235
236 $otherModules = array_values(CRM_Core_BAO_UFGroup::getUFJoinRecord($this->_gid));
237 $this->assign('otherModules', $otherModules);
238
239 if ($this->_action & CRM_Core_Action::ADD) {
240 $fieldValues = array('uf_group_id' => $this->_gid);
241 $defaults['weight'] = CRM_Utils_Weight::getDefaultWeight('CRM_Core_DAO_UFField', $fieldValues);
242 }
243
244 // lets trim all the whitespace
245 $this->applyFilter('__ALL__', 'trim');
246
247 //hidden field to catch the group id in profile
248 $this->add('hidden', 'group_id', $this->_gid);
249
250 //hidden field to catch the field id in profile
251 $this->add('hidden', 'field_id', $this->_id);
252
253 $fields = CRM_Core_BAO_UFField::getAvailableFields($this->_gid, $defaults);
254
255 $noSearchable = array();
256 $addressCustomFields = array_keys(CRM_Core_BAO_CustomField::getFieldsForImport('Address'));
257
258 foreach ($fields as $key => $value) {
259 foreach ($value as $key1 => $value1) {
260 //CRM-2676, replacing the conflict for same custom field name from different custom group.
261 if ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key1)) {
262 $customGroupId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', $customFieldId, 'custom_group_id');
263 $customGroupName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $customGroupId, 'title');
264 $this->_mapperFields[$key][$key1] = $value1['title'] . ' :: ' . $customGroupName;
265 if (in_array($key1, $addressCustomFields)) {
266 $noSearchable[] = $value1['title'] . ' :: ' . $customGroupName;
267 }
268 }
269 else {
270 $this->_mapperFields[$key][$key1] = $value1['title'];
271 }
272 $hasLocationTypes[$key][$key1] = CRM_Utils_Array::value('hasLocationType', $value1);
273
274 // hide the 'is searchable' field for 'File' custom data
275 if (isset($value1['data_type']) &&
276 isset($value1['html_type']) &&
277 (($value1['data_type'] == 'File' && $value1['html_type'] == 'File')
278 || ($value1['data_type'] == 'Link' && $value1['html_type'] == 'Link')
279 )
280 ) {
281 if (!in_array($value1['title'], $noSearchable)) {
282 $noSearchable[] = $value1['title'];
283 }
284 }
285 }
286 }
287 $this->assign('noSearchable', $noSearchable);
288
b2b0530a 289 $this->_location_types = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
6a488035
TO
290 $defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
291
292 /**
293 * FIXME: dirty hack to make the default option show up first. This
294 * avoids a mozilla browser bug with defaults on dynamically constructed
295 * selector widgets.
296 */
297 if ($defaultLocationType) {
298 $defaultLocation = $this->_location_types[$defaultLocationType->id];
299 unset($this->_location_types[$defaultLocationType->id]);
300 $this->_location_types = array(
301 $defaultLocationType->id => $defaultLocation) + $this->_location_types;
302 }
303
304 $this->_location_types = array('Primary') + $this->_location_types;
305
306 // since we need a hierarchical list to display contact types & subtypes,
307 // this is what we going to display in first selector
308 $contactTypes = CRM_Contact_BAO_ContactType::getSelectElements(FALSE, FALSE);
309 unset($contactTypes['']);
310
311 $contactTypes = !empty($contactTypes) ? array('Contact' => 'Contacts') + $contactTypes : array();
312 $sel1 = array('' => '- select -') + $contactTypes;
313
314 if (!empty($fields['Activity'])) {
315 $sel1['Activity'] = 'Activity';
316 }
317
318 if (CRM_Core_Permission::access('CiviEvent')) {
319 $sel1['Participant'] = 'Participants';
320 }
321
322 if (!empty($fields['Contribution'])) {
323 $sel1['Contribution'] = 'Contributions';
324 }
325
326 if (!empty($fields['Membership'])) {
327 $sel1['Membership'] = 'Membership';
328 }
329
1cb28d5d 330 if (!empty($fields['Case'])) {
331 $sel1['Case'] = 'Case';
332 }
333
6a488035
TO
334 if (!empty($fields['Formatting'])) {
335 $sel1['Formatting'] = 'Formatting';
336 }
337
338 foreach ($sel1 as $key => $sel) {
339 if ($key) {
340 $sel2[$key] = $this->_mapperFields[$key];
341 }
342 }
343 $sel3[''] = NULL;
b4f964d9 344 $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
6a488035
TO
345 ksort($phoneTypes);
346
347 foreach ($sel1 as $k => $sel) {
348 if ($k) {
349 foreach ($this->_location_types as $key => $value) {
350 $sel4[$k]['phone'][$key] = &$phoneTypes;
351 $sel4[$k]['phone_and_ext'][$key] = &$phoneTypes;
352 }
353 }
354 }
355
356 foreach ($sel1 as $k => $sel) {
357 if ($k) {
358 if (is_array($this->_mapperFields[$k])) {
359 foreach ($this->_mapperFields[$k] as $key => $value) {
360 if ($hasLocationTypes[$k][$key]) {
361 $sel3[$k][$key] = $this->_location_types;
362 }
363 else {
364 $sel3[$key] = NULL;
365 }
366 }
367 }
368 }
369 }
370
371 $this->_defaults = array();
372 $js = "<script type='text/javascript'>\n";
373 $formName = "document.{$this->_name}";
374
375 $alreadyMixProfile = FALSE;
376 if (CRM_Core_BAO_UFField::checkProfileType($this->_gid)) {
377 $alreadyMixProfile = TRUE;
378 }
379 $this->assign('alreadyMixProfile', $alreadyMixProfile);
380
381 $sel = &$this->addElement('hierselect', 'field_name', ts('Field Name'));
382
383 $formValues = $this->exportValues();
384
385 if (empty($formValues)) {
386 for ($k = 1; $k < 4; $k++) {
52e7a51a 387 if (!isset($defaults['field_name'][$k])) {
6a488035
TO
388 $js .= "{$formName}['field_name[$k]'].style.display = 'none';\n";
389 }
390 }
391 }
392 else {
393 if (!empty($formValues['field_name'])) {
52e7a51a
LS
394 for ($key = 1; $key < 4; $key++) {
395 if (!isset($formValues['field_name'][$key])) {
396 $js .= "{$formName}['field_name[$key]'].style.display = 'none';\n";
397 }
398 else {
399 $js .= "{$formName}['field_name[$key]'].style.display = '';\n";
6a488035
TO
400 }
401 }
402 }
403 else {
404 for ($k = 1; $k < 4; $k++) {
405 if (!isset($defaults['field_name'][$k])) {
406 $js .= "{$formName}['field_name[$k]'].style.display = 'none';\n";
407 }
408 }
409 }
410 }
411
412 foreach ($sel2 as $k => $v) {
413 if (is_array($sel2[$k])) {
414 asort($sel2[$k]);
415 }
416 }
417
0c145cc0 418 // CRM_Core_Error::debug(array($sel1, $sel2, $sel3, $sel4));
6a488035
TO
419 $sel->setOptions(array($sel1, $sel2, $sel3, $sel4));
420
421 // proper interpretation of spec in CRM-8732
422 if (!isset($this->_id) && in_array('Search Profile', $otherModules)) {
423 $defaults['visibility'] = 'Public Pages and Listings';
424 }
425
426 $js .= "</script>\n";
427 $this->assign('initHideBoxes', $js);
428
429 $this->add('select',
430 'visibility',
431 ts('Visibility'),
432 CRM_Core_SelectValues::ufVisibility(),
433 TRUE,
434 array('onChange' => "showHideSeletorSearch(this.value);")
435 );
436
437 //CRM-4363
438 $js = array('onChange' => "mixProfile();");
439 // should the field appear in selectors (as a column)?
440 $this->add('checkbox', 'in_selector', ts('Results Column?'), NULL, NULL, $js);
441 $this->add('checkbox', 'is_searchable', ts('Searchable?'), NULL, NULL, $js);
442
443 $attributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_UFField');
444
445 // weight
446 $this->add('text', 'weight', ts('Order'), $attributes['weight'], TRUE);
447 $this->addRule('weight', ts('is a numeric field'), 'numeric');
448
449 $this->add('textarea', 'help_pre', ts('Field Pre Help'), $attributes['help_pre']);
450 $this->add('textarea', 'help_post', ts('Field Post Help'), $attributes['help_post']);
451
452 $this->add('checkbox', 'is_required', ts('Required?'));
453
454 $this->add('checkbox', 'is_multi_summary', ts('Include in multi-record listing?'));
455 $this->add('checkbox', 'is_active', ts('Active?'));
456 $this->add('checkbox', 'is_view', ts('View Only?'));
457
458 // $this->add( 'checkbox', 'is_registration', ts( 'Display in Registration Form?' ) );
459 //$this->add( 'checkbox', 'is_match' , ts( 'Key to Match Contacts?' ) );
460
461 $this->add('text', 'label', ts('Field Label'), $attributes['label']);
462
463 $js = NULL;
464 if ($this->_hasSearchableORInSelector) {
465 $js = array('onclick' => "return verify( );");
466 }
467
468 // add buttons
469 $this->addButtons(array(
470 array(
471 'type' => 'next',
472 'name' => ts('Save'),
473 'isDefault' => TRUE,
474 'js' => $js,
475 ),
476 array(
477 'type' => 'next',
478 'name' => ts('Save and New'),
479 'subName' => 'new',
480 'js' => $js,
481 ),
482 array(
483 'type' => 'cancel',
484 'name' => ts('Cancel'),
485 ),
486 )
487 );
488
489 $this->addFormRule(array('CRM_UF_Form_Field', 'formRule'), $this);
490
491 // if view mode pls freeze it with the done button.
492 if ($this->_action & CRM_Core_Action::VIEW) {
493 $this->freeze();
494 $this->addElement('button', 'done', ts('Done'),
495 array('onclick' => "location.href='civicrm/admin/uf/group/field?reset=1&action=browse&gid=" . $this->_gid . "'")
496 );
497 }
498
499 if (isset($defaults['field_name']) && CRM_Utils_Array::value(1, $defaults['field_name']) == 'url-1') {
500 $defaults['field_name'][1] = 'url';
501 }
502
503 $this->setDefaults($defaults);
504 }
505
506 /**
507 * Process the form
508 *
509 * @return void
510 * @access public
511 */
512 public function postProcess() {
513 $ids = array('uf_group' => $this->_gid);
514 if ($this->_action & CRM_Core_Action::DELETE) {
515 $fieldValues = array('uf_group_id' => $this->_gid);
516 CRM_Utils_Weight::delWeight('CRM_Core_DAO_UFField', $this->_id, $fieldValues);
517 $deleted = CRM_Core_BAO_UFField::del($this->_id);
518
519 //update group_type every time. CRM-3608
520 if ($this->_gid && $deleted) {
521 //get the profile type.
522 $fieldsType = CRM_Core_BAO_UFGroup::calculateGroupType($this->_gid, TRUE);
523 CRM_Core_BAO_UFGroup::updateGroupTypes($this->_gid, $fieldsType);
524 }
525
526 CRM_Core_Session::setStatus(ts('Selected Profile Field has been deleted.'), ts('Profile Field Deleted'), 'success');
527 return;
528 }
529
530 // store the submitted values in an array
531 $params = $this->controller->exportValues('Field');
532 if ($params['visibility'] == 'User and User Admin Only') {
533 $params['is_searchable'] = $params['in_selector'] = 0;
534 }
535
536 if ($this->_action & CRM_Core_Action::UPDATE) {
537 $ids['uf_field'] = $this->_id;
538 }
539
540
541 $name = NULL;
f06c4974 542 if (isset($params['field_name'][1]) && isset($this->_selectFields[$params['field_name'][1]])) {
6a488035
TO
543 // we dont get a name for a html formatting element
544 $name = $this->_selectFields[$params['field_name'][1]];
545 }
546
547 //Hack for Formatting Field Name
548 if ($params['field_name'][0] == 'Formatting') {
549 $params['field_name'][1] = 'formatting_' . rand(1000, 9999);
550 }
551
552 // temporary hack to for website
553 if ($params['field_name'][1] == 'url') {
554 $params['field_name'][1] = 'url-1';
555 }
556
557 //check for duplicate fields
558 if ($params["field_name"][0] != "Formatting" && CRM_Core_BAO_UFField::duplicateField($params, $ids)) {
559 CRM_Core_Session::setStatus(ts('The selected field already exists in this profile.'), ts('Field Not Added'), 'error');
560 return;
561 }
562 else {
563 $params['weight'] = CRM_Core_BAO_UFField::autoWeight($params);
564 $ufField = CRM_Core_BAO_UFField::add($params, $ids);
565
566 //reset other field is searchable and in selector settings, CRM-4363
567 if ($this->_hasSearchableORInSelector &&
1cb28d5d 568 in_array($ufField->field_type, array('Participant', 'Contribution', 'Membership', 'Activity', 'Case'))
6a488035
TO
569 ) {
570 CRM_Core_BAO_UFField::resetInSelectorANDSearchable($this->_gid);
571 }
572
573 $config = CRM_Core_Config::singleton();
574 $showBestResult = FALSE;
575 if (in_array($ufField->field_name, array(
576 'country', 'state_province')) && count($config->countryLimit) > 1) {
577 // get state or country field weight if exists
578 $field = 'state_province';
579 if ($ufField->field_name == 'state_province') {
580 $field = 'country';
581 }
582 $ufFieldDAO = new CRM_Core_DAO_UFField();
583 $ufFieldDAO->field_name = $field;
584 $ufFieldDAO->location_type_id = $ufField->location_type_id;
585 $ufFieldDAO->uf_group_id = $ufField->uf_group_id;
586
587 if ($ufFieldDAO->find(TRUE)) {
588 if ($field == 'country' && $ufFieldDAO->weight > $ufField->weight) {
589 $showBestResult = TRUE;
590 }
591 elseif ($field == 'state_province' && $ufFieldDAO->weight < $ufField->weight) {
592 $showBestResult = TRUE;
593 }
594 }
595 }
596
597 //update group_type every time. CRM-3608
598 if ($this->_gid && is_a($ufField, 'CRM_Core_DAO_UFField')) {
599 // get the profile type.
600 $fieldsType = CRM_Core_BAO_UFGroup::calculateGroupType($this->_gid, TRUE);
601 CRM_Core_BAO_UFGroup::updateGroupTypes($this->_gid, $fieldsType);
602 }
603 CRM_Core_Session::setStatus(ts('Your CiviCRM Profile Field \'%1\' has been saved to \'%2\'.',
604 array(1 => $name, 2 => $this->_title)
605 ), ts('Profile Field Saved'), 'success');
606 }
607 $buttonName = $this->controller->getButtonName();
608
609 $session = CRM_Core_Session::singleton();
610 if ($buttonName == $this->getButtonName('next', 'new')) {
611 CRM_Core_Session::setStatus(ts(' You can add another profile field.'), '', 'info');
612 $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/uf/group/field/add',
613 "reset=1&action=add&gid={$this->_gid}&sbr={$showBestResult}"
614 ));
615 }
616 else {
617 $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/uf/group/field',
618 "reset=1&action=browse&gid={$this->_gid}"
619 ));
620 $session->set('showBestResult', $showBestResult);
621 }
622 }
623
624 /**
625 * validation rule for subtype.
626 *
627 * @param array $groupType contains all groupTypes.
628 *
629 * @param string $fieldType type of field.
630 *
631 * @param array $errors
632 *
633 * @return array list of errors to be posted back to the form
634 * @static
635 * @access public
636 */
637 static function formRuleSubType($fieldType, $groupType, $errors) {
638 if (in_array($fieldType, array(
639 'Participant', 'Contribution', 'Membership', 'Activity'))) {
640 $individualSubTypes = CRM_Contact_BAO_ContactType::subTypes('Individual');
641 foreach ($groupType as $value) {
642 if (!in_array($value, $individualSubTypes) &&
643 !in_array($value, array(
644 'Participant', 'Contribution', 'Membership',
645 'Individual', 'Contact', 'Activity',
646 ))
647 ) {
648 $errors['field_name'] = ts('Cannot add or update profile field "%1" with combination of Household or Organization or any subtypes of Household or Organisation.', array(1 => $fieldType));
649 break;
650 }
651 }
652 }
653 else {
654 $basicType = CRM_Contact_BAO_ContactType::getBasicType($groupType);
655 if ($basicType) {
656 if (!is_array($basicType)) {
657 $basicType = array($basicType);
658 }
659 if (!in_array($fieldType, $basicType)) {
660 $errors['field_name'] = ts('Cannot add or update profile field type "%1" with combination of subtype other than "%1".',
661 array(1 => $fieldType)
662 );
663 }
664 }
665 }
666 }
667
668 /**
669 * validation rule for custom data extends entity column values.
670 *
671 * @param Object $customField Custom field
672 * @param Integer $gid Group Id.
673 * @param String $fieldType Group type of the field
674 * @param Array $errors Collect errors
675 *
676 * @return Array list of errors to be posted back to the form
677 * @static
678 * @access public
679 */
680 static function formRuleCustomDataExtentColumnValue($customField, $gid, $fieldType, &$errors) {
681 // fix me : check object $customField
682 if (in_array($fieldType, array(
1cb28d5d 683 'Participant', 'Contribution', 'Membership', 'Activity', 'Case'))) {
6a488035
TO
684 $params = array('id' => $customField->custom_group_id);
685 $customGroup = array();
686 CRM_Core_BAO_CustomGroup::retrieve($params, $customGroup);
8cc574cf 687 if (($fieldType != CRM_Utils_Array::value('extends', $customGroup)) || empty($customGroup['extends_entity_column_value'])) {
6a488035
TO
688 return $errors;
689 }
690
691 $extendsColumnValues = array();
692 foreach (explode(CRM_Core_DAO::VALUE_SEPARATOR, $customGroup['extends_entity_column_value']) as $val) {
693 if ($val) {
694 $extendsColumnValues[] = $val;
695 }
696 }
697
698 if (empty($extendsColumnValues)) {
699 return $errors;
700 }
701
702 $fieldTypeValues = CRM_Core_BAO_UFGroup::groupTypeValues($gid, $fieldType);
a7488080 703 if (empty($fieldTypeValues[$fieldType])) {
6a488035
TO
704 return;
705 }
706
707 $disallowedTypes = array_diff($extendsColumnValues, $fieldTypeValues[$fieldType]);
708 if (!empty($disallowedTypes)) {
709 $errors['field_name'] = ts('Profile is already having custom fields extending different group types, you can not add or update this custom field.');
710 }
711 }
712 }
713
b29a9e66 714 /**
fdcc5d07 715 * Validation rule to prevent multiple fields of primary location type within the same communication type.
b29a9e66
LS
716 *
717 * @param Array $fields Submitted fields
718 * @param String $profileFieldName Group Id.
719 * @param Array $groupFields List of fields already in the group
720 * @param Array $errors Collect errors
721 *
722 * @static
723 * @access public
724 */
725 static function formRulePrimaryCheck($fields, $profileFieldName, $groupFields, &$errors) {
726 //FIXME: This may need to also apply to website fields if they are refactored to allow more than one per profile
727 $checkPrimary = array('phone' => 'civicrm_phone.phone', 'phone_and_ext' => 'civicrm_phone.phone');
728 $whereCheck = NULL;
729 $primaryOfSameTypeFound = NULL;
fdcc5d07 730 $fieldID = empty($fields['field_id']) ? 0 : $fields['field_id'];
b29a9e66
LS
731 // Is this a primary location type field of interest
732 if (array_key_exists($profileFieldName, $checkPrimary)) {
733 $whereCheck = $checkPrimary[$profileFieldName];
734 }
735 $potentialLocationType = CRM_Utils_Array::value(2, $fields['field_name']);
736
737 if ($whereCheck && $potentialLocationType == 0) {
738 $primaryOfSameTypeFound = '';
739
740 foreach ($groupFields as $groupField) {
741 // if it is a phone
fdcc5d07 742 if ($groupField['where'] == $whereCheck && is_null($groupField['location_type_id']) && $groupField['field_id'] != $fieldID) {
b29a9e66 743 $primaryOfSameTypeFound = $groupField['title'];
fdcc5d07 744 break;
b29a9e66
LS
745 }
746 }
747 if ($primaryOfSameTypeFound) {
748 $errors['field_name'] = ts('You have already added a primary location field of this type: %1', $primaryOfSameTypeFound);
749 }
750 }
751 }
752
6a488035
TO
753 /**
754 * global validation rules for the form
755 *
756 * @param array $fields posted values of the form
757 *
758 * @return array list of errors to be posted back to the form
759 * @static
760 * @access public
761 */
762 static function formRule($fields, $files, $self) {
763 $is_required = CRM_Utils_Array::value('is_required', $fields, FALSE);
764 $is_registration = CRM_Utils_Array::value('is_registration', $fields, FALSE);
765 $is_view = CRM_Utils_Array::value('is_view', $fields, FALSE);
766 $in_selector = CRM_Utils_Array::value('in_selector', $fields, FALSE);
767 $is_active = CRM_Utils_Array::value('is_active', $fields, FALSE);
768
769 $errors = array();
770 if ($is_view && $is_registration) {
771 $errors['is_registration'] = ts('View Only cannot be selected if this field is to be included on the registration form');
772 }
773 if ($is_view && $is_required) {
774 $errors['is_view'] = ts('A View Only field cannot be required');
775 }
776
b29a9e66
LS
777 $entityName = $fields['field_name'][0];
778 if (!$entityName) {
6a488035
TO
779 $errors['field_name'] = ts('Please select a field name');
780 }
781
b29a9e66 782 if ($in_selector && in_array($entityName, array(
6a488035 783 'Contribution', 'Participant', 'Membership', 'Activity'))) {
b29a9e66 784 $errors['in_selector'] = ts("'In Selector' cannot be checked for %1 fields.", array(1 => $entityName));
6a488035
TO
785 }
786
787 $isCustomField = FALSE;
788 $profileFieldName = CRM_Utils_Array::value(1, $fields['field_name']);
789 if ($profileFieldName) {
790 //get custom field id
791 $customFieldId = explode('_', $profileFieldName);
792 if ($customFieldId[0] == 'custom') {
793 $customField = new CRM_Core_DAO_CustomField();
794 $customField->id = $customFieldId[1];
795 $customField->find(TRUE);
796 $isCustomField = TRUE;
797 if (!empty($fields['field_id']) && !$customField->is_active && $is_active) {
798 $errors['field_name'] = ts('Cannot set this field "Active" since the selected custom field is disabled.');
799 }
800
801 //check if profile already has a different multi-record custom set field configured
802 $customGroupId = CRM_Core_BAO_CustomField::isMultiRecordField($profileFieldName);
803 if ($customGroupId) {
804 if ($profileMultiRecordCustomGid = CRM_Core_BAO_UFField::checkMultiRecordFieldExists($self->_gid)) {
805 if ($customGroupId != $profileMultiRecordCustomGid) {
806 $errors['field_name'] = ts("You cannot configure multi-record custom fields belonging to different custom sets in one profile");
807 }
808 }
809 }
810 }
811 }
812
b29a9e66
LS
813 // Get list of fields already in the group
814 $groupFields = CRM_Core_BAO_UFGroup::getFields($fields['group_id'], FALSE, NULL, NULL, NULL, TRUE, NULL, TRUE);
815 // Check if we already added a primary field of the same communication type
fdcc5d07 816 self::formRulePrimaryCheck($fields, $profileFieldName, $groupFields, $errors);
b29a9e66 817
6a488035
TO
818 //check profile is configured for double option process
819 //adding group field, email field should be present in the group
820 //fixed for issue CRM-2861 & CRM-4153
821 if (CRM_Core_BAO_UFGroup::isProfileDoubleOptin()) {
50c3e74b 822 if (CRM_Utils_Array::value(1, $fields['field_name']) == 'group') {
6a488035
TO
823 $dao = new CRM_Core_BAO_UFField();
824 $dao->uf_group_id = $fields['group_id'];
825 $dao->find();
826 $emailField = FALSE;
827 while ($dao->fetch()) {
828 //check email field is present in the group
829 if ($dao->field_name == 'email') {
830 $emailField = TRUE;
831 break;
832 }
833 }
834
835 if (!$emailField) {
836 $disableSettingURL = CRM_Utils_System::url(
837 'civicrm/admin/setting/preferences/mailing',
838 'reset=1'
839 );
840
841 $errors['field_name'] = ts('Your site is currently configured to require double-opt in when users join (subscribe) to Group(s) via a Profile form. In this mode, you need to include an Email field in a Profile BEFORE you can add the Group(s) field. This ensures that an opt-in confirmation email can be sent. Your site administrator can disable double opt-in on the civimail admin settings: <em>%1</em>', array(1 => $disableSettingURL));
842 }
843 }
844 }
845
846 //fix for CRM-3037
847 $fieldType = $fields['field_name'][0];
848
849 //get the group type.
850 $groupType = CRM_Core_BAO_UFGroup::calculateGroupType($self->_gid, FALSE, CRM_Utils_Array::value('field_id', $fields));
851
852 switch ($fieldType) {
853 case 'Contact':
854 self::formRuleSubType($fieldType, $groupType, $errors);
855 break;
856
857 case 'Individual':
858 if (in_array('Activity', $groupType) ||
859 in_array('Household', $groupType) ||
860 in_array('Organization', $groupType)
861 ) {
862
863 //CRM-7603 - need to support activity + individual.
864 //$errors['field_name'] =
865 //ts( 'Cannot add or update profile field type Individual with combination of Household or Organization or Activity' );
866 if (in_array('Household', $groupType) ||
867 in_array('Organization', $groupType)
868 ) {
869 $errors['field_name'] = ts('Cannot add or update profile field type Individual with combination of Household or Organization');
870 }
871 }
872 else {
873 self::formRuleSubType($fieldType, $groupType, $errors);
874 }
875 break;
876
877 case 'Household':
878 if (in_array('Activity', $groupType) || in_array('Individual', $groupType) || in_array('Organization', $groupType)) {
879 $errors['field_name'] = ts('Cannot add or update profile field type Household with combination of Individual or Organization or Activity');
880 }
881 else {
882 self::formRuleSubType($fieldType, $groupType, $errors);
883 }
884 break;
885
886 case 'Organization':
887 if (in_array('Activity', $groupType) || in_array('Household', $groupType) || in_array('Individual', $groupType)) {
888 $errors['field_name'] = ts('Cannot add or update profile field type Organization with combination of Household or Individual or Activity');
889 }
890 else {
891 self::formRuleSubType($fieldType, $groupType, $errors);
892 }
893 break;
894
895 case 'Activity':
896 if (in_array('Individual', $groupType) ||
897 in_array('Membership', $groupType) ||
898 in_array('Contribution', $groupType) ||
899 in_array('Organization', $groupType) ||
900 in_array('Household', $groupType) ||
901 in_array('Participant', $groupType)
902 ) {
903
904 //CRM-7603 - need to support activity + contact type.
905 //$errors['field_name'] =
906 //ts( 'Cannot add or update profile field type Activity with combination Participant or Membership or Contribution or Household or Organization or Individual' );
907 if (in_array('Membership', $groupType) ||
908 in_array('Contribution', $groupType) ||
909 in_array('Participant', $groupType)
910 ) {
911 $errors['field_name'] = ts('Cannot add or update profile field type Activity with combination Participant or Membership or Contribution');
912 }
913 }
914 else {
915 self::formRuleSubType($fieldType, $groupType, $errors);
916 }
917
918 if ($isCustomField && !isset($errors['field_name'])) {
919 self::formRuleCustomDataExtentColumnValue($customField, $self->_gid, $fieldType, $errors);
920 }
921 break;
922
923 case 'Participant':
924 if (in_array('Membership', $groupType) || in_array('Contribution', $groupType)
925 || in_array('Organization', $groupType) || in_array('Household', $groupType) || in_array('Activity', $groupType)
926 ) {
7132ac1f 927 $errors['field_name'] = ts('Cannot add or update profile field type Participant with combination of Activity or Membership or Contribution or Household or Organization.');
6a488035
TO
928 }
929 else {
930 self::formRuleSubType($fieldType, $groupType, $errors);
931 }
932 break;
933
934 case 'Contribution':
935 //special case where in we allow contribution + oganization fields, for on behalf feature
936 $profileId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup',
937 'on_behalf_organization', 'id', 'name'
938 );
939
940 if (in_array('Participant', $groupType) || in_array('Membership', $groupType)
941 || ($profileId != $self->_gid && in_array('Organization', $groupType)) || in_array('Household', $groupType) || in_array('Activity', $groupType)
942 ) {
943 $errors['field_name'] = ts('Cannot add or update profile field type Contribution with combination of Activity or Membership or Participant or Household or Organization');
944 }
945 else {
946 self::formRuleSubType($fieldType, $groupType, $errors);
947 }
948 break;
949
950 case 'Membership':
951 //special case where in we allow contribution + oganization fields, for on behalf feature
952 $profileId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup',
953 'on_behalf_organization', 'id', 'name'
954 );
955
956 if (in_array('Participant', $groupType) || in_array('Contribution', $groupType)
957 || ($profileId != $self->_gid && in_array('Organization', $groupType)) || in_array('Household', $groupType) || in_array('Activity', $groupType)
958 ) {
959 $errors['field_name'] = ts('Cannot add or update profile field type Membership with combination of Activity or Participant or Contribution or Household or Organization');
960 }
961 else {
962 self::formRuleSubType($fieldType, $groupType, $errors);
963 }
964 break;
965
966 default:
967 $profileType = CRM_Core_BAO_UFField::getProfileType($fields['group_id'], TRUE, FALSE, TRUE);
968 if (CRM_Contact_BAO_ContactType::isaSubType($fieldType)) {
969 if (CRM_Contact_BAO_ContactType::isaSubType($profileType)) {
970 if ($fieldType != $profileType) {
971 $errors['field_name'] = ts('Cannot add or update profile field type "%1" with combination of "%2".', array(1 => $fieldType, 2 => $profileType));
972 }
973 }
974 else {
975 $basicType = CRM_Contact_BAO_ContactType::getBasicType($fieldType);
976 if ($profileType &&
977 $profileType != $basicType &&
978 $profileType != 'Contact'
979 ) {
980 $errors['field_name'] = ts('Cannot add or update profile field type "%1" with combination of "%2".', array(1 => $fieldType, 2 => $profileType));
981 }
982 }
983 }
50c3e74b
DL
984 elseif (
985 CRM_Utils_Array::value(1, $fields['field_name']) == 'contact_sub_type' &&
986 !in_array($profileType, array('Individual', 'Household', 'Organization')) &&
6a488035
TO
987 !in_array($profileType, CRM_Contact_BAO_ContactType::subTypes())
988 ) {
989 $errors['field_name'] = ts('Cannot add or update profile field Contact Subtype as profile type is not one of Individual, Household or Organization.');
990 }
991 }
992 return empty($errors) ? TRUE : $errors;
993 }
994}