3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * This class contains function for UFField.
21 class CRM_Core_BAO_UFField
extends CRM_Core_DAO_UFField
{
27 private static $_contriBatchEntryFields = NULL;
28 private static $_memberBatchEntryFields = NULL;
31 * Create UFField object.
33 * @param array $params
34 * Array per getfields metadata.
36 * @return \CRM_Core_BAO_UFField
37 * @throws \API_Exception
39 public static function create($params) {
40 $id = $params['id'] ??
NULL;
42 $op = empty($id) ?
'create' : 'edit';
43 CRM_Utils_Hook
::pre('UFField', $op, $id, $params);
44 // Merge in data from existing field
46 $UFField = new CRM_Core_BAO_UFField();
47 $UFField->id
= $params['id'];
48 if ($UFField->find(TRUE)) {
49 $defaults = $UFField->toArray();
50 // This will be calculated based on field name
51 unset($defaults['field_type']);
55 throw new API_Exception("UFFIeld id {$params['id']} not found.");
59 // Validate field_name
60 if (strpos($params['field_name'], 'formatting') !== 0 && !CRM_Core_BAO_UFField
::isValidFieldName($params['field_name'])) {
61 throw new API_Exception('The field_name is not valid');
64 // Supply default label if not set
65 if (empty($id) && !isset($params['label'])) {
66 $params['label'] = self
::getAvailableFieldTitles()[$params['field_name']];
69 // Supply field_type if not set
70 if (empty($params['field_type']) && strpos($params['field_name'], 'formatting') !== 0) {
71 $params['field_type'] = CRM_Utils_Array
::pathGet(self
::getAvailableFieldsFlat(), [$params['field_name'], 'field_type']);
73 elseif (empty($params['field_type'])) {
74 $params['field_type'] = 'Formatting';
77 // Generate unique name for formatting fields
78 if ($params['field_name'] === 'formatting') {
79 $params['field_name'] = 'formatting_' . substr(uniqid(), -4);
82 if (self
::duplicateField($params)) {
83 throw new API_Exception("The field was not added. It already exists in this profile.");
86 //@todo why is this even optional? Surely weight should just be 'managed' ??
87 if (CRM_Utils_Array
::value('option.autoweight', $params, TRUE)) {
88 $params['weight'] = CRM_Core_BAO_UFField
::autoWeight($params);
91 // Set values for uf field properties and save
92 $ufField = new CRM_Core_DAO_UFField();
93 $ufField->copyValues($params);
95 if ($params['field_name'] == 'url') {
96 $ufField->location_type_id
= 'null';
99 $ufField->website_type_id
= 'null';
101 if (!strstr($params['field_name'], 'phone')) {
102 $ufField->phone_type_id
= 'null';
107 $fieldsType = CRM_Core_BAO_UFGroup
::calculateGroupType($ufField->uf_group_id
, TRUE);
108 CRM_Core_BAO_UFGroup
::updateGroupTypes($ufField->uf_group_id
, $fieldsType);
110 CRM_Utils_Hook
::post('UFField', $op, $ufField->id
, $ufField);
112 civicrm_api3('profile', 'getfields', ['cache_clear' => TRUE]);
117 * Fetch object based on array of properties.
119 * @param array $params
120 * (reference ) an assoc array of name/value pairs.
121 * @param array $defaults
122 * (reference ) an assoc array to hold the flattened values.
124 * @return CRM_Core_BAO_UFField
126 public static function retrieve(&$params, &$defaults) {
127 return CRM_Core_DAO
::commonRetrieve('CRM_Core_DAO_UFField', $params, $defaults);
131 * Update the is_active flag in the db.
134 * Id of the database record.
135 * @param bool $is_active
136 * Value we want to set the is_active field.
139 * true if we found and updated the object, else false
141 public static function setIsActive($id, $is_active) {
142 //check if custom data profile field is disabled
144 if (CRM_Core_BAO_UFField
::checkUFStatus($id)) {
145 return CRM_Core_DAO
::setFieldValue('CRM_Core_DAO_UFField', $id, 'is_active', $is_active);
148 CRM_Core_Session
::setStatus(ts('Cannot enable this UF field since the used custom field is disabled.'), ts('Check Custom Field'), 'error');
152 return CRM_Core_DAO
::setFieldValue('CRM_Core_DAO_UFField', $id, 'is_active', $is_active);
157 * Delete the profile Field.
165 public static function del($id) {
167 $field = new CRM_Core_DAO_UFField();
174 * Check duplicate for duplicate field in a group.
176 * @param array $params
177 * An associative array with field and values.
181 public static function duplicateField($params) {
182 $ufField = new CRM_Core_DAO_UFField();
183 $ufField->uf_group_id
= $params['uf_group_id'] ??
NULL;
184 $ufField->field_type
= $params['field_type'] ??
NULL;
185 $ufField->field_name
= $params['field_name'] ??
NULL;
186 $ufField->website_type_id
= $params['website_type_id'] ??
NULL;
187 if (is_null(CRM_Utils_Array
::value('location_type_id', $params, ''))) {
188 // primary location type have NULL value in DB
189 $ufField->whereAdd("location_type_id IS NULL");
192 $ufField->location_type_id
= $params['location_type_id'] ??
NULL;
194 $ufField->phone_type_id
= $params['phone_type_id'] ??
NULL;
196 if (!empty($params['id'])) {
197 $ufField->whereAdd("id <> " . $params['id']);
200 return (bool) $ufField->find(TRUE);
204 * Does profile consists of a multi-record custom field.
210 public static function checkMultiRecordFieldExists($gId) {
211 $queryString = "SELECT f.field_name
212 FROM civicrm_uf_field f, civicrm_uf_group g
213 WHERE f.uf_group_id = g.id
214 AND g.id = %1 AND f.field_name LIKE 'custom%'";
215 $p = [1 => [$gId, 'Integer']];
216 $dao = CRM_Core_DAO
::executeQuery($queryString, $p);
217 $customFieldIds = [];
218 $isMultiRecordFieldPresent = FALSE;
219 while ($dao->fetch()) {
220 if ($customId = CRM_Core_BAO_CustomField
::getKeyID($dao->field_name
)) {
221 if (is_numeric($customId)) {
222 $customFieldIds[] = $customId;
227 if (!empty($customFieldIds) && count($customFieldIds) == 1) {
228 $customFieldId = array_pop($customFieldIds);
229 $isMultiRecordFieldPresent = CRM_Core_BAO_CustomField
::isMultiRecordField($customFieldId);
231 elseif (count($customFieldIds) > 1) {
232 $customFieldIds = implode(", ", $customFieldIds);
235 FROM civicrm_custom_group cg
236 INNER JOIN civicrm_custom_field cf
237 ON cg.id = cf.custom_group_id
238 WHERE cf.id IN (" . $customFieldIds . ") AND is_multiple = 1 LIMIT 0,1";
240 $dao = CRM_Core_DAO
::executeQuery($queryString);
242 $isMultiRecordFieldPresent = ($dao->cgId
) ?
$dao->cgId
: FALSE;
246 return $isMultiRecordFieldPresent;
250 * Automatically determine one weight and modify others.
252 * @param array $params
253 * UFField record, e.g. with 'weight', 'uf_group_id', and 'field_id'.
256 public static function autoWeight($params) {
260 if (!empty($params['field_id']) ||
!empty($params['id'])) {
261 $oldWeight = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFField', !empty($params['id']) ?
$params['id'] : $params['field_id'], 'weight', 'id');
263 $fieldValues = ['uf_group_id' => !empty($params['uf_group_id']) ?
$params['uf_group_id'] : $params['group_id']];
264 return CRM_Utils_Weight
::updateOtherWeights('CRM_Core_DAO_UFField', $oldWeight, CRM_Utils_Array
::value('weight', $params, 0), $fieldValues);
268 * Enable/disable profile field given a custom field id
270 * @param int $customFieldId
272 * @param bool $is_active
273 * Set the is_active field.
275 public static function setUFField($customFieldId, $is_active) {
276 // Find the profile id given custom field.
277 $ufField = new CRM_Core_DAO_UFField();
278 $ufField->field_name
= "custom_" . $customFieldId;
281 while ($ufField->fetch()) {
282 // Enable/ disable profile.
283 CRM_Core_BAO_UFField
::setIsActive($ufField->id
, $is_active);
288 * Copy existing profile fields to
289 * new profile from the already built profile
292 * From which we need to copy.
293 * @param bool $new_id
296 public static function copy($old_id, $new_id) {
297 $ufField = new CRM_Core_DAO_UFField();
298 $ufField->uf_group_id
= $old_id;
300 while ($ufField->fetch()) {
301 //copy the field records as it is on new ufgroup id
302 $ufField->uf_group_id
= $new_id;
309 * Delete profile field given a custom field.
311 * @param int $customFieldId
312 * ID of the custom field to be deleted.
314 public static function delUFField($customFieldId) {
315 //find the profile id given custom field id
316 $ufField = new CRM_Core_DAO_UFField();
317 $ufField->field_name
= "custom_" . $customFieldId;
320 while ($ufField->fetch()) {
321 //enable/ disable profile
322 CRM_Core_BAO_UFField
::del($ufField->id
);
327 * Enable/disable profile field given a custom group id
329 * @param int $customGroupId
331 * @param bool $is_active
332 * Value we want to set the is_active field.
334 public static function setUFFieldStatus($customGroupId, $is_active) {
335 //find the profile id given custom group id
336 $queryString = "SELECT civicrm_custom_field.id as custom_field_id
337 FROM civicrm_custom_field, civicrm_custom_group
338 WHERE civicrm_custom_field.custom_group_id = civicrm_custom_group.id
339 AND civicrm_custom_group.id = %1";
340 $p = [1 => [$customGroupId, 'Integer']];
341 $dao = CRM_Core_DAO
::executeQuery($queryString, $p);
343 while ($dao->fetch()) {
344 // Enable/ disable profile.
345 CRM_Core_BAO_UFField
::setUFField($dao->custom_field_id
, $is_active);
350 * Check the status of custom field used in uf fields.
352 * @param int $UFFieldId
355 * false if custom field are disabled else true
357 public static function checkUFStatus($UFFieldId) {
358 $fieldName = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFField', $UFFieldId, 'field_name');
359 // return if field is not a custom field
360 if (!$customFieldId = CRM_Core_BAO_CustomField
::getKeyID($fieldName)) {
364 $customField = new CRM_Core_DAO_CustomField();
365 $customField->id
= $customFieldId;
366 // if uf field is custom field
367 if ($customField->find(TRUE)) {
368 if (!$customField->is_active
) {
378 * Find out whether given profile group using Activity
379 * Profile fields with contact fields
381 * @param int $ufGroupId
385 public static function checkContactActivityProfileType($ufGroupId) {
386 $ufGroup = new CRM_Core_DAO_UFGroup();
387 $ufGroup->id
= $ufGroupId;
388 $ufGroup->find(TRUE);
390 return self
::checkContactActivityProfileTypeByGroupType($ufGroup->group_type
);
395 * @param $ufGroupType
398 public static function checkContactActivityProfileTypeByGroupType($ufGroupType) {
401 $typeParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $ufGroupType);
402 $profileTypes = explode(',', $typeParts[0]);
405 if (empty($profileTypes)) {
408 $components = ['Contribution', 'Participant', 'Membership'];
409 if (!in_array('Activity', $profileTypes)) {
412 elseif (count($profileTypes) == 1) {
416 if ($index = array_search('Contact', $profileTypes)) {
417 unset($profileTypes[$index]);
418 if (count($profileTypes) == 1) {
423 $contactTypes = ['Individual', 'Household', 'Organization'];
424 $subTypes = CRM_Contact_BAO_ContactType
::subTypes();
426 $profileTypeComponent = array_intersect($components, $profileTypes);
427 if (!empty($profileTypeComponent) ||
428 count(array_intersect($contactTypes, $profileTypes)) > 1 ||
429 count(array_intersect($subTypes, $profileTypes)) > 1
438 * Find out whether given profile group uses $required
439 * and/or $optional profile types
441 * @param int $ufGroupId
443 * @param array $required
444 * Array of types those are required.
445 * @param array $optional
446 * Array of types those are optional.
450 public static function checkValidProfileType($ufGroupId, $required, $optional = NULL) {
451 if (!is_array($required) ||
empty($required)) {
455 $ufGroup = new CRM_Core_DAO_UFGroup();
456 $ufGroup->id
= $ufGroupId;
457 $ufGroup->find(TRUE);
460 if ($ufGroup->group_type
) {
461 $typeParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $ufGroup->group_type
);
462 $profileTypes = explode(',', $typeParts[0]);
465 if (empty($profileTypes)) {
470 foreach ($required as $key => $val) {
471 if (!in_array($val, $profileTypes)) {
477 if ($valid && is_array($optional)) {
478 foreach ($optional as $key => $val) {
479 if (in_array($val, $profileTypes)) {
490 * Check for mix profile fields (eg: individual + other contact types)
492 * @param int $ufGroupId
495 * true for mix profile else false
497 public static function checkProfileType($ufGroupId) {
498 $ufGroup = new CRM_Core_DAO_UFGroup();
499 $ufGroup->id
= $ufGroupId;
500 $ufGroup->find(TRUE);
503 if ($ufGroup->group_type
) {
504 $typeParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $ufGroup->group_type
);
505 $profileTypes = explode(',', $typeParts[0]);
508 //early return if new profile.
509 if (empty($profileTypes)) {
513 //we need to unset Contact
514 if (count($profileTypes) > 1) {
515 $index = array_search('Contact', $profileTypes);
516 if ($index !== FALSE) {
517 unset($profileTypes[$index]);
521 // suppress any subtypes if present
522 CRM_Contact_BAO_ContactType
::suppressSubTypes($profileTypes);
524 $contactTypes = ['Contact', 'Individual', 'Household', 'Organization'];
525 $components = ['Contribution', 'Participant', 'Membership', 'Activity'];
528 // check for mix profile condition
529 if (count($profileTypes) > 1) {
530 //check the there are any components include in profile
531 foreach ($components as $value) {
532 if (in_array($value, $profileTypes)) {
536 //check if there are more than one contact types included in profile
537 if (count($profileTypes) > 1) {
541 elseif (count($profileTypes) == 1) {
542 // note for subtype case count would be zero
543 $profileTypes = array_values($profileTypes);
544 if (!in_array($profileTypes[0], $contactTypes)) {
553 * Get the profile type (eg: individual/organization/household)
555 * @param int $ufGroupId
557 * @param bool $returnMixType
558 * This is true, then field type of mix profile field is returned.
559 * @param bool $onlyPure
560 * True if only pure profiles are required.
562 * @param bool $skipComponentType
568 public static function getProfileType($ufGroupId, $returnMixType = TRUE, $onlyPure = FALSE, $skipComponentType = FALSE) {
569 $ufGroup = new CRM_Core_DAO_UFGroup();
570 $ufGroup->id
= $ufGroupId;
571 $ufGroup->is_active
= 1;
573 $ufGroup->find(TRUE);
574 return self
::calculateProfileType($ufGroup->group_type
, $returnMixType, $onlyPure, $skipComponentType);
578 * Get the profile type (eg: individual/organization/household)
580 * @param string $ufGroupType
581 * @param bool $returnMixType
582 * This is true, then field type of mix profile field is returned.
583 * @param bool $onlyPure
584 * True if only pure profiles are required.
585 * @param bool $skipComponentType
587 * @return string profile group_type
590 public static function calculateProfileType($ufGroupType, $returnMixType = TRUE, $onlyPure = FALSE, $skipComponentType = FALSE) {
592 $contactTypes = ['Contact', 'Individual', 'Household', 'Organization'];
593 $subTypes = CRM_Contact_BAO_ContactType
::subTypes();
594 $components = ['Contribution', 'Participant', 'Membership', 'Activity'];
598 $typeParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $ufGroupType);
599 $profileTypes = explode(',', $typeParts[0]);
603 if (count($profileTypes) == 1) {
604 return $profileTypes[0];
611 //we need to unset Contact
612 if (count($profileTypes) > 1) {
613 $index = array_search('Contact', $profileTypes);
614 if ($index !== FALSE) {
615 unset($profileTypes[$index]);
619 $profileType = $mixProfileType = NULL;
621 // this case handles pure profile
622 if (count($profileTypes) == 1) {
623 $profileType = array_pop($profileTypes);
626 //check the there are any components include in profile
627 $componentCount = [];
628 foreach ($components as $value) {
629 if (in_array($value, $profileTypes)) {
630 $componentCount[] = $value;
634 //check contact type included in profile
635 $contactTypeCount = [];
636 foreach ($contactTypes as $value) {
637 if (in_array($value, $profileTypes)) {
638 $contactTypeCount[] = $value;
643 foreach ($subTypes as $value) {
644 if (in_array($value, $profileTypes)) {
645 $subTypeCount[] = $value;
648 if (!$skipComponentType && count($componentCount) == 1) {
649 $profileType = $componentCount[0];
651 elseif (count($componentCount) > 1) {
652 $mixProfileType = $componentCount[1];
654 elseif (count($subTypeCount) == 1) {
655 $profileType = $subTypeCount[0];
657 elseif (count($contactTypeCount) == 1) {
658 $profileType = $contactTypeCount[0];
660 elseif (count($subTypeCount) > 1) {
661 // this is mix subtype profiles
662 $mixProfileType = $subTypeCount[1];
664 elseif (count($contactTypeCount) > 1) {
665 // this is mix contact profiles
666 $mixProfileType = $contactTypeCount[1];
670 if ($mixProfileType) {
671 if ($returnMixType) {
672 return $mixProfileType;
684 * Check for mix profiles groups (eg: individual + other contact types)
689 * true for mix profile group else false
691 public static function checkProfileGroupType($ctype) {
692 $ufGroup = new CRM_Core_DAO_UFGroup();
696 FROM civicrm_uf_group as ufg, civicrm_uf_join as ufj
697 WHERE ufg.id = ufj.uf_group_id
698 AND ufj.module = 'User Registration'
699 AND ufg.is_active = 1 ";
701 $ufGroup = CRM_Core_DAO
::executeQuery($query);
704 $validProfiles = ['Individual', 'Organization', 'Household', 'Contribution'];
705 while ($ufGroup->fetch()) {
706 $profileType = self
::getProfileType($ufGroup->id
);
707 if (in_array($profileType, $validProfiles)) {
710 elseif ($profileType) {
719 * Check for searchable or in selector field for given profile.
721 * @param int $profileID
725 public static function checkSearchableORInSelector($profileID) {
733 From civicrm_uf_field
734 WHERE (in_selector = 1 OR is_searchable = 1)
735 AND uf_group_id = {$profileID}";
737 $ufFields = CRM_Core_DAO
::executeQuery($query);
738 while ($ufFields->fetch()) {
747 * Reset In selector and is searchable values for given $profileID.
749 * @param int $profileID
751 public function resetInSelectorANDSearchable($profileID) {
755 $query = "UPDATE civicrm_uf_field SET in_selector = 0, is_searchable = 0 WHERE uf_group_id = {$profileID}";
756 CRM_Core_DAO
::executeQuery($query);
760 * Add fields to $profileAddressFields as appropriate.
761 * profileAddressFields is assigned to the template to tell it
762 * what fields are in the profile address
763 * that potentially should be copied to the Billing fields
764 * we want to give precedence to
766 * 2) then Primary designated as 'Primary
767 * 3) location_type is primary
768 * 4) if none of these apply then it just uses the first one
770 * as this will be used to
771 * transfer profile address data to billing fields
772 * http://issues.civicrm.org/jira/browse/CRM-5869
775 * Field key - e.g. street_address-Primary, first_name.
776 * @param array $profileAddressFields
777 * Array of profile fields that relate to address fields.
778 * @param array $profileFilter
779 * Filter to apply to profile fields - expected usage is to only fill based on.
780 * the bottom profile per CRM-13726
783 * Can the address block be hidden safe in the knowledge all fields are elsewhere collected (see CRM-15118)
785 public static function assignAddressField($key, &$profileAddressFields, $profileFilter) {
786 $billing_id = CRM_Core_BAO_LocationType
::getBilling();
787 list($prefixName, $index) = CRM_Utils_System
::explode('-', $key, 2);
789 $profileFields = civicrm_api3('uf_field', 'get', array_merge($profileFilter,
792 'return' => 'field_name, is_required',
798 //check for valid fields ( fields that are present in billing block )
799 $validBillingFields = [
804 'supplemental_address_1',
810 $requiredBillingFields = array_diff($validBillingFields, ['middle_name', 'supplemental_address_1']);
811 $validProfileFields = [];
812 $requiredProfileFields = [];
814 foreach ($profileFields['values'] as $field) {
815 if (in_array($field['field_name'], $validBillingFields)) {
816 $validProfileFields[] = $field['field_name'];
818 if (!empty($field['is_required'])) {
819 $requiredProfileFields[] = $field['field_name'];
823 if (!in_array($prefixName, $validProfileFields)) {
827 if (!empty($index) && (
828 // it's empty so we set it OR
829 !CRM_Utils_Array
::value($prefixName, $profileAddressFields)
830 //we are dealing with billing id (precedence)
831 ||
$index == $billing_id
832 // we are dealing with primary & billing not set
833 ||
($index == 'Primary' && $profileAddressFields[$prefixName] != $billing_id)
834 ||
($index == CRM_Core_BAO_LocationType
::getDefault()->id
835 && $profileAddressFields[$prefixName] != $billing_id
836 && $profileAddressFields[$prefixName] != 'Primary'
840 $profileAddressFields[$prefixName] = $index;
843 $potentiallyMissingRequiredFields = array_diff($requiredBillingFields, $requiredProfileFields);
844 CRM_Core_Resources
::singleton()
845 ->addSetting(['billing' => ['billingProfileIsHideable' => empty($potentiallyMissingRequiredFields)]]);
849 * Get a list of fields which can be added to profiles.
851 * @param int $gid : UF group ID
852 * @param array $defaults : Form defaults
853 * @return array, multidimensional; e.g. $result['FieldGroup']['field_name']['label']
855 public static function getAvailableFields($gid = NULL, $defaults = []) {
858 'Individual' => CRM_Contact_BAO_Contact
::importableFields('Individual', FALSE, FALSE, TRUE, TRUE, TRUE),
859 'Household' => CRM_Contact_BAO_Contact
::importableFields('Household', FALSE, FALSE, TRUE, TRUE, TRUE),
860 'Organization' => CRM_Contact_BAO_Contact
::importableFields('Organization', FALSE, FALSE, TRUE, TRUE, TRUE),
863 // include hook injected fields
864 $fields['Contact'] = array_merge($fields['Contact'], CRM_Contact_BAO_Query_Hook
::singleton()->getFields());
866 // add current employer for individuals
867 $fields['Individual']['current_employer'] = [
868 'name' => 'organization_name',
869 'title' => ts('Current Employer'),
872 $addressOptions = CRM_Core_BAO_Setting
::valueOptions(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
873 'address_options', TRUE, NULL, TRUE
876 if (empty($addressOptions['county'])) {
877 unset($fields['Individual']['county'], $fields['Household']['county'], $fields['Organization']['county']);
880 // break out common contact fields array CRM-3037.
881 // from a UI perspective this makes very little sense
882 foreach ($fields['Individual'] as $key => $value) {
883 if (!empty($fields['Household'][$key]) && !empty($fields['Organization'][$key])) {
884 $fields['Contact'][$key] = $value;
885 unset($fields['Individual'][$key], $fields['Household'][$key], $fields['Organization'][$key]);
889 // Internal field not exposed to forms
890 unset($fields['Contact']['contact_type']);
891 unset($fields['Contact']['master_id']);
893 // convert phone extension in to psedo-field phone + phone extension
895 unset($fields['Contact']['phone_ext']);
897 $fields['Contact']['phone_and_ext'] = [
898 'name' => 'phone_and_ext',
899 'title' => ts('Phone and Extension'),
900 'hasLocationType' => 1,
903 // include Subtypes For Profile
904 $subTypes = CRM_Contact_BAO_ContactType
::subTypeInfo();
905 foreach ($subTypes as $name => $val) {
906 //custom fields for sub type
907 $subTypeFields = CRM_Core_BAO_CustomField
::getFieldsForImport($name, FALSE, FALSE, FALSE, TRUE, TRUE);
908 if (array_key_exists($val['parent'], $fields)) {
909 $fields[$name] = $fields[$val['parent']] +
$subTypeFields;
912 $fields[$name] = $subTypeFields;
916 if (CRM_Core_Permission
::access('CiviContribute')) {
917 $contribFields = CRM_Contribute_BAO_Contribution
::getContributionFields(FALSE);
918 if (!empty($contribFields)) {
919 unset($contribFields['is_test']);
920 unset($contribFields['is_pay_later']);
921 unset($contribFields['contribution_id']);
922 $contribFields['contribution_note'] = [
923 'name' => 'contribution_note',
924 'title' => ts('Contribution Note'),
926 $fields['Contribution'] = array_merge($contribFields, self
::getContribBatchEntryFields());
930 if (CRM_Core_Permission
::access('CiviEvent')) {
931 $participantFields = CRM_Event_BAO_Query
::getParticipantFields();
932 if ($participantFields) {
933 // Remove fields not supported by profiles
934 CRM_Utils_Array
::remove($participantFields,
935 'external_identifier',
937 'participant_contact_id',
938 'participant_role_id',
939 'participant_status_id',
940 'participant_is_test',
941 'participant_fee_level',
943 'participant_is_pay_later',
944 'participant_campaign'
946 if (isset($participantFields['participant_campaign_id'])) {
947 $participantFields['participant_campaign_id']['title'] = ts('Campaign');
949 $fields['Participant'] = $participantFields;
953 if (CRM_Core_Permission
::access('CiviMember')) {
954 $membershipFields = CRM_Member_BAO_Membership
::getMembershipFields();
955 // Remove fields not supported by profiles
956 CRM_Utils_Array
::remove($membershipFields,
958 'membership_type_id',
961 'member_is_override',
962 'status_override_end_date',
964 'member_is_pay_later'
966 if ($gid && CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'name') == 'membership_batch_entry') {
967 $fields['Membership'] = array_merge($membershipFields, self
::getMemberBatchEntryFields());
970 $fields['Membership'] = $membershipFields;
974 if (CRM_Core_Permission
::access('CiviCase')) {
975 $caseFields = CRM_Case_BAO_Query
::getFields(TRUE);
976 $caseFields = array_merge($caseFields, CRM_Core_BAO_CustomField
::getFieldsForImport('Case'));
978 // Remove fields not supported by profiles
979 CRM_Utils_Array
::remove($caseFields,
986 $fields['Case'] = $caseFields;
989 $activityFields = CRM_Activity_BAO_Activity
::getProfileFields();
990 if ($activityFields) {
991 // campaign related fields.
992 if (isset($activityFields['activity_campaign_id'])) {
993 $activityFields['activity_campaign_id']['title'] = ts('Campaign');
995 $fields['Activity'] = $activityFields;
998 $fields['Formatting']['format_free_html_' . rand(1000, 9999)] = [
999 'name' => 'free_html',
1002 'title' => 'Free HTML',
1006 foreach ($fields as &$values) {
1007 $values = CRM_Utils_Array
::crmArraySortByField($values, 'title');
1010 //group selected and unwanted fields list
1011 $ufFields = $gid ? CRM_Core_BAO_UFGroup
::getFields($gid, FALSE, NULL, NULL, NULL, TRUE, NULL, TRUE) : [];
1012 $groupFieldList = array_merge($ufFields, [
1014 'email_greeting_custom',
1015 'postal_greeting_custom',
1019 //unset selected fields
1020 foreach ($groupFieldList as $key => $value) {
1022 unset($fields['Individual'][$value], $fields['Household'][$value], $fields['Organization'][$value]);
1025 if (!empty($defaults['field_name'])
1026 && $defaults['field_name']['0'] == $value['field_type']
1027 && $defaults['field_name']['1'] == $key
1031 unset($fields[$value['field_type']][$key]);
1034 // Allow extensions to alter the array of entity => fields permissible in a CiviCRM Profile.
1035 CRM_Utils_Hook
::alterUFFields($fields);
1040 * Get a list of fields which can be added to profiles.
1042 * @param bool $force
1045 * e.g. $result['field_name']['label']
1047 public static function getAvailableFieldsFlat($force = FALSE) {
1048 if (!isset(Civi
::$statics['UFFieldsFlat']) ||
$force) {
1049 Civi
::$statics['UFFieldsFlat'] = [];
1050 foreach (self
::getAvailableFields() as $fieldType => $fields) {
1051 foreach ($fields as $fieldName => $field) {
1052 if (!isset(Civi
::$statics['UFFieldsFlat'][$fieldName])) {
1053 $field['field_type'] = $fieldType;
1054 Civi
::$statics['UFFieldsFlat'][$fieldName] = $field;
1059 return Civi
::$statics['UFFieldsFlat'];
1063 * Get a list of fields which can be added to profiles in the format [name => title]
1067 public static function getAvailableFieldTitles() {
1068 $fields = self
::getAvailableFieldsFlat();
1069 $fields['formatting'] = ['title' => ts('Formatting')];
1070 return CRM_Utils_Array
::collect('title', $fields);
1074 * Determine whether the given field_name is valid.
1076 * @param string $fieldName
1079 public static function isValidFieldName($fieldName) {
1080 $availableFields = CRM_Core_BAO_UFField
::getAvailableFieldsFlat();
1081 return isset($availableFields[$fieldName]);
1085 * @return array|null
1087 public static function getContribBatchEntryFields() {
1088 if (self
::$_contriBatchEntryFields === NULL) {
1089 self
::$_contriBatchEntryFields = [
1091 'name' => 'send_receipt',
1092 'title' => ts('Send Receipt'),
1095 'name' => 'soft_credit',
1096 'title' => ts('Soft Credit'),
1098 'soft_credit_type' => [
1099 'name' => 'soft_credit_type',
1100 'title' => ts('Soft Credit Type'),
1103 'name' => 'product_name',
1104 'title' => ts('Premiums'),
1106 'contribution_note' => [
1107 'name' => 'contribution_note',
1108 'title' => ts('Contribution Note'),
1110 'contribution_soft_credit_pcp_id' => [
1111 'name' => 'contribution_soft_credit_pcp_id',
1112 'title' => ts('Personal Campaign Page'),
1116 return self
::$_contriBatchEntryFields;
1120 * @return array|null
1122 public static function getMemberBatchEntryFields() {
1123 if (self
::$_memberBatchEntryFields === NULL) {
1124 self
::$_memberBatchEntryFields = [
1126 'name' => 'send_receipt',
1127 'title' => ts('Send Receipt'),
1130 'name' => 'soft_credit',
1131 'title' => ts('Soft Credit'),
1134 'name' => 'product_name',
1135 'title' => ts('Premiums'),
1137 'financial_type' => [
1138 'name' => 'financial_type',
1139 'title' => ts('Financial Type'),
1142 'name' => 'total_amount',
1143 'title' => ts('Total Amount'),
1146 'name' => 'receive_date',
1147 'title' => ts('Date Received'),
1149 'payment_instrument' => [
1150 'name' => 'payment_instrument',
1151 'title' => ts('Payment Method'),
1153 'contribution_status_id' => [
1154 'name' => 'contribution_status_id',
1155 'title' => ts('Contribution Status'),
1158 'name' => 'contribution_trxn_id',
1159 'title' => ts('Contribution Transaction ID'),
1163 return self
::$_memberBatchEntryFields;