Merge pull request #14327 from seamuslee001/fix_warning_job_manager_test
[civicrm-core.git] / CRM / UF / Form / Field.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
6b83d5bd 6 | Copyright CiviCRM LLC (c) 2004-2019 |
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
6b83d5bd 31 * @copyright CiviCRM LLC (c) 2004-2019
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 *
9f266042 85 * @var bool
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);
be2fb01f
CW
108 $breadCrumb = [
109 [
353ffa53 110 'title' => ts('CiviCRM Profile Fields'),
6a488035 111 'url' => $url,
be2fb01f
CW
112 ],
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
be2fb01f 152 $this->_selectFields = [];
6a488035
TO
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) {
be2fb01f
CW
185 $this->addButtons([
186 [
c5c263ca
AH
187 'type' => 'next',
188 'name' => ts('Delete Profile Field'),
189 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
190 'isDefault' => TRUE,
be2fb01f
CW
191 ],
192 [
c5c263ca
AH
193 'type' => 'cancel',
194 'name' => ts('Cancel'),
be2fb01f
CW
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)) {
be2fb01f 202 $params = ['id' => $this->_id];
6a488035
TO
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
be2fb01f 217 $defaults['field_name'] = [
6a488035
TO
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 221 CRM_Utils_Array::value('phone_type_id', $defaults),
be2fb01f 222 ];
6a488035
TO
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) {
be2fb01f 233 $fieldValues = ['uf_group_id' => $this->_gid];
6a488035
TO
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
be2fb01f 248 $noSearchable = $hasWebsiteTypes = [];
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]);
be2fb01f 293 $this->_location_types = [
af9b09df 294 $defaultLocationType->id => $defaultLocation,
be2fb01f 295 ] + $this->_location_types;
6a488035
TO
296 }
297
be2fb01f 298 $this->_location_types = ['Primary'] + $this->_location_types;
6a488035
TO
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
be2fb01f
CW
305 $contactTypes = !empty($contactTypes) ? ['Contact' => 'Contacts'] + $contactTypes : [];
306 $sel1 = ['' => '- select -'] + $contactTypes;
6a488035
TO
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
be2fb01f 368 $this->_defaults = [];
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
be2fb01f 415 $sel->setOptions([$sel1, $sel2, $sel3, $sel4]);
6a488035
TO
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,
be2fb01f 430 ['onChange' => "showHideSeletorSearch(this.value);"]
6a488035
TO
431 );
432
433 //CRM-4363
be2fb01f 434 $js = ['onChange' => "mixProfile();"];
6a488035 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
f20d2b0d 442 $this->add('number', 'weight', ts('Order'), $attributes['weight'], TRUE);
6a488035
TO
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) {
be2fb01f 458 $js = ['onclick' => "return verify( );"];
6a488035
TO
459 }
460
461 // add buttons
be2fb01f
CW
462 $this->addButtons([
463 [
c5c263ca
AH
464 'type' => 'next',
465 'name' => ts('Save'),
466 'isDefault' => TRUE,
467 'js' => $js,
be2fb01f
CW
468 ],
469 [
c5c263ca
AH
470 'type' => 'next',
471 'name' => ts('Save and New'),
472 'subName' => 'new',
473 'js' => $js,
be2fb01f
CW
474 ],
475 [
c5c263ca
AH
476 'type' => 'cancel',
477 'name' => ts('Cancel'),
be2fb01f
CW
478 ],
479 ]);
6a488035 480
be2fb01f 481 $this->addFormRule(['CRM_UF_Form_Field', 'formRule'], $this);
6a488035
TO
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'),
be2fb01f 487 ['onclick' => "location.href='civicrm/admin/uf/group/field?reset=1&action=browse&gid=" . $this->_gid . "'"]
6a488035
TO
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 501 if ($this->_action & CRM_Core_Action::DELETE) {
be2fb01f 502 $fieldValues = ['uf_group_id' => $this->_gid];
6a488035 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
aa9732b9
CW
534 // If field_name is missing, it's formatting
535 $fieldName = CRM_Utils_Array::value(1, $params['field_name'], 'formatting');
6a488035 536
6a488035 537 //check for duplicate fields
b99bd217
ASB
538 $apiFormattedParams = $params;
539 $apiFormattedParams['field_type'] = $params['field_name'][0];
78580003 540 $apiFormattedParams['field_name'] = $fieldName;
b99bd217 541 if (!empty($params['field_name'][2])) {
78580003 542 if ($fieldName === 'url') {
b99bd217
ASB
543 $apiFormattedParams['website_type_id'] = $params['field_name'][2];
544 }
545 else {
546 $apiFormattedParams['location_type_id'] = $params['field_name'][2];
547 }
548 }
2e74ff55
SP
549 elseif ($params['field_name'][2] == 0) {
550 // 0 is Primary location type
551 $apiFormattedParams['location_type_id'] = NULL;
552 }
b99bd217
ASB
553 if (!empty($params['field_name'][3])) {
554 $apiFormattedParams['phone_type_id'] = $params['field_name'][3];
555 }
556
557 if ($apiFormattedParams['field_type'] != "Formatting" && CRM_Core_BAO_UFField::duplicateField($apiFormattedParams)) {
558 CRM_Core_Error::statusBounce(ts('The selected field already exists in this profile.'), NULL, ts('Field Not Added'));
6a488035
TO
559 }
560 else {
78580003
SB
561 $apiFormattedParams['weight'] = CRM_Core_BAO_UFField::autoWeight($params);
562 civicrm_api3('UFField', 'create', $apiFormattedParams);
6a488035
TO
563
564 //reset other field is searchable and in selector settings, CRM-4363
565 if ($this->_hasSearchableORInSelector &&
be2fb01f 566 in_array($apiFormattedParams['field_type'], ['Participant', 'Contribution', 'Membership', 'Activity', 'Case'])
6a488035
TO
567 ) {
568 CRM_Core_BAO_UFField::resetInSelectorANDSearchable($this->_gid);
569 }
570
78580003 571 $this->setMessageIfCountryNotAboveState($fieldName, CRM_Utils_Array::value('location_type_id', $apiFormattedParams), $apiFormattedParams['weight'], $apiFormattedParams['uf_group_id']);
6a488035 572
6a488035 573 CRM_Core_Session::setStatus(ts('Your CiviCRM Profile Field \'%1\' has been saved to \'%2\'.',
be2fb01f 574 [1 => $name, 2 => $this->_title]
353ffa53 575 ), ts('Profile Field Saved'), 'success');
6a488035
TO
576 }
577 $buttonName = $this->controller->getButtonName();
578
579 $session = CRM_Core_Session::singleton();
580 if ($buttonName == $this->getButtonName('next', 'new')) {
6a488035 581 $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/uf/group/field/add',
78580003 582 "reset=1&action=add&gid={$this->_gid}"
353ffa53 583 ));
6a488035
TO
584 }
585 else {
586 $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/uf/group/field',
353ffa53
TO
587 "reset=1&action=browse&gid={$this->_gid}"
588 ));
6a488035
TO
589 }
590 }
591
592 /**
100fef9d 593 * Validation rule for subtype.
6a488035 594 *
5ce1712d
TO
595 * @param string $fieldType
596 * Type of field.
a1a2a83d
TO
597 * @param array $groupType
598 * Contains all groupTypes.
6a488035 599 * @param array $errors
a1a2a83d 600 * List of errors to be posted back to the form.
6a488035 601 */
a1a2a83d 602 public static function formRuleSubType($fieldType, $groupType, &$errors) {
be2fb01f 603 if (in_array($fieldType, [
353ffa53
TO
604 'Participant',
605 'Contribution',
606 'Membership',
a130e045 607 'Activity',
be2fb01f 608 ])) {
6a488035
TO
609 $individualSubTypes = CRM_Contact_BAO_ContactType::subTypes('Individual');
610 foreach ($groupType as $value) {
611 if (!in_array($value, $individualSubTypes) &&
be2fb01f 612 !in_array($value, [
353ffa53
TO
613 'Participant',
614 'Contribution',
615 'Membership',
616 'Individual',
617 'Contact',
618 'Activity',
8d172516 619 'Formatting',
be2fb01f 620 ])
6a488035 621 ) {
be2fb01f 622 $errors['field_name'] = ts('Cannot add or update profile field "%1" with combination of Household or Organization or any subtypes of Household or Organization.', [1 => $fieldType]);
6a488035
TO
623 break;
624 }
625 }
626 }
627 else {
628 $basicType = CRM_Contact_BAO_ContactType::getBasicType($groupType);
629 if ($basicType) {
630 if (!is_array($basicType)) {
be2fb01f 631 $basicType = [$basicType];
6a488035 632 }
1be099c7 633 if (!in_array($fieldType, $basicType) && $fieldType != 'Contact') {
6a488035 634 $errors['field_name'] = ts('Cannot add or update profile field type "%1" with combination of subtype other than "%1".',
be2fb01f 635 [1 => $fieldType]
6a488035
TO
636 );
637 }
638 }
639 }
640 }
641
642 /**
100fef9d 643 * Validation rule for custom data extends entity column values.
6a488035 644 *
5ce1712d
TO
645 * @param Object $customField
646 * Custom field.
647 * @param int $gid
648 * Group Id.
649 * @param string $fieldType
650 * Group type of the field.
651 * @param array $errors
652 * Collect errors.
6a488035 653 *
a130e045 654 * @return array
a6c01b45 655 * list of errors to be posted back to the form
6a488035 656 */
00be9182 657 public static function formRuleCustomDataExtentColumnValue($customField, $gid, $fieldType, &$errors) {
6a488035 658 // fix me : check object $customField
be2fb01f 659 if (in_array($fieldType, [
353ffa53
TO
660 'Participant',
661 'Contribution',
662 'Membership',
663 'Activity',
a130e045 664 'Case',
be2fb01f
CW
665 ])) {
666 $params = ['id' => $customField->custom_group_id];
667 $customGroup = [];
6a488035 668 CRM_Core_BAO_CustomGroup::retrieve($params, $customGroup);
8cc574cf 669 if (($fieldType != CRM_Utils_Array::value('extends', $customGroup)) || empty($customGroup['extends_entity_column_value'])) {
6a488035
TO
670 return $errors;
671 }
672
be2fb01f 673 $extendsColumnValues = [];
6a488035
TO
674 foreach (explode(CRM_Core_DAO::VALUE_SEPARATOR, $customGroup['extends_entity_column_value']) as $val) {
675 if ($val) {
676 $extendsColumnValues[] = $val;
677 }
678 }
679
680 if (empty($extendsColumnValues)) {
681 return $errors;
682 }
683
684 $fieldTypeValues = CRM_Core_BAO_UFGroup::groupTypeValues($gid, $fieldType);
a7488080 685 if (empty($fieldTypeValues[$fieldType])) {
3655bea4 686 return $errors;
6a488035
TO
687 }
688
689 $disallowedTypes = array_diff($extendsColumnValues, $fieldTypeValues[$fieldType]);
690 if (!empty($disallowedTypes)) {
691 $errors['field_name'] = ts('Profile is already having custom fields extending different group types, you can not add or update this custom field.');
692 }
693 }
694 }
695
b29a9e66 696 /**
fdcc5d07 697 * Validation rule to prevent multiple fields of primary location type within the same communication type.
b29a9e66 698 *
5ce1712d
TO
699 * @param array $fields
700 * Submitted fields.
701 * @param string $profileFieldName
702 * Group Id.
703 * @param array $groupFields
704 * List of fields already in the group.
705 * @param array $errors
706 * Collect errors.
b29a9e66 707 *
b29a9e66 708 */
00be9182 709 public static function formRulePrimaryCheck($fields, $profileFieldName, $groupFields, &$errors) {
b29a9e66 710 //FIXME: This may need to also apply to website fields if they are refactored to allow more than one per profile
be2fb01f 711 $checkPrimary = ['phone' => 'civicrm_phone.phone', 'phone_and_ext' => 'civicrm_phone.phone'];
b29a9e66
LS
712 $whereCheck = NULL;
713 $primaryOfSameTypeFound = NULL;
fdcc5d07 714 $fieldID = empty($fields['field_id']) ? 0 : $fields['field_id'];
b29a9e66
LS
715 // Is this a primary location type field of interest
716 if (array_key_exists($profileFieldName, $checkPrimary)) {
717 $whereCheck = $checkPrimary[$profileFieldName];
718 }
719 $potentialLocationType = CRM_Utils_Array::value(2, $fields['field_name']);
720
721 if ($whereCheck && $potentialLocationType == 0) {
722 $primaryOfSameTypeFound = '';
723
724 foreach ($groupFields as $groupField) {
725 // if it is a phone
9147c186 726 if ($groupField['where'] == $whereCheck && is_null($groupField['location_type_id']) && $groupField['field_id'] != $fieldID) {
b29a9e66 727 $primaryOfSameTypeFound = $groupField['title'];
fdcc5d07 728 break;
b29a9e66
LS
729 }
730 }
731 if ($primaryOfSameTypeFound) {
be2fb01f 732 $errors['field_name'] = ts('You have already added a primary location field of this type: %1', [1 => $primaryOfSameTypeFound]);
b29a9e66
LS
733 }
734 }
735 }
736
6a488035 737 /**
fe482240 738 * Global validation rules for the form.
6a488035 739 *
5ce1712d
TO
740 * @param array $fields
741 * Posted values of the form.
6a488035 742 *
77b97be7
EM
743 * @param $files
744 * @param $self
745 *
a6c01b45
CW
746 * @return array
747 * list of errors to be posted back to the form
6a488035 748 */
00be9182 749 public static function formRule($fields, $files, $self) {
353ffa53 750 $is_required = CRM_Utils_Array::value('is_required', $fields, FALSE);
6a488035 751 $is_registration = CRM_Utils_Array::value('is_registration', $fields, FALSE);
353ffa53
TO
752 $is_view = CRM_Utils_Array::value('is_view', $fields, FALSE);
753 $in_selector = CRM_Utils_Array::value('in_selector', $fields, FALSE);
754 $is_active = CRM_Utils_Array::value('is_active', $fields, FALSE);
6a488035 755
be2fb01f 756 $errors = [];
6a488035
TO
757 if ($is_view && $is_registration) {
758 $errors['is_registration'] = ts('View Only cannot be selected if this field is to be included on the registration form');
759 }
760 if ($is_view && $is_required) {
761 $errors['is_view'] = ts('A View Only field cannot be required');
762 }
763
b29a9e66
LS
764 $entityName = $fields['field_name'][0];
765 if (!$entityName) {
6a488035
TO
766 $errors['field_name'] = ts('Please select a field name');
767 }
768
be2fb01f 769 if ($in_selector && in_array($entityName, [
c5c263ca
AH
770 'Contribution',
771 'Participant',
772 'Membership',
773 'Activity',
be2fb01f 774 ])
353ffa53 775 ) {
be2fb01f 776 $errors['in_selector'] = ts("'Results Column' cannot be checked for %1 fields.", [1 => $entityName]);
6a488035
TO
777 }
778
779 $isCustomField = FALSE;
780 $profileFieldName = CRM_Utils_Array::value(1, $fields['field_name']);
781 if ($profileFieldName) {
782 //get custom field id
783 $customFieldId = explode('_', $profileFieldName);
784 if ($customFieldId[0] == 'custom') {
785 $customField = new CRM_Core_DAO_CustomField();
786 $customField->id = $customFieldId[1];
787 $customField->find(TRUE);
788 $isCustomField = TRUE;
789 if (!empty($fields['field_id']) && !$customField->is_active && $is_active) {
790 $errors['field_name'] = ts('Cannot set this field "Active" since the selected custom field is disabled.');
791 }
792
793 //check if profile already has a different multi-record custom set field configured
794 $customGroupId = CRM_Core_BAO_CustomField::isMultiRecordField($profileFieldName);
795 if ($customGroupId) {
796 if ($profileMultiRecordCustomGid = CRM_Core_BAO_UFField::checkMultiRecordFieldExists($self->_gid)) {
797 if ($customGroupId != $profileMultiRecordCustomGid) {
798 $errors['field_name'] = ts("You cannot configure multi-record custom fields belonging to different custom sets in one profile");
799 }
800 }
801 }
802 }
803 }
804
b29a9e66
LS
805 // Get list of fields already in the group
806 $groupFields = CRM_Core_BAO_UFGroup::getFields($fields['group_id'], FALSE, NULL, NULL, NULL, TRUE, NULL, TRUE);
807 // Check if we already added a primary field of the same communication type
fdcc5d07 808 self::formRulePrimaryCheck($fields, $profileFieldName, $groupFields, $errors);
b29a9e66 809
6a488035
TO
810 //check profile is configured for double option process
811 //adding group field, email field should be present in the group
812 //fixed for issue CRM-2861 & CRM-4153
813 if (CRM_Core_BAO_UFGroup::isProfileDoubleOptin()) {
50c3e74b 814 if (CRM_Utils_Array::value(1, $fields['field_name']) == 'group') {
6a488035
TO
815 $dao = new CRM_Core_BAO_UFField();
816 $dao->uf_group_id = $fields['group_id'];
817 $dao->find();
818 $emailField = FALSE;
819 while ($dao->fetch()) {
820 //check email field is present in the group
821 if ($dao->field_name == 'email') {
822 $emailField = TRUE;
823 break;
824 }
825 }
826
827 if (!$emailField) {
828 $disableSettingURL = CRM_Utils_System::url(
829 'civicrm/admin/setting/preferences/mailing',
830 'reset=1'
831 );
832
be2fb01f 833 $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>', [1 => $disableSettingURL]);
6a488035
TO
834 }
835 }
836 }
837
838 //fix for CRM-3037
839 $fieldType = $fields['field_name'][0];
840
841 //get the group type.
842 $groupType = CRM_Core_BAO_UFGroup::calculateGroupType($self->_gid, FALSE, CRM_Utils_Array::value('field_id', $fields));
843
844 switch ($fieldType) {
845 case 'Contact':
846 self::formRuleSubType($fieldType, $groupType, $errors);
847 break;
848
849 case 'Individual':
850 if (in_array('Activity', $groupType) ||
851 in_array('Household', $groupType) ||
852 in_array('Organization', $groupType)
853 ) {
854
855 //CRM-7603 - need to support activity + individual.
856 //$errors['field_name'] =
857 //ts( 'Cannot add or update profile field type Individual with combination of Household or Organization or Activity' );
858 if (in_array('Household', $groupType) ||
859 in_array('Organization', $groupType)
860 ) {
861 $errors['field_name'] = ts('Cannot add or update profile field type Individual with combination of Household or Organization');
862 }
863 }
864 else {
865 self::formRuleSubType($fieldType, $groupType, $errors);
866 }
867 break;
868
869 case 'Household':
870 if (in_array('Activity', $groupType) || in_array('Individual', $groupType) || in_array('Organization', $groupType)) {
871 $errors['field_name'] = ts('Cannot add or update profile field type Household with combination of Individual or Organization or Activity');
872 }
873 else {
874 self::formRuleSubType($fieldType, $groupType, $errors);
875 }
876 break;
877
878 case 'Organization':
879 if (in_array('Activity', $groupType) || in_array('Household', $groupType) || in_array('Individual', $groupType)) {
880 $errors['field_name'] = ts('Cannot add or update profile field type Organization with combination of Household or Individual or Activity');
881 }
882 else {
883 self::formRuleSubType($fieldType, $groupType, $errors);
884 }
885 break;
886
887 case 'Activity':
888 if (in_array('Individual', $groupType) ||
889 in_array('Membership', $groupType) ||
890 in_array('Contribution', $groupType) ||
891 in_array('Organization', $groupType) ||
892 in_array('Household', $groupType) ||
893 in_array('Participant', $groupType)
894 ) {
895
896 //CRM-7603 - need to support activity + contact type.
897 //$errors['field_name'] =
898 //ts( 'Cannot add or update profile field type Activity with combination Participant or Membership or Contribution or Household or Organization or Individual' );
899 if (in_array('Membership', $groupType) ||
900 in_array('Contribution', $groupType) ||
901 in_array('Participant', $groupType)
902 ) {
903 $errors['field_name'] = ts('Cannot add or update profile field type Activity with combination Participant or Membership or Contribution');
904 }
905 }
906 else {
907 self::formRuleSubType($fieldType, $groupType, $errors);
908 }
909
910 if ($isCustomField && !isset($errors['field_name'])) {
911 self::formRuleCustomDataExtentColumnValue($customField, $self->_gid, $fieldType, $errors);
912 }
913 break;
914
915 case 'Participant':
916 if (in_array('Membership', $groupType) || in_array('Contribution', $groupType)
917 || in_array('Organization', $groupType) || in_array('Household', $groupType) || in_array('Activity', $groupType)
918 ) {
7132ac1f 919 $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
920 }
921 else {
922 self::formRuleSubType($fieldType, $groupType, $errors);
923 }
924 break;
925
926 case 'Contribution':
927 //special case where in we allow contribution + oganization fields, for on behalf feature
928 $profileId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup',
929 'on_behalf_organization', 'id', 'name'
930 );
931
932 if (in_array('Participant', $groupType) || in_array('Membership', $groupType)
933 || ($profileId != $self->_gid && in_array('Organization', $groupType)) || in_array('Household', $groupType) || in_array('Activity', $groupType)
934 ) {
935 $errors['field_name'] = ts('Cannot add or update profile field type Contribution with combination of Activity or Membership or Participant or Household or Organization');
936 }
937 else {
938 self::formRuleSubType($fieldType, $groupType, $errors);
939 }
940 break;
941
942 case 'Membership':
943 //special case where in we allow contribution + oganization fields, for on behalf feature
944 $profileId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup',
945 'on_behalf_organization', 'id', 'name'
946 );
947
948 if (in_array('Participant', $groupType) || in_array('Contribution', $groupType)
949 || ($profileId != $self->_gid && in_array('Organization', $groupType)) || in_array('Household', $groupType) || in_array('Activity', $groupType)
950 ) {
951 $errors['field_name'] = ts('Cannot add or update profile field type Membership with combination of Activity or Participant or Contribution or Household or Organization');
952 }
953 else {
954 self::formRuleSubType($fieldType, $groupType, $errors);
955 }
956 break;
957
958 default:
959 $profileType = CRM_Core_BAO_UFField::getProfileType($fields['group_id'], TRUE, FALSE, TRUE);
960 if (CRM_Contact_BAO_ContactType::isaSubType($fieldType)) {
961 if (CRM_Contact_BAO_ContactType::isaSubType($profileType)) {
962 if ($fieldType != $profileType) {
be2fb01f 963 $errors['field_name'] = ts('Cannot add or update profile field type "%1" with combination of "%2".', [
c5c263ca
AH
964 1 => $fieldType,
965 2 => $profileType,
be2fb01f 966 ]);
6a488035
TO
967 }
968 }
969 else {
970 $basicType = CRM_Contact_BAO_ContactType::getBasicType($fieldType);
971 if ($profileType &&
972 $profileType != $basicType &&
973 $profileType != 'Contact'
974 ) {
be2fb01f 975 $errors['field_name'] = ts('Cannot add or update profile field type "%1" with combination of "%2".', [
c5c263ca
AH
976 1 => $fieldType,
977 2 => $profileType,
be2fb01f 978 ]);
6a488035
TO
979 }
980 }
981 }
50c3e74b
DL
982 elseif (
983 CRM_Utils_Array::value(1, $fields['field_name']) == 'contact_sub_type' &&
be2fb01f 984 !in_array($profileType, ['Individual', 'Household', 'Organization']) &&
6a488035
TO
985 !in_array($profileType, CRM_Contact_BAO_ContactType::subTypes())
986 ) {
987 $errors['field_name'] = ts('Cannot add or update profile field Contact Subtype as profile type is not one of Individual, Household or Organization.');
988 }
989 }
990 return empty($errors) ? TRUE : $errors;
991 }
96025800 992
78580003
SB
993 /**
994 * Set a message warning the user about putting country first to render states, if required.
995 *
996 * @param string $fieldName
997 * @param int $locationTypeID
998 * @param int $weight
999 * @param int $ufGroupID
1000 */
1001 protected function setMessageIfCountryNotAboveState($fieldName, $locationTypeID, $weight, $ufGroupID) {
1002 $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.');
1003
be2fb01f 1004 if (in_array($fieldName, [
3655bea4
SL
1005 'country',
1006 'state_province',
1007 ]) && count(CRM_Core_Config::singleton()->countryLimit) > 1
78580003
SB
1008 ) {
1009 // get state or country field weight if exists
1010 $ufFieldDAO = new CRM_Core_DAO_UFField();
1011 $ufFieldDAO->field_name = ($fieldName == 'state_province' ? 'country' : 'state_province');
1012 $ufFieldDAO->location_type_id = $locationTypeID;
1013 $ufFieldDAO->uf_group_id = $ufGroupID;
1014
1015 if ($ufFieldDAO->find(TRUE)) {
1016 if ($ufFieldDAO->field_name == 'country' && $ufFieldDAO->weight > $weight) {
1017 CRM_Core_Session::setStatus($message);
1018 }
1019 elseif ($ufFieldDAO->field_name == 'state_province' && $ufFieldDAO->weight < $weight) {
1020 CRM_Core_Session::setStatus($message);
1021 }
1022 }
1023 }
1024 }
1025
6a488035 1026}