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