Merge pull request #6892 from eileenmcnaughton/CRM-17225-report-save
[civicrm-core.git] / CRM / UF / Form / Field.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2015
32 */
33
34 /**
35 * Form to process actions on the field aspect of Custom.
36 */
37 class CRM_UF_Form_Field extends CRM_Core_Form {
38
39 /**
40 * The uf group id saved to the session for an update.
41 *
42 * @var int
43 */
44 protected $_gid;
45
46 /**
47 * The field id, used when editing the field.
48 *
49 * @var int
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
57 */
58 protected $_fields;
59
60 /**
61 * The title for field.
62 *
63 * @var int
64 */
65 protected $_title;
66
67 /**
68 * The set of fields sent to the select element.
69 *
70 * @var array
71 */
72 protected $_selectFields;
73
74 /**
75 * store fields with if locationtype exits status.
76 *
77 * @var array
78 */
79 protected $_hasLocationTypes;
80
81 /**
82 * Is this profile has searchable field.
83 * or is any field having in selector true.
84 *
85 * @var boolean.
86 */
87 protected $_hasSearchableORInSelector;
88
89 /**
90 * Set variables up before form is built.
91 *
92 * @return void
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');
99
100 $this->setPageTitle(ts('Profile Field'));
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);
108 $breadCrumb = array(
109 array(
110 'title' => ts('CiviCRM Profile Fields'),
111 'url' => $url,
112 ),
113 );
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
146 if (CRM_Core_Permission::access('CiviCase')) {
147 $this->_fields = array_merge(CRM_Case_BAO_Query::getFields(), $this->_fields);
148 }
149
150 $this->_fields = array_merge($this->_fields, CRM_Contact_BAO_Query_Hook::singleton()->getFields());
151
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 /**
179 * Build the form object.
180 *
181 * @return void
182 */
183 public function buildQuickForm() {
184 if ($this->_action & CRM_Core_Action::DELETE) {
185 $this->addButtons(array(
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 )
197 );
198 return;
199 }
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
208 $specialFields = CRM_Core_BAO_UFGroup::getLocationFields();
209
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']),
220 ($defaults['field_name'] == "url") ? $defaults['website_type_id'] : $defaults['location_type_id'],
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
248 $noSearchable = $hasWebsiteTypes = array();
249 $addressCustomFields = array_keys(CRM_Core_BAO_CustomField::getFieldsForImport('Address'));
250
251 foreach ($fields as $key => $value) {
252 foreach ($value as $key1 => $value1) {
253 //CRM-2676, replacing the conflict for same custom field name from different custom group.
254 if ($customFieldId = CRM_Core_BAO_CustomField::getKeyID($key1)) {
255 $customGroupId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', $customFieldId, 'custom_group_id');
256 $customGroupName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $customGroupId, 'title');
257 $this->_mapperFields[$key][$key1] = $value1['title'] . ' :: ' . $customGroupName;
258 if (in_array($key1, $addressCustomFields)) {
259 $noSearchable[] = $value1['title'] . ' :: ' . $customGroupName;
260 }
261 }
262 else {
263 $this->_mapperFields[$key][$key1] = $value1['title'];
264 }
265 $hasLocationTypes[$key][$key1] = CRM_Utils_Array::value('hasLocationType', $value1);
266 $hasWebsiteTypes[$key][$key1] = CRM_Utils_Array::value('hasWebsiteType', $value1);
267 // hide the 'is searchable' field for 'File' custom data
268 if (isset($value1['data_type']) &&
269 isset($value1['html_type']) &&
270 (($value1['data_type'] == 'File' && $value1['html_type'] == 'File')
271 || ($value1['data_type'] == 'Link' && $value1['html_type'] == 'Link')
272 )
273 ) {
274 if (!in_array($value1['title'], $noSearchable)) {
275 $noSearchable[] = $value1['title'];
276 }
277 }
278 }
279 }
280 $this->assign('noSearchable', $noSearchable);
281
282 $this->_location_types = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
283 $defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
284 $this->_website_types = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Website', 'website_type_id');
285
286 /**
287 * FIXME: dirty hack to make the default option show up first. This
288 * avoids a mozilla browser bug with defaults on dynamically constructed
289 * selector widgets.
290 */
291 if ($defaultLocationType) {
292 $defaultLocation = $this->_location_types[$defaultLocationType->id];
293 unset($this->_location_types[$defaultLocationType->id]);
294 $this->_location_types = array(
295 $defaultLocationType->id => $defaultLocation,
296 ) + $this->_location_types;
297 }
298
299 $this->_location_types = array('Primary') + $this->_location_types;
300
301 // since we need a hierarchical list to display contact types & subtypes,
302 // this is what we going to display in first selector
303 $contactTypes = CRM_Contact_BAO_ContactType::getSelectElements(FALSE, FALSE);
304 unset($contactTypes['']);
305
306 $contactTypes = !empty($contactTypes) ? array('Contact' => 'Contacts') + $contactTypes : array();
307 $sel1 = array('' => '- select -') + $contactTypes;
308
309 if (!empty($fields['Activity'])) {
310 $sel1['Activity'] = 'Activity';
311 }
312
313 if (CRM_Core_Permission::access('CiviEvent')) {
314 $sel1['Participant'] = 'Participants';
315 }
316
317 if (!empty($fields['Contribution'])) {
318 $sel1['Contribution'] = 'Contributions';
319 }
320
321 if (!empty($fields['Membership'])) {
322 $sel1['Membership'] = 'Membership';
323 }
324
325 if (!empty($fields['Case'])) {
326 $sel1['Case'] = 'Case';
327 }
328
329 if (!empty($fields['Formatting'])) {
330 $sel1['Formatting'] = 'Formatting';
331 }
332
333 foreach ($sel1 as $key => $sel) {
334 if ($key) {
335 $sel2[$key] = $this->_mapperFields[$key];
336 }
337 }
338 $sel3[''] = NULL;
339 $phoneTypes = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
340 ksort($phoneTypes);
341
342 foreach ($sel1 as $k => $sel) {
343 if ($k) {
344 foreach ($this->_location_types as $key => $value) {
345 $sel4[$k]['phone'][$key] = &$phoneTypes;
346 $sel4[$k]['phone_and_ext'][$key] = &$phoneTypes;
347 }
348 }
349 }
350
351 foreach ($sel1 as $k => $sel) {
352 if ($k) {
353 if (is_array($this->_mapperFields[$k])) {
354 foreach ($this->_mapperFields[$k] as $key => $value) {
355 if ($hasLocationTypes[$k][$key]) {
356 $sel3[$k][$key] = $this->_location_types;
357 }
358 elseif ($hasWebsiteTypes[$k][$key]) {
359 $sel3[$k][$key] = $this->_website_types;
360 }
361 else {
362 $sel3[$key] = NULL;
363 }
364 }
365 }
366 }
367 }
368
369 $this->_defaults = array();
370 $js = "<script type='text/javascript'>\n";
371 $formName = "document.{$this->_name}";
372
373 $alreadyMixProfile = FALSE;
374 if (CRM_Core_BAO_UFField::checkProfileType($this->_gid)) {
375 $alreadyMixProfile = TRUE;
376 }
377 $this->assign('alreadyMixProfile', $alreadyMixProfile);
378
379 $sel = &$this->addElement('hierselect', 'field_name', ts('Field Name'));
380
381 $formValues = $this->exportValues();
382
383 if (empty($formValues)) {
384 for ($k = 1; $k < 4; $k++) {
385 if (!isset($defaults['field_name'][$k])) {
386 $js .= "{$formName}['field_name[$k]'].style.display = 'none';\n";
387 }
388 }
389 }
390 else {
391 if (!empty($formValues['field_name'])) {
392 for ($key = 1; $key < 4; $key++) {
393 if (!isset($formValues['field_name'][$key])) {
394 $js .= "{$formName}['field_name[$key]'].style.display = 'none';\n";
395 }
396 else {
397 $js .= "{$formName}['field_name[$key]'].style.display = '';\n";
398 }
399 }
400 }
401 else {
402 for ($k = 1; $k < 4; $k++) {
403 if (!isset($defaults['field_name'][$k])) {
404 $js .= "{$formName}['field_name[$k]'].style.display = 'none';\n";
405 }
406 }
407 }
408 }
409
410 foreach ($sel2 as $k => $v) {
411 if (is_array($sel2[$k])) {
412 asort($sel2[$k]);
413 }
414 }
415
416 $sel->setOptions(array($sel1, $sel2, $sel3, $sel4));
417
418 // proper interpretation of spec in CRM-8732
419 if (!isset($this->_id) && in_array('Search Profile', $otherModules)) {
420 $defaults['visibility'] = 'Public Pages and Listings';
421 }
422
423 $js .= "</script>\n";
424 $this->assign('initHideBoxes', $js);
425
426 $this->add('select',
427 'visibility',
428 ts('Visibility'),
429 CRM_Core_SelectValues::ufVisibility(),
430 TRUE,
431 array('onChange' => "showHideSeletorSearch(this.value);")
432 );
433
434 //CRM-4363
435 $js = array('onChange' => "mixProfile();");
436 // should the field appear in selectors (as a column)?
437 $this->add('checkbox', 'in_selector', ts('Results Column?'), NULL, NULL, $js);
438 $this->add('checkbox', 'is_searchable', ts('Searchable?'), NULL, NULL, $js);
439
440 $attributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_UFField');
441
442 // weight
443 $this->add('text', 'weight', ts('Order'), $attributes['weight'], TRUE);
444 $this->addRule('weight', ts('is a numeric field'), 'numeric');
445
446 $this->add('textarea', 'help_pre', ts('Field Pre Help'), $attributes['help_pre']);
447 $this->add('textarea', 'help_post', ts('Field Post Help'), $attributes['help_post']);
448
449 $this->add('checkbox', 'is_required', ts('Required?'));
450
451 $this->add('checkbox', 'is_multi_summary', ts('Include in multi-record listing?'));
452 $this->add('checkbox', 'is_active', ts('Active?'));
453 $this->add('checkbox', 'is_view', ts('View Only?'));
454
455 // $this->add( 'checkbox', 'is_registration', ts( 'Display in Registration Form?' ) );
456 //$this->add( 'checkbox', 'is_match' , ts( 'Key to Match Contacts?' ) );
457
458 $this->add('text', 'label', ts('Field Label'), $attributes['label']);
459
460 $js = NULL;
461 if ($this->_hasSearchableORInSelector) {
462 $js = array('onclick' => "return verify( );");
463 }
464
465 // add buttons
466 $this->addButtons(array(
467 array(
468 'type' => 'next',
469 'name' => ts('Save'),
470 'isDefault' => TRUE,
471 'js' => $js,
472 ),
473 array(
474 'type' => 'next',
475 'name' => ts('Save and New'),
476 'subName' => 'new',
477 'js' => $js,
478 ),
479 array(
480 'type' => 'cancel',
481 'name' => ts('Cancel'),
482 ),
483 )
484 );
485
486 $this->addFormRule(array('CRM_UF_Form_Field', 'formRule'), $this);
487
488 // if view mode pls freeze it with the done button.
489 if ($this->_action & CRM_Core_Action::VIEW) {
490 $this->freeze();
491 $this->addElement('button', 'done', ts('Done'),
492 array('onclick' => "location.href='civicrm/admin/uf/group/field?reset=1&action=browse&gid=" . $this->_gid . "'")
493 );
494 }
495
496 $this->setDefaults($defaults);
497 }
498
499 /**
500 * Process the form.
501 *
502 * @return void
503 */
504 public function postProcess() {
505
506 if ($this->_action & CRM_Core_Action::DELETE) {
507 $fieldValues = array('uf_group_id' => $this->_gid);
508 CRM_Utils_Weight::delWeight('CRM_Core_DAO_UFField', $this->_id, $fieldValues);
509 $deleted = CRM_Core_BAO_UFField::del($this->_id);
510
511 //update group_type every time. CRM-3608
512 if ($this->_gid && $deleted) {
513 //get the profile type.
514 $fieldsType = CRM_Core_BAO_UFGroup::calculateGroupType($this->_gid, TRUE);
515 CRM_Core_BAO_UFGroup::updateGroupTypes($this->_gid, $fieldsType);
516 }
517
518 CRM_Core_Session::setStatus(ts('Selected Profile Field has been deleted.'), ts('Profile Field Deleted'), 'success');
519 return;
520 }
521
522 // store the submitted values in an array
523 $params = $this->controller->exportValues('Field');
524 $params['uf_group_id'] = $this->_gid;
525 if ($params['visibility'] == 'User and User Admin Only') {
526 $params['is_searchable'] = $params['in_selector'] = 0;
527 }
528
529 if ($this->_action & CRM_Core_Action::UPDATE) {
530 $params['id'] = $this->_id;
531 }
532
533 $name = NULL;
534 if (isset($params['field_name'][1]) && isset($this->_selectFields[$params['field_name'][1]])) {
535 // we dont get a name for a html formatting element
536 $name = $this->_selectFields[$params['field_name'][1]];
537 }
538
539 //Hack for Formatting Field Name
540 if ($params['field_name'][0] == 'Formatting') {
541 $params['field_name'][1] = 'formatting_' . rand(1000, 9999);
542 }
543
544 //check for duplicate fields
545 if ($params["field_name"][0] != "Formatting" && CRM_Core_BAO_UFField::duplicateField($params, array('uf_group' => $params['uf_group_id'], 'uf_field' => $params['id']))) {
546 CRM_Core_Session::setStatus(ts('The selected field already exists in this profile.'), ts('Field Not Added'), 'error');
547 return;
548 }
549 else {
550 $params['weight'] = CRM_Core_BAO_UFField::autoWeight($params);
551 $ufField = CRM_Core_BAO_UFField::add($params);
552
553 //reset other field is searchable and in selector settings, CRM-4363
554 if ($this->_hasSearchableORInSelector &&
555 in_array($ufField->field_type, array('Participant', 'Contribution', 'Membership', 'Activity', 'Case'))
556 ) {
557 CRM_Core_BAO_UFField::resetInSelectorANDSearchable($this->_gid);
558 }
559
560 $config = CRM_Core_Config::singleton();
561 $showBestResult = FALSE;
562 if (in_array($ufField->field_name, array(
563 'country',
564 'state_province',
565 )) && count($config->countryLimit) > 1
566 ) {
567 // get state or country field weight if exists
568 $field = 'state_province';
569 if ($ufField->field_name == 'state_province') {
570 $field = 'country';
571 }
572 $ufFieldDAO = new CRM_Core_DAO_UFField();
573 $ufFieldDAO->field_name = $field;
574 $ufFieldDAO->location_type_id = $ufField->location_type_id;
575 $ufFieldDAO->uf_group_id = $ufField->uf_group_id;
576
577 if ($ufFieldDAO->find(TRUE)) {
578 if ($field == 'country' && $ufFieldDAO->weight > $ufField->weight) {
579 $showBestResult = TRUE;
580 }
581 elseif ($field == 'state_province' && $ufFieldDAO->weight < $ufField->weight) {
582 $showBestResult = TRUE;
583 }
584 }
585 }
586
587 //update group_type every time. CRM-3608
588 if ($this->_gid && is_a($ufField, 'CRM_Core_DAO_UFField')) {
589 // get the profile type.
590 $fieldsType = CRM_Core_BAO_UFGroup::calculateGroupType($this->_gid, TRUE);
591 CRM_Core_BAO_UFGroup::updateGroupTypes($this->_gid, $fieldsType);
592 }
593 CRM_Core_Session::setStatus(ts('Your CiviCRM Profile Field \'%1\' has been saved to \'%2\'.',
594 array(1 => $name, 2 => $this->_title)
595 ), ts('Profile Field Saved'), 'success');
596 }
597 $buttonName = $this->controller->getButtonName();
598
599 $session = CRM_Core_Session::singleton();
600 if ($buttonName == $this->getButtonName('next', 'new')) {
601 $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/uf/group/field/add',
602 "reset=1&action=add&gid={$this->_gid}&sbr={$showBestResult}"
603 ));
604 }
605 else {
606 $session->replaceUserContext(CRM_Utils_System::url('civicrm/admin/uf/group/field',
607 "reset=1&action=browse&gid={$this->_gid}"
608 ));
609 $session->set('showBestResult', $showBestResult);
610 }
611 }
612
613 /**
614 * Validation rule for subtype.
615 *
616 * @param string $fieldType
617 * Type of field.
618 * @param array $groupType
619 * Contains all groupTypes.
620 * @param array $errors
621 * List of errors to be posted back to the form.
622 */
623 public static function formRuleSubType($fieldType, $groupType, &$errors) {
624 if (in_array($fieldType, array(
625 'Participant',
626 'Contribution',
627 'Membership',
628 'Activity',
629 ))) {
630 $individualSubTypes = CRM_Contact_BAO_ContactType::subTypes('Individual');
631 foreach ($groupType as $value) {
632 if (!in_array($value, $individualSubTypes) &&
633 !in_array($value, array(
634 'Participant',
635 'Contribution',
636 'Membership',
637 'Individual',
638 'Contact',
639 'Activity',
640 'Formatting',
641 ))
642 ) {
643 $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));
644 break;
645 }
646 }
647 }
648 else {
649 $basicType = CRM_Contact_BAO_ContactType::getBasicType($groupType);
650 if ($basicType) {
651 if (!is_array($basicType)) {
652 $basicType = array($basicType);
653 }
654 if (!in_array($fieldType, $basicType) && $fieldType != 'Contact') {
655 $errors['field_name'] = ts('Cannot add or update profile field type "%1" with combination of subtype other than "%1".',
656 array(1 => $fieldType)
657 );
658 }
659 }
660 }
661 }
662
663 /**
664 * Validation rule for custom data extends entity column values.
665 *
666 * @param Object $customField
667 * Custom field.
668 * @param int $gid
669 * Group Id.
670 * @param string $fieldType
671 * Group type of the field.
672 * @param array $errors
673 * Collect errors.
674 *
675 * @return array
676 * list of errors to be posted back to the form
677 */
678 public static function formRuleCustomDataExtentColumnValue($customField, $gid, $fieldType, &$errors) {
679 // fix me : check object $customField
680 if (in_array($fieldType, array(
681 'Participant',
682 'Contribution',
683 'Membership',
684 'Activity',
685 'Case',
686 ))) {
687 $params = array('id' => $customField->custom_group_id);
688 $customGroup = array();
689 CRM_Core_BAO_CustomGroup::retrieve($params, $customGroup);
690 if (($fieldType != CRM_Utils_Array::value('extends', $customGroup)) || empty($customGroup['extends_entity_column_value'])) {
691 return $errors;
692 }
693
694 $extendsColumnValues = array();
695 foreach (explode(CRM_Core_DAO::VALUE_SEPARATOR, $customGroup['extends_entity_column_value']) as $val) {
696 if ($val) {
697 $extendsColumnValues[] = $val;
698 }
699 }
700
701 if (empty($extendsColumnValues)) {
702 return $errors;
703 }
704
705 $fieldTypeValues = CRM_Core_BAO_UFGroup::groupTypeValues($gid, $fieldType);
706 if (empty($fieldTypeValues[$fieldType])) {
707 return;
708 }
709
710 $disallowedTypes = array_diff($extendsColumnValues, $fieldTypeValues[$fieldType]);
711 if (!empty($disallowedTypes)) {
712 $errors['field_name'] = ts('Profile is already having custom fields extending different group types, you can not add or update this custom field.');
713 }
714 }
715 }
716
717 /**
718 * Validation rule to prevent multiple fields of primary location type within the same communication type.
719 *
720 * @param array $fields
721 * Submitted fields.
722 * @param string $profileFieldName
723 * Group Id.
724 * @param array $groupFields
725 * List of fields already in the group.
726 * @param array $errors
727 * Collect errors.
728 *
729 */
730 public static function formRulePrimaryCheck($fields, $profileFieldName, $groupFields, &$errors) {
731 //FIXME: This may need to also apply to website fields if they are refactored to allow more than one per profile
732 $checkPrimary = array('phone' => 'civicrm_phone.phone', 'phone_and_ext' => 'civicrm_phone.phone');
733 $whereCheck = NULL;
734 $primaryOfSameTypeFound = NULL;
735 $fieldID = empty($fields['field_id']) ? 0 : $fields['field_id'];
736 // Is this a primary location type field of interest
737 if (array_key_exists($profileFieldName, $checkPrimary)) {
738 $whereCheck = $checkPrimary[$profileFieldName];
739 }
740 $potentialLocationType = CRM_Utils_Array::value(2, $fields['field_name']);
741
742 if ($whereCheck && $potentialLocationType == 0) {
743 $primaryOfSameTypeFound = '';
744
745 foreach ($groupFields as $groupField) {
746 // if it is a phone
747 if ($groupField['where'] == $whereCheck && is_null($groupField['location_type_id']) && $groupField['field_id'] != $fieldID) {
748 $primaryOfSameTypeFound = $groupField['title'];
749 break;
750 }
751 }
752 if ($primaryOfSameTypeFound) {
753 $errors['field_name'] = ts('You have already added a primary location field of this type: %1', array(1 => $primaryOfSameTypeFound));
754 }
755 }
756 }
757
758 /**
759 * Global validation rules for the form.
760 *
761 * @param array $fields
762 * Posted values of the form.
763 *
764 * @param $files
765 * @param $self
766 *
767 * @return array
768 * list of errors to be posted back to the form
769 */
770 public static function formRule($fields, $files, $self) {
771 $is_required = CRM_Utils_Array::value('is_required', $fields, FALSE);
772 $is_registration = CRM_Utils_Array::value('is_registration', $fields, FALSE);
773 $is_view = CRM_Utils_Array::value('is_view', $fields, FALSE);
774 $in_selector = CRM_Utils_Array::value('in_selector', $fields, FALSE);
775 $is_active = CRM_Utils_Array::value('is_active', $fields, FALSE);
776
777 $errors = array();
778 if ($is_view && $is_registration) {
779 $errors['is_registration'] = ts('View Only cannot be selected if this field is to be included on the registration form');
780 }
781 if ($is_view && $is_required) {
782 $errors['is_view'] = ts('A View Only field cannot be required');
783 }
784
785 $entityName = $fields['field_name'][0];
786 if (!$entityName) {
787 $errors['field_name'] = ts('Please select a field name');
788 }
789
790 if ($in_selector && in_array($entityName, array(
791 'Contribution',
792 'Participant',
793 'Membership',
794 'Activity',
795 ))
796 ) {
797 $errors['in_selector'] = ts("'In Selector' cannot be checked for %1 fields.", array(1 => $entityName));
798 }
799
800 $isCustomField = FALSE;
801 $profileFieldName = CRM_Utils_Array::value(1, $fields['field_name']);
802 if ($profileFieldName) {
803 //get custom field id
804 $customFieldId = explode('_', $profileFieldName);
805 if ($customFieldId[0] == 'custom') {
806 $customField = new CRM_Core_DAO_CustomField();
807 $customField->id = $customFieldId[1];
808 $customField->find(TRUE);
809 $isCustomField = TRUE;
810 if (!empty($fields['field_id']) && !$customField->is_active && $is_active) {
811 $errors['field_name'] = ts('Cannot set this field "Active" since the selected custom field is disabled.');
812 }
813
814 //check if profile already has a different multi-record custom set field configured
815 $customGroupId = CRM_Core_BAO_CustomField::isMultiRecordField($profileFieldName);
816 if ($customGroupId) {
817 if ($profileMultiRecordCustomGid = CRM_Core_BAO_UFField::checkMultiRecordFieldExists($self->_gid)) {
818 if ($customGroupId != $profileMultiRecordCustomGid) {
819 $errors['field_name'] = ts("You cannot configure multi-record custom fields belonging to different custom sets in one profile");
820 }
821 }
822 }
823 }
824 }
825
826 // Get list of fields already in the group
827 $groupFields = CRM_Core_BAO_UFGroup::getFields($fields['group_id'], FALSE, NULL, NULL, NULL, TRUE, NULL, TRUE);
828 // Check if we already added a primary field of the same communication type
829 self::formRulePrimaryCheck($fields, $profileFieldName, $groupFields, $errors);
830
831 //check profile is configured for double option process
832 //adding group field, email field should be present in the group
833 //fixed for issue CRM-2861 & CRM-4153
834 if (CRM_Core_BAO_UFGroup::isProfileDoubleOptin()) {
835 if (CRM_Utils_Array::value(1, $fields['field_name']) == 'group') {
836 $dao = new CRM_Core_BAO_UFField();
837 $dao->uf_group_id = $fields['group_id'];
838 $dao->find();
839 $emailField = FALSE;
840 while ($dao->fetch()) {
841 //check email field is present in the group
842 if ($dao->field_name == 'email') {
843 $emailField = TRUE;
844 break;
845 }
846 }
847
848 if (!$emailField) {
849 $disableSettingURL = CRM_Utils_System::url(
850 'civicrm/admin/setting/preferences/mailing',
851 'reset=1'
852 );
853
854 $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));
855 }
856 }
857 }
858
859 //fix for CRM-3037
860 $fieldType = $fields['field_name'][0];
861
862 //get the group type.
863 $groupType = CRM_Core_BAO_UFGroup::calculateGroupType($self->_gid, FALSE, CRM_Utils_Array::value('field_id', $fields));
864
865 switch ($fieldType) {
866 case 'Contact':
867 self::formRuleSubType($fieldType, $groupType, $errors);
868 break;
869
870 case 'Individual':
871 if (in_array('Activity', $groupType) ||
872 in_array('Household', $groupType) ||
873 in_array('Organization', $groupType)
874 ) {
875
876 //CRM-7603 - need to support activity + individual.
877 //$errors['field_name'] =
878 //ts( 'Cannot add or update profile field type Individual with combination of Household or Organization or Activity' );
879 if (in_array('Household', $groupType) ||
880 in_array('Organization', $groupType)
881 ) {
882 $errors['field_name'] = ts('Cannot add or update profile field type Individual with combination of Household or Organization');
883 }
884 }
885 else {
886 self::formRuleSubType($fieldType, $groupType, $errors);
887 }
888 break;
889
890 case 'Household':
891 if (in_array('Activity', $groupType) || in_array('Individual', $groupType) || in_array('Organization', $groupType)) {
892 $errors['field_name'] = ts('Cannot add or update profile field type Household with combination of Individual or Organization or Activity');
893 }
894 else {
895 self::formRuleSubType($fieldType, $groupType, $errors);
896 }
897 break;
898
899 case 'Organization':
900 if (in_array('Activity', $groupType) || in_array('Household', $groupType) || in_array('Individual', $groupType)) {
901 $errors['field_name'] = ts('Cannot add or update profile field type Organization with combination of Household or Individual or Activity');
902 }
903 else {
904 self::formRuleSubType($fieldType, $groupType, $errors);
905 }
906 break;
907
908 case 'Activity':
909 if (in_array('Individual', $groupType) ||
910 in_array('Membership', $groupType) ||
911 in_array('Contribution', $groupType) ||
912 in_array('Organization', $groupType) ||
913 in_array('Household', $groupType) ||
914 in_array('Participant', $groupType)
915 ) {
916
917 //CRM-7603 - need to support activity + contact type.
918 //$errors['field_name'] =
919 //ts( 'Cannot add or update profile field type Activity with combination Participant or Membership or Contribution or Household or Organization or Individual' );
920 if (in_array('Membership', $groupType) ||
921 in_array('Contribution', $groupType) ||
922 in_array('Participant', $groupType)
923 ) {
924 $errors['field_name'] = ts('Cannot add or update profile field type Activity with combination Participant or Membership or Contribution');
925 }
926 }
927 else {
928 self::formRuleSubType($fieldType, $groupType, $errors);
929 }
930
931 if ($isCustomField && !isset($errors['field_name'])) {
932 self::formRuleCustomDataExtentColumnValue($customField, $self->_gid, $fieldType, $errors);
933 }
934 break;
935
936 case 'Participant':
937 if (in_array('Membership', $groupType) || in_array('Contribution', $groupType)
938 || in_array('Organization', $groupType) || in_array('Household', $groupType) || in_array('Activity', $groupType)
939 ) {
940 $errors['field_name'] = ts('Cannot add or update profile field type Participant with combination of Activity or Membership or Contribution or Household or Organization.');
941 }
942 else {
943 self::formRuleSubType($fieldType, $groupType, $errors);
944 }
945 break;
946
947 case 'Contribution':
948 //special case where in we allow contribution + oganization fields, for on behalf feature
949 $profileId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup',
950 'on_behalf_organization', 'id', 'name'
951 );
952
953 if (in_array('Participant', $groupType) || in_array('Membership', $groupType)
954 || ($profileId != $self->_gid && in_array('Organization', $groupType)) || in_array('Household', $groupType) || in_array('Activity', $groupType)
955 ) {
956 $errors['field_name'] = ts('Cannot add or update profile field type Contribution with combination of Activity or Membership or Participant or Household or Organization');
957 }
958 else {
959 self::formRuleSubType($fieldType, $groupType, $errors);
960 }
961 break;
962
963 case 'Membership':
964 //special case where in we allow contribution + oganization fields, for on behalf feature
965 $profileId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup',
966 'on_behalf_organization', 'id', 'name'
967 );
968
969 if (in_array('Participant', $groupType) || in_array('Contribution', $groupType)
970 || ($profileId != $self->_gid && in_array('Organization', $groupType)) || in_array('Household', $groupType) || in_array('Activity', $groupType)
971 ) {
972 $errors['field_name'] = ts('Cannot add or update profile field type Membership with combination of Activity or Participant or Contribution or Household or Organization');
973 }
974 else {
975 self::formRuleSubType($fieldType, $groupType, $errors);
976 }
977 break;
978
979 default:
980 $profileType = CRM_Core_BAO_UFField::getProfileType($fields['group_id'], TRUE, FALSE, TRUE);
981 if (CRM_Contact_BAO_ContactType::isaSubType($fieldType)) {
982 if (CRM_Contact_BAO_ContactType::isaSubType($profileType)) {
983 if ($fieldType != $profileType) {
984 $errors['field_name'] = ts('Cannot add or update profile field type "%1" with combination of "%2".', array(
985 1 => $fieldType,
986 2 => $profileType,
987 ));
988 }
989 }
990 else {
991 $basicType = CRM_Contact_BAO_ContactType::getBasicType($fieldType);
992 if ($profileType &&
993 $profileType != $basicType &&
994 $profileType != 'Contact'
995 ) {
996 $errors['field_name'] = ts('Cannot add or update profile field type "%1" with combination of "%2".', array(
997 1 => $fieldType,
998 2 => $profileType,
999 ));
1000 }
1001 }
1002 }
1003 elseif (
1004 CRM_Utils_Array::value(1, $fields['field_name']) == 'contact_sub_type' &&
1005 !in_array($profileType, array('Individual', 'Household', 'Organization')) &&
1006 !in_array($profileType, CRM_Contact_BAO_ContactType::subTypes())
1007 ) {
1008 $errors['field_name'] = ts('Cannot add or update profile field Contact Subtype as profile type is not one of Individual, Household or Organization.');
1009 }
1010 }
1011 return empty($errors) ? TRUE : $errors;
1012 }
1013
1014 }