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
21 class CRM_Core_BAO_UFGroup
extends CRM_Core_DAO_UFGroup
{
23 const PUBLIC_VISIBILITY
= 1,
25 LISTINGS_VISIBILITY
= 4;
28 * Cache the match clause used in this transaction.
32 public static $_matchFields = NULL;
35 * Fetch object based on array of properties.
37 * @param array $params
38 * (reference) an assoc array of name/value pairs.
39 * @param array $defaults
40 * (reference) an assoc array to hold the flattened values.
43 * CRM_Core_DAO_UFGroup object
45 public static function retrieve(&$params, &$defaults) {
46 return CRM_Core_DAO
::commonRetrieve('CRM_Core_DAO_UFGroup', $params, $defaults);
50 * Retrieve the first non-generic contact type
58 public static function getContactType($id) {
60 $validTypes = array_filter(array_keys(CRM_Core_SelectValues
::contactType()));
61 $validSubTypes = CRM_Contact_BAO_ContactType
::subTypeInfo();
63 $typesParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $id, 'group_type'));
64 $types = explode(',', $typesParts[0]);
67 foreach ($types as $type) {
68 if (in_array($type, $validTypes)) {
71 elseif (array_key_exists($type, $validSubTypes)) {
72 $cType = $validSubTypes[$type]['parent'] ??
NULL;
92 public static function getTitle($id) {
93 return CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $id, 'title');
97 * Update the is_active flag in the db.
100 * Id of the database record.
101 * @param bool $is_active
102 * Value we want to set the is_active field.
105 * true if we found and updated the object, else false
107 public static function setIsActive($id, $is_active) {
108 return CRM_Core_DAO
::setFieldValue('CRM_Core_DAO_UFGroup', $id, 'is_active', $is_active);
112 * Get all the registration fields.
115 * What action are we doing.
119 * @param string $ctype
122 * the fields that are needed for registration
126 public static function getRegistrationFields($action, $mode, $ctype = NULL) {
127 if ($mode & CRM_Profile_Form
::MODE_REGISTER
) {
128 $ufGroups = CRM_Core_BAO_UFGroup
::getModuleUFGroup('User Registration');
131 $ufGroups = CRM_Core_BAO_UFGroup
::getModuleUFGroup('Profile');
134 if (!is_array($ufGroups)) {
140 foreach ($ufGroups as $id => $title) {
142 $fieldType = CRM_Core_BAO_UFField
::getProfileType($id);
143 if (($fieldType != 'Contact') &&
144 ($fieldType != $ctype) &&
145 !CRM_Contact_BAO_ContactType
::isExtendsContactType($fieldType, $ctype)
149 if (CRM_Contact_BAO_ContactType
::isaSubType($fieldType)) {
150 $profileSubType = $fieldType;
154 $subset = self
::getFields($id, TRUE, $action,
155 NULL, NULL, FALSE, NULL, TRUE, $ctype
158 // we do not allow duplicates. the first field is the winner
159 foreach ($subset as $name => $field) {
160 if (empty($fields[$name])) {
161 $fields[$name] = $field;
170 * Get all the listing fields.
173 * What action are we doing.
174 * @param int $visibility
175 * Visibility of fields we are interested in.
176 * @param bool $considerSelector
177 * Whether to consider the in_selector parameter.
178 * @param array $ufGroupIds
179 * @param bool $searchable
181 * @param null $restrict
182 * @param bool $skipPermission
183 * @param int $permissionType
186 * the fields that are listings related
190 public static function getListingFields(
193 $considerSelector = FALSE,
197 $skipPermission = FALSE,
198 $permissionType = CRM_Core_Permission
::SEARCH
201 $subset = self
::getFields($ufGroupIds, FALSE, $action,
202 $visibility, $searchable,
208 if ($considerSelector) {
209 // drop the fields not meant for the selector
210 foreach ($subset as $name => $field) {
211 if (!$field['in_selector']) {
212 unset($subset[$name]);
219 $ufGroups = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_UFField', 'uf_group_id');
222 foreach ($ufGroups as $id => $title) {
223 $subset = self
::getFields($id, FALSE, $action,
224 $visibility, $searchable,
230 if ($considerSelector) {
231 // drop the fields not meant for the selector
232 foreach ($subset as $name => $field) {
233 if (!$field['in_selector']) {
234 unset($subset[$name]);
238 $fields = array_merge($fields, $subset);
245 * Get all the fields that belong to the group with the name title,
246 * and format for use with buildProfile. This is the SQL analog of
250 * The id of the UF group or ids of ufgroup.
251 * @param bool|int $register are we interested in registration fields
253 * What action are we doing.
254 * @param int $visibility
255 * Visibility of fields we are interested in.
256 * @param bool $searchable
257 * @param bool $showAll
258 * @param string $restrict
259 * Should we restrict based on a specified profile type.
260 * @param bool $skipPermission
262 * @param int $permissionType
263 * @param string $orderBy
264 * @param bool $orderProfiles
265 * @param bool $eventProfile
267 * The fields that belong to this ufgroup(s)
269 * @throws \CRM_Core_Exception
271 public static function getFields(
279 $skipPermission = FALSE,
281 $permissionType = CRM_Core_Permission
::CREATE
,
282 $orderBy = 'field_name',
283 $orderProfiles = FALSE,
284 $eventProfile = FALSE
286 if (!is_array($id)) {
287 $id = CRM_Utils_Type
::escape($id, 'Positive');
294 $gids = implode(',', $profileIds);
297 $query = "SELECT g.* from civicrm_uf_group g
298 LEFT JOIN civicrm_uf_join j ON (j.uf_group_id = g.id)
299 WHERE g.id IN ( {$gids} )
300 AND ((j.uf_group_id IN ( {$gids} ) AND j.module = %1) OR g.is_reserved = 1 )
302 $params = [1 => [$restrict, 'String']];
305 $query = "SELECT g.* from civicrm_uf_group g WHERE g.id IN ( {$gids} ) ";
309 $query .= " AND g.is_active = 1";
314 'administer CiviCRM',
315 'manage event profiles',
318 if ($eventProfile && CRM_Core_Permission
::check($checkPermission)) {
319 $skipPermission = TRUE;
322 // add permissioning for profiles only if not registration
323 if (!$skipPermission) {
324 $permissionClause = CRM_Core_Permission
::ufGroupClause($permissionType, 'g.');
325 $query .= " AND $permissionClause ";
328 if ($orderProfiles and count($profileIds) > 1) {
329 $query .= " ORDER BY FIELD( g.id, {$gids} )";
331 $group = CRM_Core_DAO
::executeQuery($query, $params);
335 while ($group->fetch()) {
337 $query = self
::createUFFieldQuery($group->id
, $searchable, $showAll, $visibility, $orderBy);
338 $field = CRM_Core_DAO
::executeQuery($query);
340 $importableFields = self
::getProfileFieldMetadata($showAll);
341 list($customFields, $addressCustomFields) = self
::getCustomFields($ctype, $skipPermission ?
FALSE : $permissionType);
343 while ($field->fetch()) {
344 list($name, $formattedField) = self
::formatUFField($group, $field, $customFields, $addressCustomFields, $importableFields, $permissionType);
345 if ($formattedField !== NULL) {
346 $fields[$name] = $formattedField;
351 if (empty($fields) && !$validGroup) {
352 throw new CRM_Core_Exception(ts('The requested Profile (gid=%1) is disabled OR it is not configured to be used for \'Profile\' listings in its Settings OR there is no Profile with that ID OR you do not have permission to access this profile. Please contact the site administrator if you need assistance.',
353 [1 => implode(',', $profileIds)]
357 self
::reformatProfileFields($fields);
364 * Format a list of UFFields for use with buildProfile. This is the in-memory analog
367 * @param array $groupArr
368 * (mimic CRM_UF_DAO_UFGroup).
369 * @param array $fieldArrs
370 * List of fields (each mimics CRM_UF_DAO_UFField).
371 * @param bool $visibility
372 * Visibility of fields we are interested in.
373 * @param bool $searchable
374 * @param bool $showAll
376 * @param int $permissionType
379 * @see self::getFields
381 public static function formatUFFields(
388 $permissionType = CRM_Core_Permission
::CREATE
390 // $group = new CRM_Core_DAO_UFGroup();
391 // $group->copyValues($groupArr); // no... converts string('') to string('null')
392 $group = (object) $groupArr;
394 // Refactoring note: The $fieldArrs here may be slightly different than the $ufFields
395 // used by calculateGroupType, but I don't think the missing fields matter, and -- if
396 // they did -- the obvious fix would produce mutual recursion.
397 $ufGroupType = self
::_calculateGroupType($fieldArrs);
398 $profileType = CRM_Core_BAO_UFField
::calculateProfileType(implode(',', $ufGroupType));
399 $contactActivityProfile = CRM_Core_BAO_UFField
::checkContactActivityProfileTypeByGroupType(implode(',', $ufGroupType));
400 $importableFields = self
::getImportableFields($showAll, $profileType, $contactActivityProfile);
401 list($customFields, $addressCustomFields) = self
::getCustomFields($ctype, $permissionType);
403 $formattedFields = [];
404 foreach ($fieldArrs as $fieldArr) {
405 $field = (object) $fieldArr;
406 if (!self
::filterUFField($field, $searchable, $showAll, $visibility)) {
410 list($name, $formattedField) = self
::formatUFField($group, $field, $customFields, $addressCustomFields, $importableFields, $permissionType);
411 if ($formattedField !== NULL) {
412 $formattedFields[$name] = $formattedField;
415 return $formattedFields;
419 * Prepare a field for rendering with CRM_Core_BAO_UFGroup::buildProfile.
421 * @param CRM_Core_DAO_UFGroup|CRM_Core_DAO $group
422 * @param CRM_Core_DAO_UFField|CRM_Core_DAO $field
423 * @param array $customFields
424 * @param array $addressCustomFields
425 * @param array $importableFields
426 * @param int $permissionType
427 * Eg CRM_Core_Permission::CREATE.
431 protected static function formatUFField(
435 $addressCustomFields,
437 $permissionType = CRM_Core_Permission
::CREATE
439 $name = $field->field_name
;
440 $title = $field->label
;
442 $addressCustom = FALSE;
443 if (in_array($permissionType, [CRM_Core_Permission
::CREATE
, CRM_Core_Permission
::EDIT
]) &&
444 array_key_exists($field->field_name
, $addressCustomFields)
446 $addressCustom = TRUE;
447 $name = "address_{$name}";
449 if ($field->field_name
== 'url') {
450 $name .= "-{$field->website_type_id}";
452 elseif (!empty($field->location_type_id
)) {
453 $name .= "-{$field->location_type_id}";
456 $locationFields = self
::getLocationFields();
457 if (in_array($field->field_name
, $locationFields) ||
$addressCustom) {
462 if (isset($field->phone_type_id
)) {
463 $name .= "-{$field->phone_type_id}";
465 $fieldMetaData = CRM_Utils_Array
::value($name, $importableFields, ($importableFields[$field->field_name
] ??
[]));
467 // No lie: this is bizarre; why do we need to mix so many UFGroup properties into UFFields?
468 // I guess to make field self sufficient with all the required data and avoid additional calls
471 'groupTitle' => $group->title
,
472 'groupName' => $group->name
,
473 'groupDisplayTitle' => (!empty($group->frontend_title
)) ?
$group->frontend_title
: $group->title
,
474 'groupHelpPre' => empty($group->help_pre
) ?
'' : $group->help_pre
,
475 'groupHelpPost' => empty($group->help_post
) ?
'' : $group->help_post
,
477 'where' => CRM_Utils_Array
::value('where', CRM_Utils_Array
::value($field->field_name
, $importableFields)),
478 'attributes' => CRM_Core_DAO
::makeAttribute(CRM_Utils_Array
::value($field->field_name
, $importableFields)),
479 'is_required' => $field->is_required
,
480 'is_view' => $field->is_view
,
481 'help_pre' => $field->help_pre
,
482 'help_post' => $field->help_post
,
483 'visibility' => $field->visibility
,
484 'in_selector' => $field->in_selector
,
485 'rule' => CRM_Utils_Array
::value('rule', CRM_Utils_Array
::value($field->field_name
, $importableFields)),
486 'location_type_id' => $field->location_type_id ??
NULL,
487 'website_type_id' => $field->website_type_id ??
NULL,
488 'phone_type_id' => $field->phone_type_id ??
NULL,
489 'group_id' => $group->id
,
490 'add_to_group_id' => $group->add_to_group_id ??
NULL,
491 'add_captcha' => $group->add_captcha ??
NULL,
492 'field_type' => $field->field_type
,
493 'field_id' => $field->id
,
494 'pseudoconstant' => CRM_Utils_Array
::value(
496 CRM_Utils_Array
::value($field->field_name
, $importableFields)
498 // obsolete this when we remove the name / dbName discrepancy with gender/suffix/prefix
499 'dbName' => CRM_Utils_Array
::value(
501 CRM_Utils_Array
::value($field->field_name
, $importableFields)
504 'data_type' => CRM_Utils_Type
::getDataTypeFromFieldMetadata($fieldMetaData),
505 'bao' => $fieldMetaData['bao'] ??
NULL,
508 $formattedField = CRM_Utils_Date
::addDateMetadataToField($fieldMetaData, $formattedField);
510 //adding custom field property
511 if (substr($field->field_name
, 0, 6) == 'custom' ||
512 substr($field->field_name
, 0, 14) === 'address_custom'
514 // if field is not present in customFields, that means the user
515 // DOES NOT HAVE permission to access that field
516 if (array_key_exists($field->field_name
, $customFields)) {
517 $formattedField['serialize'] = !empty($customFields[$field->field_name
]['serialize']);
518 $formattedField['is_search_range'] = $customFields[$field->field_name
]['is_search_range'];
520 $formattedField['options_per_line'] = $customFields[$field->field_name
]['options_per_line'];
521 $formattedField['html_type'] = $customFields[$field->field_name
]['html_type'];
523 if (CRM_Utils_Array
::value('html_type', $formattedField) == 'Select Date') {
524 $formattedField['date_format'] = $customFields[$field->field_name
]['date_format'];
525 $formattedField['time_format'] = $customFields[$field->field_name
]['time_format'];
526 $formattedField['is_datetime_field'] = TRUE;
527 $formattedField['smarty_view_format'] = CRM_Utils_Date
::getDateFieldViewFormat($formattedField['date_format']);
530 $formattedField['is_multi_summary'] = $field->is_multi_summary
;
531 return [$name, $formattedField];
534 $formattedField = NULL;
535 return [$name, $formattedField];
538 return [$name, $formattedField];
542 * Create a query to find all visible UFFields in a UFGroup.
544 * This is the SQL-variant of checkUFFieldDisplayable().
546 * @param int $groupId
547 * @param bool $searchable
548 * @param bool $showAll
549 * @param int $visibility
550 * @param string $orderBy
551 * Comma-delimited list of SQL columns.
555 protected static function createUFFieldQuery($groupId, $searchable, $showAll, $visibility, $orderBy) {
556 $where = " WHERE uf_group_id = {$groupId}";
559 $where .= " AND is_searchable = 1";
563 $where .= " AND is_active = 1";
568 if ($visibility & self
::PUBLIC_VISIBILITY
) {
569 $clause[] = 'visibility = "Public Pages"';
571 if ($visibility & self
::ADMIN_VISIBILITY
) {
572 $clause[] = 'visibility = "User and User Admin Only"';
574 if ($visibility & self
::LISTINGS_VISIBILITY
) {
575 $clause[] = 'visibility = "Public Pages and Listings"';
577 if (!empty($clause)) {
578 $where .= ' AND ( ' . implode(' OR ', $clause) . ' ) ';
582 $query = "SELECT * FROM civicrm_uf_field $where ORDER BY weight";
584 $query .= ", " . $orderBy;
591 * Create a query to find all visible UFFields in a UFGroup.
593 * This is the PHP in-memory variant of createUFFieldQuery().
595 * @param CRM_Core_DAO_UFField|CRM_Core_DAO $field
596 * @param bool $searchable
597 * @param bool $showAll
598 * @param int $visibility
601 * TRUE if field is displayable
603 protected static function filterUFField($field, $searchable, $showAll, $visibility) {
604 if ($searchable && $field->is_searchable
!= 1) {
608 if (!$showAll && $field->is_active
!= 1) {
613 $allowedVisibilities = [];
614 if ($visibility & self
::PUBLIC_VISIBILITY
) {
615 $allowedVisibilities[] = 'Public Pages';
617 if ($visibility & self
::ADMIN_VISIBILITY
) {
618 $allowedVisibilities[] = 'User and User Admin Only';
620 if ($visibility & self
::LISTINGS_VISIBILITY
) {
621 $allowedVisibilities[] = 'Public Pages and Listings';
623 // !empty($allowedVisibilities) seems silly to me, but it is equivalent to the pre-existing SQL
624 if (!empty($allowedVisibilities) && !in_array($field->visibility
, $allowedVisibilities)) {
633 * Get a list of filtered field metadata.
636 * @param $profileType
637 * @param $contactActivityProfile
638 * @param bool $filterMode
639 * Filter mode means you are using importable fields for filtering rather than just getting metadata.
640 * With filter mode = FALSE BOTH activity fields and component fields are returned.
641 * I can't see why you would ever want to use this function in filter mode as the component fields are
642 * still unfiltered. However, I feel scared enough to leave it as it is. I have marked this function as
643 * deprecated and am recommending the wrapper 'getProfileFieldMetadata' in order to try to
644 * send this confusion to history.
647 * @deprecated use getProfileFieldMetadata
650 protected static function getImportableFields($showAll, $profileType, $contactActivityProfile, $filterMode = TRUE) {
652 $importableFields = CRM_Contact_BAO_Contact
::importableFields('All', FALSE, FALSE, FALSE, TRUE, TRUE);
655 $importableFields = CRM_Contact_BAO_Contact
::importableFields('All', FALSE, TRUE, FALSE, TRUE, TRUE);
658 $activityFields = CRM_Activity_BAO_Activity
::getProfileFields();
659 $componentFields = CRM_Core_Component
::getQueryFields();
660 if ($filterMode == TRUE) {
661 if ($profileType == 'Activity' ||
$contactActivityProfile) {
662 $importableFields = array_merge($importableFields, $activityFields);
665 $importableFields = array_merge($importableFields, $componentFields);
669 $importableFields = array_merge($importableFields, $activityFields, $componentFields);
672 $importableFields['group']['title'] = ts('Group(s)');
673 $importableFields['group']['where'] = NULL;
674 $importableFields['tag']['title'] = ts('Tag(s)');
675 $importableFields['tag']['where'] = NULL;
676 return $importableFields;
680 * Get the metadata for all potential profile fields.
682 * @param bool $isIncludeInactive
683 * Should disabled fields be included.
686 * Field metadata for all fields that might potentially be in a profile.
688 protected static function getProfileFieldMetadata($isIncludeInactive) {
689 return self
::getImportableFields($isIncludeInactive, NULL, NULL, NULL, TRUE);
693 * Get the fields relating to locations.
697 public static function getLocationFields() {
698 static $locationFields = [
700 'supplemental_address_1',
701 'supplemental_address_2',
702 'supplemental_address_3',
705 'postal_code_suffix',
718 return $locationFields;
723 * @param int|bool $checkPermission
726 protected static function getCustomFields($ctype, $checkPermission = CRM_Core_Permission
::VIEW
) {
727 // Only Edit and View is supported in ACL for custom field.
728 if ($checkPermission == CRM_Core_Permission
::CREATE
) {
729 $checkPermission = CRM_Core_Permission
::EDIT
;
731 $cacheKey = 'uf_group_custom_fields_' . $ctype . '_' . (int) $checkPermission;
732 if (!Civi
::cache('metadata')->has($cacheKey)) {
733 $customFields = CRM_Core_BAO_CustomField
::getFieldsForImport($ctype, FALSE, FALSE, FALSE, $checkPermission, TRUE);
735 // hack to add custom data for components
736 $components = ['Contribution', 'Participant', 'Membership', 'Activity', 'Case'];
737 foreach ($components as $value) {
738 $customFields = array_merge($customFields, CRM_Core_BAO_CustomField
::getFieldsForImport($value));
740 $addressCustomFields = CRM_Core_BAO_CustomField
::getFieldsForImport('Address');
741 $customFields = array_merge($customFields, $addressCustomFields);
742 Civi
::cache('metadata')->set($cacheKey, [$customFields, $addressCustomFields]);
744 return Civi
::cache('metadata')->get($cacheKey);
748 * Check the data validity.
751 * The user id that we are actually editing.
752 * @param string $name
753 * The machine-name of the group we are interested in.
754 * @param bool $register
756 * The action of the form.
758 * @pram boolean $register is this the registrtion form
760 * true if form is valid
762 public static function isValid($userID, $name, $register = FALSE, $action = NULL) {
764 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
765 ts('Dynamic Form Creator'),
768 $controller->set('id', $userID);
769 $controller->set('register', 1);
770 $controller->process();
771 return $controller->validate();
774 // make sure we have a valid group
775 $group = new CRM_Core_DAO_UFGroup();
777 $group->name
= $name;
779 if ($group->find(TRUE) && $userID) {
780 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic', ts('Dynamic Form Creator'), $action);
781 $controller->set('gid', $group->id
);
782 $controller->set('id', $userID);
783 $controller->set('register', 0);
784 $controller->process();
785 return $controller->validate();
792 * Get the html for the form that represents this particular group.
795 * The user id that we are actually editing.
796 * @param string $title
797 * The title of the group we are interested in.
799 * The action of the form.
800 * @param bool $register
801 * Is this the registration form.
803 * Should we reset the form?.
804 * @param int $profileID
805 * Do we have the profile ID?.
807 * @param bool $doNotProcess
811 * the html for the form on success, otherwise empty string
813 public static function getEditHTML(
820 $doNotProcess = FALSE,
825 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
826 ts('Dynamic Form Creator'),
829 if ($reset ||
$doNotProcess) {
830 // hack to make sure we do not process this form
831 $oldQFDefault = CRM_Utils_Array
::value('_qf_default',
834 unset($_POST['_qf_default']);
835 unset($_REQUEST['_qf_default']);
837 $controller->reset();
841 $controller->set('id', $userID);
842 $controller->set('register', 1);
843 $controller->set('skipPermission', 1);
844 $controller->set('ctype', $ctype);
845 $controller->process();
846 if ($doNotProcess ||
!empty($_POST)) {
847 $controller->validate();
849 $controller->setEmbedded(TRUE);
851 //CRM-5839 - though we want to process form, get the control back.
852 $controller->setSkipRedirection(!$doNotProcess);
856 // we are done processing so restore the POST/REQUEST vars
857 if (($reset ||
$doNotProcess) && $oldQFDefault) {
858 $_POST['_qf_default'] = $_REQUEST['_qf_default'] = $oldQFDefault;
861 $template = CRM_Core_Smarty
::singleton();
863 // Hide CRM error messages if they are displayed using drupal form_set_error.
864 if (!empty($_POST)) {
865 $template->assign('suppressForm', TRUE);
868 return trim($template->fetch('CRM/Profile/Form/Dynamic.tpl'));
872 // make sure we have a valid group
873 $group = new CRM_Core_DAO_UFGroup();
875 $group->title
= $title;
877 if ($group->find(TRUE)) {
878 $profileID = $group->id
;
883 // make sure profileID and ctype match if ctype exists
885 $profileType = CRM_Core_BAO_UFField
::getProfileType($profileID);
886 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
887 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
890 if (($profileType != 'Contact') && ($profileType != $ctype)) {
895 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
896 ts('Dynamic Form Creator'),
900 $controller->reset();
902 $controller->set('gid', $profileID);
903 $controller->set('id', $userID);
904 $controller->set('register', 0);
905 $controller->set('skipPermission', 1);
907 $controller->set('ctype', $ctype);
909 $controller->process();
910 $controller->setEmbedded(TRUE);
912 //CRM-5846 - give the control back to drupal.
913 $controller->setSkipRedirection(!$doNotProcess);
916 $template = CRM_Core_Smarty
::singleton();
918 // Hide CRM error messages if they are displayed using drupal form_set_error.
919 if (!empty($_POST) && CRM_Core_Config
::singleton()->userFramework
== 'Drupal') {
920 if (arg(0) == 'user' ||
(arg(0) == 'admin' && arg(1) == 'people')) {
921 $template->assign('suppressForm', TRUE);
925 $templateFile = "CRM/Profile/Form/{$profileID}/Dynamic.tpl";
926 if (!$template->template_exists($templateFile)) {
927 $templateFile = 'CRM/Profile/Form/Dynamic.tpl';
929 return trim($template->fetch($templateFile));
932 $userEmail = CRM_Contact_BAO_Contact_Location
::getEmailDetails($userID);
934 // if post not empty then only proceed
935 if (!empty($_POST)) {
937 $config = CRM_Core_Config
::singleton();
938 $email = $_POST['mail'] ??
NULL;
940 if (CRM_Utils_Rule
::email($email) && ($email != $userEmail[1])) {
941 CRM_Core_BAO_UFMatch
::updateContactEmail($userID, $email);
950 * Given a contact id and a field set, return the values from the db.
953 * @param array $fields
954 * The profile fields of interest.
955 * @param array $values
956 * The values for the above fields.
957 * @param bool $searchable
959 * @param array $componentWhere
960 * Component condition.
961 * @param bool $absolute
962 * Return urls in absolute form (useful when sending an email).
963 * @param null $additionalWhereClause
967 public static function getValues(
968 $cid, &$fields, &$values,
969 $searchable = TRUE, $componentWhere = NULL,
970 $absolute = FALSE, $additionalWhereClause = NULL
972 if (empty($cid) && empty($componentWhere)) {
976 // get the contact details (hier)
977 $returnProperties = CRM_Contact_BAO_Contact
::makeHierReturnProperties($fields);
978 $params = $cid ?
[['contact_id', '=', $cid, 0, 0]] : [];
980 // add conditions specified by components. eg partcipant_id etc
981 if (!empty($componentWhere)) {
982 $params = array_merge($params, $componentWhere);
985 $query = new CRM_Contact_BAO_Query($params, $returnProperties, $fields);
987 $details = $query->searchQuery(0, 0, NULL, FALSE, FALSE,
988 FALSE, FALSE, FALSE, $additionalWhereClause);
989 while ($details->fetch()) {
994 $query->convertToPseudoNames($details);
996 $locationTypes = CRM_Core_BAO_Address
::buildOptions('location_type_id', 'validate');
997 $imProviders = CRM_Core_DAO_IM
::buildOptions('provider_id');
998 $websiteTypes = CRM_Core_DAO_Website
::buildOptions('website_type_id');
1000 $multipleFields = ['url'];
1002 //start of code to set the default values
1003 foreach ($fields as $name => $field) {
1005 if ($name == 'id') {
1006 $name = 'contact_id';
1009 // skip fields that should not be displayed separately
1010 if (!empty($field['skipDisplay'])) {
1014 // Create a unique, non-empty index for each field.
1015 $index = $field['title'];
1016 if ($index === '') {
1019 while (array_key_exists($index, $values)) {
1023 $params[$index] = $values[$index] = '';
1024 $customFieldName = NULL;
1026 if (isset($details->$name) ||
$name == 'group' ||
$name == 'tag') {
1027 // to handle gender / suffix / prefix
1028 if (in_array(substr($name, 0, -3), ['gender', 'prefix', 'suffix'])) {
1029 $params[$index] = $details->$name;
1030 $values[$index] = $details->$name;
1032 elseif (in_array($name, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
1033 $dname = $name . '_display';
1034 $values[$index] = $details->$dname;
1035 $name = $name . '_id';
1036 $params[$index] = $details->$name;
1038 elseif (in_array($name, [
1043 $values[$index] = $details->$name;
1044 $idx = $name . '_id';
1045 $params[$index] = $details->$idx;
1047 elseif ($name === 'preferred_language') {
1048 $params[$index] = $details->$name;
1049 $values[$index] = CRM_Core_PseudoConstant
::getLabel('CRM_Contact_DAO_Contact', 'preferred_language', $details->$name);
1051 elseif ($name == 'group') {
1052 $groups = CRM_Contact_BAO_GroupContact
::getContactGroup($cid, 'Added', NULL, FALSE, TRUE);
1055 foreach ($groups as $g) {
1056 // CRM-8362: User and User Admin visibility groups should be included in display if user has
1057 // VIEW permission on that group
1058 $groupPerm = CRM_Contact_BAO_Group
::checkPermission($g['group_id'], TRUE);
1060 if ($g['visibility'] != 'User and User Admin Only' ||
1061 CRM_Utils_Array
::key(CRM_Core_Permission
::VIEW
, $groupPerm)
1063 $title[] = $g['title'];
1064 if ($g['visibility'] == 'Public Pages') {
1065 $ids[] = $g['group_id'];
1069 $values[$index] = implode(', ', $title);
1070 $params[$index] = implode(',', $ids);
1072 elseif ($name == 'tag') {
1073 $entityTags = CRM_Core_BAO_EntityTag
::getTag($cid);
1074 $allTags = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]);
1076 foreach ($entityTags as $tagId) {
1077 $title[] = $allTags[$tagId];
1079 $values[$index] = implode(', ', $title);
1080 $params[$index] = implode(',', $entityTags);
1082 elseif ($name == 'activity_status_id') {
1083 $activityStatus = CRM_Core_PseudoConstant
::activityStatus();
1084 $values[$index] = $activityStatus[$details->$name];
1085 $params[$index] = $details->$name;
1087 elseif ($name == 'activity_date_time') {
1088 $values[$index] = CRM_Utils_Date
::customFormat($details->$name);
1089 $params[$index] = $details->$name;
1091 elseif ($name == 'contact_sub_type') {
1092 $contactSubTypeNames = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $details->$name);
1093 if (!empty($contactSubTypeNames)) {
1094 $contactSubTypeLabels = [];
1095 // get all contact subtypes
1096 $allContactSubTypes = CRM_Contact_BAO_ContactType
::subTypeInfo();
1097 // build contact subtype labels array
1098 foreach ($contactSubTypeNames as $cstName) {
1100 $contactSubTypeLabels[] = $allContactSubTypes[$cstName]['label'];
1103 $values[$index] = implode(',', $contactSubTypeLabels);
1106 $params[$index] = $details->$name;
1109 if (substr($name, 0, 7) === 'do_not_' ||
substr($name, 0, 3) === 'is_') {
1110 if ($details->$name) {
1111 $values[$index] = '[ x ]';
1115 if ($cfID = CRM_Core_BAO_CustomField
::getKeyID($name)) {
1116 $htmlType = $field['html_type'];
1118 // field_type is only set when we are retrieving profile values
1119 // when sending email, we call the same function to get custom field
1120 // values etc, i.e. emulating a profile
1121 $fieldType = $field['field_type'] ??
NULL;
1123 if ($htmlType == 'File') {
1126 $fieldType == 'Activity' && !empty($componentWhere[0][2])
1128 $entityId = $componentWhere[0][2];
1131 $fileURL = CRM_Core_BAO_CustomField
::getFileURL($entityId,
1135 $additionalWhereClause
1137 $params[$index] = $values[$index] = $fileURL['file_url'];
1141 if (isset($dao) && property_exists($dao, 'data_type') &&
1142 ($dao->data_type
== 'Int' ||
1143 $dao->data_type
== 'Boolean'
1146 $customVal = (int ) ($details->{$name});
1148 elseif (isset($dao) && property_exists($dao, 'data_type')
1149 && $dao->data_type
== 'Float'
1151 $customVal = (float ) ($details->{$name});
1153 elseif (!CRM_Utils_System
::isNull(explode(CRM_Core_DAO
::VALUE_SEPARATOR
,
1157 $customVal = $details->{$name};
1161 if (CRM_Utils_System
::isNull($customVal)) {
1165 $params[$index] = $customVal;
1166 $values[$index] = CRM_Core_BAO_CustomField
::displayValue($customVal, $cfID);
1167 if ($field['data_type'] == 'ContactReference') {
1168 $params[$index] = $values[$index];
1170 if (CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_CustomField',
1171 $cfID, 'is_search_range'
1174 $customFieldName = "{$name}_from";
1178 elseif ($name == 'image_URL') {
1179 list($width, $height) = getimagesize(CRM_Utils_String
::unstupifyUrl($details->$name));
1180 list($thumbWidth, $thumbHeight) = CRM_Contact_BAO_Contact
::getThumbSize($width, $height);
1182 $image_URL = '<img src="' . $details->$name . '" height= ' . $thumbHeight . ' width= ' . $thumbWidth . ' />';
1183 $values[$index] = "<a href='#' onclick='contactImagePopUp(\"{$details->$name}\", {$width}, {$height});'>{$image_URL}</a>";
1185 elseif (in_array($name, [
1189 // @todo this set should be determined from metadata, not hard-coded.
1190 $values[$index] = CRM_Utils_Date
::customFormat($details->$name);
1191 $params[$index] = CRM_Utils_Date
::isoToMysql($details->$name);
1195 if ($index == 'Campaign') {
1196 $dao = 'CRM_Campaign_DAO_Campaign';
1198 elseif ($index == 'Contribution Page') {
1199 $dao = 'CRM_Contribute_DAO_ContributionPage';
1202 $value = CRM_Core_DAO
::getFieldValue($dao, $details->$name, 'title');
1205 $value = $details->$name;
1207 $values[$index] = $value;
1212 elseif (strpos($name, '-') !== FALSE) {
1213 list($fieldName, $id, $type) = CRM_Utils_System
::explode('-', $name, 3);
1215 if (!in_array($fieldName, $multipleFields)) {
1216 if ($id == 'Primary') {
1218 // not sure why we'd every use Primary location type id
1219 // we need to fix the source if we are using it
1220 // $locationTypeName = CRM_Contact_BAO_Contact::getPrimaryLocationType( $cid );
1221 $locationTypeName = 1;
1224 $locationTypeName = $locationTypes[$id] ??
NULL;
1227 if (!$locationTypeName) {
1231 $detailName = "{$locationTypeName}-{$fieldName}";
1232 $detailName = str_replace(' ', '_', $detailName);
1234 if (in_array($fieldName, [
1241 $detailName .= "-{$type}";
1245 if (in_array($fieldName, [
1250 $values[$index] = $details->$detailName;
1251 $idx = $detailName . '_id';
1252 $params[$index] = $details->$idx;
1254 elseif ($fieldName == 'im') {
1255 $providerId = $detailName . '-provider_id';
1256 if (isset($imProviders[$details->$providerId])) {
1257 $values[$index] = $details->$detailName . " (" . $imProviders[$details->$providerId] . ")";
1260 $values[$index] = $details->$detailName;
1262 $params[$index] = $details->$detailName;
1264 elseif ($fieldName == 'phone') {
1265 $phoneExtField = str_replace('phone', 'phone_ext', $detailName);
1266 if (isset($details->$phoneExtField)) {
1267 $values[$index] = $details->$detailName . " (" . $details->$phoneExtField . ")";
1270 $values[$index] = $details->$detailName;
1272 $params[$index] = $details->$detailName;
1275 $values[$index] = $params[$index] = $details->$detailName;
1279 $detailName = "website-{$id}-{$fieldName}";
1280 $url = CRM_Utils_System
::fixURL($details->$detailName);
1281 if ($details->$detailName) {
1282 $websiteTypeId = "website-{$id}-website_type_id";
1283 $websiteType = $websiteTypes[$details->$websiteTypeId];
1284 $values[$index] = "<a href=\"$url\">{$details->$detailName} ( {$websiteType} )</a>";
1287 $values[$index] = '';
1292 if ((CRM_Utils_Array
::value('visibility', $field) == 'Public Pages and Listings') &&
1293 CRM_Core_Permission
::check('profile listings and forms')
1296 if (CRM_Utils_System
::isNull($params[$index])) {
1297 $params[$index] = $values[$index];
1299 if (!isset($params[$index])) {
1302 if (!$customFieldName) {
1303 $fieldName = $field['name'];
1306 $fieldName = $customFieldName;
1310 if (CRM_Core_BAO_CustomField
::getKeyID($field['name'])) {
1311 $htmlType = $field['html_type'];
1312 if ($htmlType == 'Link') {
1313 $url = $params[$index];
1315 elseif (!empty($field['serialize'])) {
1316 $valSeparator = CRM_Core_DAO
::VALUE_SEPARATOR
;
1317 $selectedOptions = explode($valSeparator, $params[$index]);
1319 foreach ($selectedOptions as $key => $multiOption) {
1321 $url[] = CRM_Utils_System
::url('civicrm/profile',
1322 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1323 urlencode($fieldName) .
1325 urlencode($multiOption)
1331 $url = CRM_Utils_System
::url('civicrm/profile',
1332 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1333 urlencode($fieldName) .
1335 urlencode($params[$index])
1340 $url = CRM_Utils_System
::url('civicrm/profile',
1341 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1342 urlencode($fieldName) .
1344 urlencode($params[$index])
1349 !empty($values[$index]) &&
1353 if (is_array($url) && !empty($url)) {
1355 $eachMultiValue = explode(', ', $values[$index]);
1356 foreach ($eachMultiValue as $key => $valueLabel) {
1357 $links[] = '<a href="' . $url[$key] . '">' . $valueLabel . '</a>';
1359 $values[$index] = implode(', ', $links);
1362 $values[$index] = '<a href="' . $url . '">' . $values[$index] . '</a>';
1370 * Check if profile Group used by any module.
1378 public static function usedByModule($id) {
1379 //check whether this group is used by any module(check uf join records)
1381 FROM civicrm_uf_join
1382 WHERE civicrm_uf_join.uf_group_id=$id";
1384 $dao = new CRM_Core_DAO();
1386 if ($dao->fetch()) {
1395 * Delete the profile Group.
1403 public static function del($id) {
1404 CRM_Utils_Hook
::pre('delete', 'UFGroup', $id);
1406 //check whether this group contains any profile fields
1407 $profileField = new CRM_Core_DAO_UFField();
1408 $profileField->uf_group_id
= $id;
1409 $profileField->find();
1410 while ($profileField->fetch()) {
1411 CRM_Core_BAO_UFField
::del($profileField->id
);
1414 //delete records from uf join table
1415 $ufJoin = new CRM_Core_DAO_UFJoin();
1416 $ufJoin->uf_group_id
= $id;
1419 //delete profile group
1420 $group = new CRM_Core_DAO_UFGroup();
1424 CRM_Utils_Hook
::post('delete', 'UFGroup', $id, $group);
1431 * @param array $params
1432 * Reference array contains the values submitted by the form.
1439 public static function add(&$params, $ids = []) {
1440 if (empty($params['id']) && !empty($ids['ufgroup'])) {
1441 $params['id'] = $ids['ufgroup'];
1442 CRM_Core_Error
::deprecatedWarning('ids parameter is deprecated');
1445 // Convert parameter names but don't overwrite existing data on updates
1446 // unless explicitly specified. And allow setting to null, so use
1447 // array_key_exists. i.e. we need to treat missing and empty separately.
1448 if (array_key_exists('group', $params)) {
1449 $params['limit_listings_group_id'] = $params['group'];
1451 if (array_key_exists('add_contact_to_group', $params)) {
1452 $params['add_to_group_id'] = $params['add_contact_to_group'];
1456 if (!empty($params['group_type']) && is_array($params['group_type'])) {
1457 $params['group_type'] = implode(',', $params['group_type']);
1460 $hook = empty($params['id']) ?
'create' : 'edit';
1461 CRM_Utils_Hook
::pre($hook, 'UFGroup', ($params['id'] ??
NULL), $params);
1463 $ufGroup = new CRM_Core_DAO_UFGroup();
1464 $ufGroup->copyValues($params);
1466 $ufGroupID = CRM_Utils_Array
::value('ufgroup', $ids, CRM_Utils_Array
::value('id', $params));
1467 if (!$ufGroupID && empty($params['name'])) {
1468 $ufGroup->name
= CRM_Utils_String
::munge($ufGroup->title
, '_', 56);
1470 $ufGroup->id
= $ufGroupID;
1474 if (!$ufGroupID && empty($params['name'])) {
1475 $ufGroup->name
= $ufGroup->name
. "_{$ufGroup->id}";
1479 CRM_Utils_Hook
::post($hook, 'UFGroup', $ufGroup->id
, $ufGroup);
1485 * Make uf join entries for an uf group.
1487 * @param int $weight
1488 * @param array $groupTypes
1489 * An assoc array of name/value pairs.
1490 * @param int $ufGroupId
1493 public static function createUFJoin($weight, $groupTypes, $ufGroupId) {
1495 // get ufjoin records for uf group
1496 $ufGroupRecord = CRM_Core_BAO_UFGroup
::getUFJoinRecord($ufGroupId);
1498 // get the list of all ufgroup types
1499 $allUFGroupType = CRM_Core_SelectValues
::ufGroupTypes();
1501 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
1502 if (!is_array($groupTypes)) {
1506 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
1507 if (!is_array($ufGroupRecord)) {
1508 $ufGroupRecord = [];
1511 // check which values has to be inserted/deleted for contact
1512 $menuRebuild = FALSE;
1513 foreach ($allUFGroupType as $key => $value) {
1515 $joinParams['uf_group_id'] = $ufGroupId;
1516 $joinParams['module'] = $key;
1517 if ($key === 'User Account') {
1518 $menuRebuild = TRUE;
1520 if (array_key_exists($key, $groupTypes) && !in_array($key, $ufGroupRecord)) {
1521 // insert a new record
1522 CRM_Core_BAO_UFGroup
::addUFJoin($joinParams);
1524 elseif (!array_key_exists($key, $groupTypes) && in_array($key, $ufGroupRecord)) {
1525 // delete a record for existing ufgroup
1526 CRM_Core_BAO_UFGroup
::delUFJoin($joinParams);
1532 UPDATE civicrm_uf_join
1534 WHERE uf_group_id = %2
1535 AND ( entity_id IS NULL OR entity_id <= 0 )
1538 1 => [$weight, 'Integer'],
1539 2 => [$ufGroupId, 'Integer'],
1541 CRM_Core_DAO
::executeQuery($query, $p);
1543 // Do a menu rebuild, so it gets all the new menu entries for user account
1545 $config = CRM_Core_Config
::singleton();
1546 $config->userSystem
->updateCategories();
1551 * Get the UF Join records for an ufgroup id.
1553 * @param int $ufGroupId
1555 * @param int $displayName
1556 * If set return display name in array.
1557 * @param int $status
1558 * If set return module other than default modules (User Account/User registration/Profile).
1563 public static function getUFJoinRecord($ufGroupId = NULL, $displayName = NULL, $status = NULL) {
1566 $UFGroupType = CRM_Core_SelectValues
::ufGroupTypes();
1570 $dao = new CRM_Core_DAO_UFJoin();
1573 $dao->uf_group_id
= $ufGroupId;
1579 while ($dao->fetch()) {
1580 if (!$displayName) {
1581 $ufJoin[$dao->id
] = $dao->module
;
1584 if (isset($UFGroupType[$dao->module
])) {
1585 // skip the default modules
1587 $ufJoin[$dao->id
] = $UFGroupType[$dao->module
];
1589 // added for CRM-1475
1591 elseif (!CRM_Utils_Array
::key($dao->module
, $ufJoin)) {
1592 $ufJoin[$dao->id
] = $dao->module
;
1600 * Function takes an associative array and creates a ufjoin record for ufgroup.
1602 * @param array $params
1603 * (reference) an assoc array of name/value pairs.
1605 * @return CRM_Core_DAO_UFJoin
1607 public static function addUFJoin(&$params) {
1608 $ufJoin = new CRM_Core_DAO_UFJoin();
1609 $ufJoin->copyValues($params);
1615 * Delete the uf join record for an uf group.
1617 * @param array $params
1618 * (reference) an assoc array of name/value pairs.
1620 public static function delUFJoin(&$params) {
1621 $ufJoin = new CRM_Core_DAO_UFJoin();
1622 $ufJoin->copyValues($params);
1627 * Get the weight for ufjoin record.
1629 * @param int $ufGroupId
1630 * If $ufGroupId get update weight or add weight.
1633 * weight of the UFGroup
1635 public static function getWeight($ufGroupId = NULL) {
1636 //calculate the weight
1639 $queryString = "SELECT ( MAX(civicrm_uf_join.weight)+1) as new_weight
1640 FROM civicrm_uf_join
1641 WHERE module = 'User Registration' OR module = 'User Account' OR module = 'Profile'";
1644 $queryString = "SELECT MAX(civicrm_uf_join.weight) as new_weight
1645 FROM civicrm_uf_join
1646 WHERE civicrm_uf_join.uf_group_id = %1
1647 AND ( entity_id IS NULL OR entity_id <= 0 )";
1648 $p[1] = [$ufGroupId, 'Integer'];
1651 $dao = CRM_Core_DAO
::executeQuery($queryString, $p);
1653 return ($dao->new_weight
) ?
$dao->new_weight
: 1;
1657 * Get the uf group for a module.
1659 * @param string $moduleName
1662 * No to increment the weight.
1663 * @param bool $skipPermission
1665 * Which operation (view, edit, create, etc) to check permission for.
1666 * @param array|null $returnFields
1669 * array of ufgroups for a module
1671 public static function getModuleUFGroup($moduleName = NULL, $count = 0, $skipPermission = TRUE, $op = CRM_Core_Permission
::VIEW
, $returnFields = NULL) {
1672 $selectFields = ['id', 'title', 'created_id', 'is_active', 'is_reserved', 'group_type', 'description'];
1674 if (CRM_Core_BAO_SchemaHandler
::checkIfFieldExists('civicrm_uf_group', 'frontend_title')) {
1675 $selectFields[] = 'frontend_title';
1678 if (!empty($returnFields)) {
1679 $selectFields = array_merge($returnFields, array_diff($selectFields, $returnFields));
1682 $queryString = 'SELECT civicrm_uf_group.' . implode(', civicrm_uf_group.', $selectFields) . '
1683 FROM civicrm_uf_group
1684 LEFT JOIN civicrm_uf_join ON (civicrm_uf_group.id = uf_group_id)';
1687 $queryString .= ' AND civicrm_uf_group.is_active = 1
1688 WHERE civicrm_uf_join.module = %2';
1689 $p[2] = [$moduleName, 'String'];
1692 // add permissioning for profiles only if not registration
1693 if (!$skipPermission) {
1694 $permissionClause = CRM_Core_Permission
::ufGroupClause($op, 'civicrm_uf_group.');
1695 if (strpos($queryString, 'WHERE') !== FALSE) {
1696 $queryString .= " AND $permissionClause ";
1699 $queryString .= " $permissionClause ";
1703 $queryString .= ' ORDER BY civicrm_uf_join.weight, civicrm_uf_group.title';
1704 $dao = CRM_Core_DAO
::executeQuery($queryString, $p);
1707 while ($dao->fetch()) {
1708 //skip mix profiles in user Registration / User Account
1709 if (($moduleName === 'User Registration' ||
$moduleName === 'User Account') &&
1710 CRM_Core_BAO_UFField
::checkProfileType($dao->id
)
1714 foreach ($selectFields as $key => $field) {
1715 if ($field === 'id') {
1718 $ufGroups[$dao->id
][$field] = $dao->$field;
1722 // Allow other modules to alter/override the UFGroups.
1723 CRM_Utils_Hook
::buildUFGroupsForModule($moduleName, $ufGroups);
1729 * Filter ufgroups based on logged in user contact type.
1731 * @param int $ufGroupId
1732 * Uf group id (profile id).
1733 * @param int $contactID
1738 public static function filterUFGroups($ufGroupId, $contactID = NULL) {
1740 $session = CRM_Core_Session
::singleton();
1741 $contactID = $session->get('userID');
1745 //get the contact type
1746 $contactType = CRM_Contact_BAO_Contact
::getContactType($contactID);
1748 //match if exixting contact type is same as profile contact type
1749 $profileType = CRM_Core_BAO_UFField
::getProfileType($ufGroupId);
1751 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
1752 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
1754 //in some cases getBasicType() returns a cached array instead of string. Example: array ('sponsor' => 'organization')
1755 if (is_array($profileType)) {
1756 $profileType = array_shift($profileType);
1760 //allow special mix profiles for Contribution and Participant
1761 $specialProfiles = ['Contribution', 'Participant', 'Membership'];
1763 if (in_array($profileType, $specialProfiles)) {
1767 if (($contactType == $profileType) ||
$profileType == 'Contact') {
1776 * Add profile field to a form.
1778 * @param CRM_Core_Form $form
1779 * @param array $field
1783 * @param int $contactId
1784 * @param bool $online
1785 * @param string $usedFor
1786 * For building up prefixed fieldname for special cases (e.g. onBehalf, Honor).
1787 * @param int $rowNumber
1788 * @param string $prefix
1792 public static function buildProfile(
1802 $defaultValues = [];
1803 $fieldName = $field['name'];
1804 $title = $field['title'];
1805 $attributes = $field['attributes'];
1806 $rule = $field['rule'];
1807 $view = $field['is_view'];
1808 $required = ($mode == CRM_Profile_Form
::MODE_SEARCH
) ?
FALSE : $field['is_required'];
1809 $search = $mode == CRM_Profile_Form
::MODE_SEARCH
;
1810 $isShared = CRM_Utils_Array
::value('is_shared', $field, 0);
1812 // do not display view fields in drupal registration form
1814 if ($view && $mode == CRM_Profile_Form
::MODE_REGISTER
) {
1818 if ($usedFor == 'onbehalf') {
1819 $name = "onbehalf[$fieldName]";
1821 elseif ($usedFor == 'honor') {
1822 $name = "honor[$fieldName]";
1824 elseif ($contactId && !$online) {
1825 $name = "field[$contactId][$fieldName]";
1827 elseif ($rowNumber) {
1828 $name = "field[$rowNumber][$fieldName]";
1830 elseif (!empty($prefix)) {
1831 $name = $prefix . "[$fieldName]";
1837 $selectAttributes = ['class' => 'crm-select2', 'placeholder' => TRUE];
1839 if ($fieldName == 'image_URL' && $mode == CRM_Profile_Form
::MODE_EDIT
) {
1840 $deleteExtra = json_encode(ts('Are you sure you want to delete contact image.'));
1842 CRM_Core_Action
::DELETE
=> [
1843 'name' => ts('Delete Contact Image'),
1844 'url' => 'civicrm/contact/image',
1845 'qs' => 'reset=1&id=%%id%%&gid=%%gid%%&action=delete',
1846 'extra' => 'onclick = "' . htmlspecialchars("if (confirm($deleteExtra)) this.href+='&confirmed=1'; else return false;") . '"',
1849 $deleteURL = CRM_Core_Action
::formLink($deleteURL,
1850 CRM_Core_Action
::DELETE
,
1852 'id' => $form->get('id'),
1853 'gid' => $form->get('gid'),
1857 'contact.profileimage.delete',
1861 $form->assign('deleteURL', $deleteURL);
1863 $addressOptions = CRM_Core_BAO_Setting
::valueOptions(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
1864 'address_options', TRUE, NULL, TRUE
1867 if (substr($fieldName, 0, 14) === 'state_province') {
1868 $form->addChainSelect($name, ['label' => $title, 'required' => $required]);
1869 $config = CRM_Core_Config
::singleton();
1870 if (!in_array($mode, [CRM_Profile_Form
::MODE_EDIT
, CRM_Profile_Form
::MODE_SEARCH
]) &&
1871 $config->defaultContactStateProvince
1873 $defaultValues[$name] = $config->defaultContactStateProvince
;
1874 $form->setDefaults($defaultValues);
1877 elseif (substr($fieldName, 0, 7) === 'country') {
1878 $form->add('select', $name, $title, CRM_Core_PseudoConstant
::country(), $required, $selectAttributes);
1879 $config = CRM_Core_Config
::singleton();
1880 if (!in_array($mode, [CRM_Profile_Form
::MODE_EDIT
, CRM_Profile_Form
::MODE_SEARCH
]) &&
1881 $config->defaultContactCountry
1883 $defaultValues[$name] = $config->defaultContactCountry
;
1884 $form->setDefaults($defaultValues);
1887 elseif (substr($fieldName, 0, 6) === 'county') {
1888 if ($addressOptions['county']) {
1889 $form->addChainSelect($name, ['label' => $title, 'required' => $required]);
1892 elseif (substr($fieldName, 0, 9) === 'image_URL') {
1893 $form->add('file', $name, $title, $attributes, $required);
1894 $form->addUploadElement($name);
1896 elseif (substr($fieldName, 0, 2) === 'im') {
1897 $form->add('text', $name, $title, $attributes, $required);
1900 if (substr($name, -1) === ']') {
1901 $providerName = substr($name, 0, -1) . '-provider_id]';
1903 $form->add('select', $providerName, NULL,
1904 CRM_Core_PseudoConstant
::get('CRM_Core_DAO_IM', 'provider_id'), $required
1908 $form->add('select', $name . '-provider_id', $title,
1909 CRM_Core_PseudoConstant
::get('CRM_Core_DAO_IM', 'provider_id'), $required
1913 if ($view && $mode != CRM_Profile_Form
::MODE_SEARCH
) {
1914 $form->freeze($name . '-provider_id');
1918 elseif (CRM_Utils_Array
::value('name', $field) == 'membership_type') {
1919 list($orgInfo, $types) = CRM_Member_BAO_MembershipType
::getMembershipTypeInfo();
1920 $sel = &$form->addElement('hierselect', $name, $title);
1921 $select = ['' => ts('- select membership type -')];
1922 if (count($orgInfo) == 1 && $field['is_required']) {
1923 // we only have one org - so we should default to it. Not sure about defaulting to first type
1924 // as it could be missed - so adding a select
1925 // however, possibly that is more similar to the membership form
1926 if (count($types[1]) > 1) {
1927 $types[1] = $select +
$types[1];
1931 $orgInfo = $select +
$orgInfo;
1933 $sel->setOptions([$orgInfo, $types]);
1935 elseif (CRM_Utils_Array
::value('name', $field) == 'membership_status') {
1936 $form->add('select', $name, $title,
1937 CRM_Member_PseudoConstant
::membershipStatus(NULL, NULL, 'label'), $required
1940 elseif (in_array($fieldName, ['gender_id', 'communication_style_id'])) {
1942 $pseudoValues = CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', $fieldName);
1943 $form->addRadio($name, ts('%1', [1 => $title]), $pseudoValues, ['allowClear' => !$required], NULL, $required);
1945 elseif ($fieldName === 'prefix_id' ||
$fieldName === 'suffix_id') {
1946 $form->addSelect($name, [
1948 'entity' => 'contact',
1949 'field' => $fieldName,
1951 'placeholder' => '',
1954 elseif ($fieldName === 'contact_sub_type') {
1955 $gId = $form->get('gid') ?
$form->get('gid') : CRM_Utils_Array
::value('group_id', $field);
1956 if ($usedFor == 'onbehalf') {
1957 $profileType = 'Organization';
1959 elseif ($usedFor == 'honor') {
1960 $profileType = CRM_Core_BAO_UFField
::getProfileType($form->_params
['honoree_profile_id']);
1963 $profileType = $gId ? CRM_Core_BAO_UFField
::getProfileType($gId) : NULL;
1964 if ($profileType == 'Contact') {
1965 $profileType = 'Individual';
1969 $setSubtype = FALSE;
1970 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
1971 $setSubtype = $profileType;
1972 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
1975 $subtypes = $profileType ? CRM_Contact_BAO_ContactType
::subTypePairs($profileType) : [];
1979 $subtypeList[$setSubtype] = $subtypes[$setSubtype];
1982 $subtypeList = $subtypes;
1985 $form->add('select', $name, $title, $subtypeList, $required, ['class' => 'crm-select2', 'multiple' => TRUE]);
1987 elseif (in_array($fieldName, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
1988 // Get contact type for greeting selector
1989 $gId = $form->get('gid') ?
: CRM_Utils_Array
::value('group_id', $field);
1990 $profileType = CRM_Core_BAO_UFField
::getProfileType($gId, TRUE, FALSE, TRUE);
1992 if (!$profileType ||
in_array($profileType, ['Contact', 'Contribution', 'Participant', 'Membership'])) {
1993 $profileType = ($profileType == 'Contact' && $form->get('id')) ? CRM_Contact_BAO_Contact
::getContactType($form->get('id')) : 'Individual';
1995 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
1996 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
1999 'contact_type' => $profileType,
2000 'greeting_type' => $fieldName,
2002 $form->add('select', $name, $title, CRM_Core_PseudoConstant
::greeting($greeting), $required, ['placeholder' => TRUE]);
2003 // add custom greeting element
2004 $form->add('text', $fieldName . '_custom', ts('Custom %1', [1 => ucwords(str_replace('_', ' ', $fieldName))]),
2008 elseif ($fieldName === 'preferred_communication_method') {
2009 $communicationFields = CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'preferred_communication_method');
2010 foreach ($communicationFields as $key => $var) {
2014 $communicationOptions[] = $form->createElement('checkbox', $key, NULL, $var);
2016 $form->addGroup($communicationOptions, $name, $title, '<br/>');
2018 elseif ($fieldName === 'preferred_mail_format') {
2019 $form->add('select', $name, $title, CRM_Core_SelectValues
::pmf());
2021 elseif ($fieldName === 'preferred_language') {
2022 $form->add('select', $name, $title, CRM_Contact_BAO_Contact
::buildOptions('preferred_language'), $required, ['placeholder' => TRUE]);
2024 elseif ($fieldName == 'external_identifier') {
2025 $form->add('text', $name, $title, $attributes, $required);
2026 $contID = $contactId;
2028 $contID = $form->get('id');
2030 $form->addRule($name,
2031 ts('External ID already exists in Database.'),
2033 ['CRM_Contact_DAO_Contact', $contID, 'external_identifier']
2036 elseif ($fieldName === 'group') {
2037 CRM_Contact_Form_Edit_TagsAndGroups
::buildQuickForm($form, $contactId,
2038 CRM_Contact_Form_Edit_TagsAndGroups
::GROUP
,
2040 $title, NULL, $name, 'checkbox', TRUE
2043 elseif ($fieldName === 'tag') {
2044 CRM_Contact_Form_Edit_TagsAndGroups
::buildQuickForm($form, $contactId,
2045 CRM_Contact_Form_Edit_TagsAndGroups
::TAG
,
2050 elseif (substr($fieldName, 0, 4) === 'url-') {
2051 $form->add('text', $name, $title, CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Website', 'url'), $required);
2052 $form->addRule($name, ts('Enter a valid web address beginning with \'http://\' or \'https://\'.'), 'url');
2054 // Note should be rendered as textarea
2055 elseif (substr($fieldName, -4) == 'note') {
2056 $form->add('textarea', $name, $title, $attributes, $required);
2058 elseif (substr($fieldName, 0, 6) === 'custom') {
2059 $customFieldID = CRM_Core_BAO_CustomField
::getKeyID($fieldName);
2060 if ($customFieldID) {
2061 CRM_Core_BAO_CustomField
::addQuickFormElement($form, $name, $customFieldID, $required, $search, $title);
2064 elseif (substr($fieldName, 0, 14) === 'address_custom') {
2065 list($fName, $locTypeId) = CRM_Utils_System
::explode('-', $fieldName, 2);
2066 $customFieldID = CRM_Core_BAO_CustomField
::getKeyID(substr($fName, 8));
2067 if ($customFieldID) {
2068 CRM_Core_BAO_CustomField
::addQuickFormElement($form, $name, $customFieldID, $required, $search, $title);
2071 elseif ($fieldName == 'send_receipt') {
2072 $form->addElement('checkbox', $name, $title);
2074 elseif ($fieldName == 'soft_credit') {
2075 $form->addEntityRef("soft_credit_contact_id[$rowNumber]", ts('Soft Credit To'), ['create' => TRUE]);
2076 $form->addMoney("soft_credit_amount[{$rowNumber}]", ts('Amount'), FALSE, NULL, FALSE);
2078 elseif ($fieldName === 'product_name') {
2079 list($products, $options) = CRM_Contribute_BAO_Premium
::getPremiumProductInfo();
2080 $sel = &$form->addElement('hierselect', $name, $title);
2081 $products = ['0' => ts('- select %1 -', [1 => $title])] +
$products;
2082 $sel->setOptions([$products, $options]);
2084 elseif ($fieldName === 'payment_instrument') {
2085 $form->add('select', $name, $title,
2086 CRM_Contribute_PseudoConstant
::paymentInstrument(), $required, ['placeholder' => TRUE]);
2088 elseif ($fieldName === 'financial_type') {
2089 $form->add('select', $name, $title,
2090 CRM_Contribute_PseudoConstant
::financialType(), $required, ['placeholder' => TRUE]
2093 elseif ($fieldName === 'contribution_status_id') {
2094 $contributionStatuses = CRM_Contribute_BAO_Contribution_Utils
::getPendingCompleteFailedAndCancelledStatuses();
2096 $form->add('select', $name, $title,
2097 $contributionStatuses, $required, ['placeholder' => TRUE]
2100 elseif ($fieldName === 'soft_credit_type') {
2101 $name = "soft_credit_type[$rowNumber]";
2102 $form->add('select', $name, $title,
2103 CRM_Core_OptionGroup
::values("soft_credit_type"), ['placeholder' => TRUE]
2105 //CRM-15350: choose SCT field default value as 'Gift' for membership use
2106 //else (for contribution), use configured SCT default value
2107 $SCTDefaultValue = CRM_Core_OptionGroup
::getDefaultValue("soft_credit_type");
2108 if ($field['field_type'] == 'Membership') {
2109 $SCTDefaultValue = CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_ContributionSoft', 'soft_credit_type_id', 'gift');
2111 $form->addElement('hidden', 'sct_default_id', $SCTDefaultValue, ['id' => 'sct_default_id']);
2113 elseif ($fieldName == 'contribution_soft_credit_pcp_id') {
2114 CRM_Contribute_Form_SoftCredit
::addPCPFields($form, "[$rowNumber]");
2116 elseif ($fieldName == 'currency') {
2117 $form->addCurrency($name, $title, $required, NULL, FALSE, FALSE);
2119 elseif ($fieldName == 'contribution_page_id') {
2120 $form->add('select', $name, $title,
2121 CRM_Contribute_PseudoConstant
::contributionPage(), $required, [
2123 'placeholder' => TRUE,
2127 elseif ($fieldName == 'activity_status_id') {
2128 $form->add('select', $name, $title,
2129 CRM_Core_PseudoConstant
::activityStatus(), $required, ['placeholder' => TRUE]
2132 elseif ($fieldName == 'activity_engagement_level') {
2133 $form->add('select', $name, $title,
2134 CRM_Campaign_PseudoConstant
::engagementLevel(), $required, ['placeholder' => TRUE]
2137 elseif ($fieldName == 'participant_status') {
2139 if ($online == TRUE) {
2140 $cond = 'visibility_id = 1';
2142 $form->add('select', $name, $title,
2143 CRM_Event_PseudoConstant
::participantStatus(NULL, $cond, 'label'), $required, ['placeholder' => TRUE]
2146 elseif ($fieldName == 'participant_role') {
2147 if (!empty($field['is_multiple'])) {
2148 $form->addCheckBox($name, $title, CRM_Event_PseudoConstant
::participantRole(), NULL, NULL, NULL, NULL, ' ', TRUE);
2151 $form->add('select', $name, $title,
2152 CRM_Event_PseudoConstant
::participantRole(), $required, ['placeholder' => TRUE]
2156 elseif ($fieldName == 'world_region') {
2157 $form->add('select', $name, $title, CRM_Core_PseudoConstant
::worldRegion(), $required, $selectAttributes);
2159 elseif ($fieldName == 'signature_html') {
2160 $form->add('wysiwyg', $name, $title, CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Email', $fieldName));
2162 elseif ($fieldName == 'signature_text') {
2163 $form->add('textarea', $name, $title, CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Email', $fieldName));
2165 elseif (substr($fieldName, -11) == 'campaign_id') {
2166 if (CRM_Campaign_BAO_Campaign
::isCampaignEnable()) {
2167 $campaigns = CRM_Campaign_BAO_Campaign
::getCampaigns(CRM_Utils_Array
::value($contactId,
2168 $form->_componentCampaigns
2170 $form->add('select', $name, $title,
2171 $campaigns, $required,
2173 'class' => 'crm-select2 big',
2174 'placeholder' => TRUE,
2179 elseif ($fieldName == 'activity_details') {
2180 $form->add('wysiwyg', $name, $title, ['rows' => 4, 'cols' => 60], $required);
2182 elseif ($fieldName == 'activity_duration') {
2183 $form->add('text', $name, $title, $attributes, $required);
2184 $form->addRule($name, ts('Please enter the duration as number of minutes (integers only).'), 'positiveInteger');
2186 elseif ($fieldName == 'case_status') {
2187 $form->add('select', $name, $title,
2188 CRM_Case_BAO_Case
::buildOptions('case_status_id', 'create'),
2189 $required, ['placeholder' => TRUE]
2193 if (substr($fieldName, 0, 3) === 'is_' or substr($fieldName, 0, 7) === 'do_not_') {
2194 $form->add('advcheckbox', $name, $title, $attributes, $required);
2196 elseif (CRM_Utils_Array
::value('html_type', $field) === 'Select Date') {
2197 $extra = isset($field['datepicker']) ?
$field['datepicker']['extra'] : CRM_Utils_Date
::getDatePickerExtra($field);
2198 $attributes = isset($field['datepicker']) ?
$field['datepicker']['attributes'] : CRM_Utils_Date
::getDatePickerAttributes($field);
2199 $form->add('datepicker', $name, $title, $attributes, $required, $extra);
2202 $form->add('text', $name, $title, $attributes, $required);
2206 static $hiddenSubtype = FALSE;
2207 if (!$hiddenSubtype && CRM_Contact_BAO_ContactType
::isaSubType($field['field_type'])) {
2208 // In registration mode params are submitted via POST and we don't have any clue
2209 // about profile-id or the profile-type (which could be a subtype)
2210 // To generalize the behavior and simplify the process,
2211 // lets always add the hidden
2212 //subtype value if there is any, and we won't have to
2213 // compute it while processing.
2215 $form->addElement('hidden', $usedFor . '[contact_sub_type]', $field['field_type']);
2218 $form->addElement('hidden', 'contact_sub_type_hidden', $field['field_type']);
2220 $hiddenSubtype = TRUE;
2223 if (($view && $mode != CRM_Profile_Form
::MODE_SEARCH
) ||
$isShared) {
2224 $form->freeze($name);
2228 if (in_array($fieldName, [
2229 'non_deductible_amount',
2234 $form->addRule($name, ts('Please enter a valid amount.'), 'money');
2237 if (!($rule == 'email' && $mode == CRM_Profile_Form
::MODE_SEARCH
)) {
2238 $form->addRule($name, ts('Please enter a valid %1', [1 => $title]), $rule);
2244 * Set profile defaults.
2246 * @param int $contactId
2248 * @param array $fields
2249 * Associative array of fields.
2250 * @param array $defaults
2252 * @param bool $singleProfile
2253 * True for single profile else false(Update multiple items).
2254 * @param int $componentId
2255 * Id for specific components like contribute, event etc.
2256 * @param null $component
2258 public static function setProfileDefaults(
2259 $contactId, &$fields, &$defaults,
2260 $singleProfile = TRUE, $componentId = NULL, $component = NULL
2262 if (!$componentId) {
2263 //get the contact details
2264 $contactDetails = CRM_Contact_BAO_Contact
::getHierContactDetails($contactId, $fields);
2265 $details = $contactDetails[$contactId] ??
NULL;
2266 $multipleFields = ['website' => 'url'];
2268 //start of code to set the default values
2269 foreach ($fields as $name => $field) {
2270 // skip pseudo fields
2271 if (substr($name, 0, 9) == 'phone_ext') {
2275 //set the field name depending upon the profile mode(single/multiple)
2276 if ($singleProfile) {
2280 $fldName = "field[$contactId][$name]";
2283 if ($name == 'group') {
2284 CRM_Contact_Form_Edit_TagsAndGroups
::setDefaults($contactId, $defaults, CRM_Contact_Form_Edit_TagsAndGroups
::GROUP
, $fldName);
2286 if ($name == 'tag') {
2287 CRM_Contact_Form_Edit_TagsAndGroups
::setDefaults($contactId, $defaults, CRM_Contact_Form_Edit_TagsAndGroups
::TAG
, $fldName);
2290 if (!empty($details[$name]) ||
isset($details[$name])) {
2291 //to handle custom data (checkbox) to be written
2292 // to handle birth/deceased date, greeting_type and few other fields
2293 if (in_array($name, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
2294 $defaults[$fldName] = $details[$name . '_id'];
2295 $defaults[$name . '_custom'] = $details[$name . '_custom'];
2297 elseif ($name == 'preferred_communication_method') {
2298 $v = $details[$name];
2299 if (!is_array($details[$name])) {
2300 $v = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $v);
2302 foreach ($v as $item) {
2304 $defaults[$fldName . "[$item]"] = 1;
2308 elseif ($name == 'contact_sub_type') {
2309 $defaults[$fldName] = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, trim($details[$name], CRM_Core_DAO
::VALUE_SEPARATOR
));
2311 elseif ($name == 'world_region') {
2312 $defaults[$fldName] = $details['worldregion_id'];
2314 elseif (CRM_Core_BAO_CustomField
::getKeyID($name)) {
2315 $defaults[$fldName] = self
::formatCustomValue($field, $details[$name]);
2316 if (!$singleProfile && $field['html_type'] === 'CheckBox') {
2317 // For batch update profile there needs to be a key lik
2318 // $defaults['field[166]['custom_8'][2]'] => 1 where
2319 // 166 is the conntact id, 8 is the field id and 2 is the checkbox option.
2320 foreach ($defaults[$fldName] as $itemKey => $itemValue) {
2321 $defaults[$fldName . '[' . $itemKey . ']'] = $itemValue;
2326 $defaults[$fldName] = $details[$name];
2330 $blocks = ['email', 'phone', 'im', 'openid'];
2331 list($fieldName, $locTypeId, $phoneTypeId) = CRM_Utils_System
::explode('-', $name, 3);
2332 if (!in_array($fieldName, $multipleFields)) {
2333 if (is_array($details)) {
2334 foreach ($details as $key => $value) {
2335 // when we fixed CRM-5319 - get primary loc
2336 // type as per loc field and removed below code.
2337 $primaryLocationType = FALSE;
2338 if ($locTypeId == 'Primary') {
2339 if (is_array($value) && array_key_exists($fieldName, $value)) {
2340 $primaryLocationType = TRUE;
2341 if (in_array($fieldName, $blocks)) {
2342 $locTypeId = CRM_Contact_BAO_Contact
::getPrimaryLocationType($contactId, FALSE, $fieldName);
2345 $locTypeId = CRM_Contact_BAO_Contact
::getPrimaryLocationType($contactId, FALSE, 'address');
2350 // fixed for CRM-665
2351 if (is_numeric($locTypeId)) {
2352 if ($primaryLocationType ||
$locTypeId == CRM_Utils_Array
::value('location_type_id', $value)) {
2353 if (!empty($value[$fieldName])) {
2354 //to handle stateprovince and country
2355 if ($fieldName == 'state_province') {
2356 $defaults[$fldName] = $value['state_province_id'];
2358 elseif ($fieldName == 'county') {
2359 $defaults[$fldName] = $value['county_id'];
2361 elseif ($fieldName == 'country') {
2362 if (!isset($value['country_id']) ||
!$value['country_id']) {
2363 $config = CRM_Core_Config
::singleton();
2364 if ($config->defaultContactCountry
) {
2365 $defaults[$fldName] = $config->defaultContactCountry
;
2369 $defaults[$fldName] = $value['country_id'];
2372 elseif ($fieldName == 'phone') {
2374 if (isset($value['phone'][$phoneTypeId])) {
2375 $defaults[$fldName] = $value['phone'][$phoneTypeId];
2377 if (isset($value['phone_ext'][$phoneTypeId])) {
2378 $defaults[str_replace('phone', 'phone_ext', $fldName)] = $value['phone_ext'][$phoneTypeId];
2382 $phoneDefault = $value['phone'] ??
NULL;
2384 if (!is_array($phoneDefault)) {
2385 $defaults[$fldName] = $phoneDefault;
2389 elseif ($fieldName == 'email') {
2390 //adding the first email (currently we don't support multiple emails of same location type)
2391 $defaults[$fldName] = $value['email'];
2393 elseif ($fieldName == 'im') {
2394 //adding the first im (currently we don't support multiple ims of same location type)
2395 $defaults[$fldName] = $value['im'];
2396 $defaults[$fldName . '-provider_id'] = $value['im_provider_id'];
2399 $defaults[$fldName] = $value[$fieldName];
2402 elseif (strpos($fieldName, 'address_custom') === 0 && !empty($value[substr($fieldName, 8)])) {
2403 $defaults[$fldName] = self
::formatCustomValue($field, $value[substr($fieldName, 8)]);
2407 elseif (strpos($fieldName, 'address_custom') === 0 && !empty($value[substr($fieldName, 8)])) {
2408 $defaults[$fldName] = self
::formatCustomValue($field, $value[substr($fieldName, 8)]);
2414 if (is_array($details)) {
2415 if ($fieldName === 'url'
2416 && !empty($details['website'])
2417 && !empty($details['website'][$locTypeId])
2419 $defaults[$fldName] = $details['website'][$locTypeId]['url'] ??
NULL;
2427 // Handling Contribution Part of the batch profile
2428 if (CRM_Core_Permission
::access('CiviContribute') && $component == 'Contribute') {
2429 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2432 // Handling Event Participation Part of the batch profile
2433 if (CRM_Core_Permission
::access('CiviEvent') && $component == 'Event') {
2434 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2437 // Handling membership Part of the batch profile
2438 if (CRM_Core_Permission
::access('CiviMember') && $component == 'Membership') {
2439 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2442 // Handling Activity Part of the batch profile
2443 if ($component == 'Activity') {
2444 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2447 // Handling Case Part of the batch profile
2448 if (CRM_Core_Permission
::access('CiviCase') && $component == 'Case') {
2449 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2454 * Get profiles by type eg: pure Individual etc
2456 * @param array $types
2457 * Associative array of types eg: types('Individual').
2458 * @param bool $onlyPure
2459 * True if only pure profiles are required.
2462 * associative array of profiles
2464 public static function getProfiles($types, $onlyPure = FALSE) {
2466 $ufGroups = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_UFField', 'uf_group_id');
2468 CRM_Utils_Hook
::aclGroup(CRM_Core_Permission
::ADMIN
, NULL, 'civicrm_uf_group', $ufGroups, $ufGroups);
2470 // Exclude Batch Data Entry profiles - CRM-10901
2471 $batchProfiles = CRM_Core_BAO_UFGroup
::getBatchProfiles();
2473 foreach ($ufGroups as $id => $title) {
2474 $ptype = CRM_Core_BAO_UFField
::getProfileType($id, FALSE, $onlyPure);
2475 if (in_array($ptype, $types) && !array_key_exists($id, $batchProfiles)) {
2476 $profiles[$id] = $title;
2483 * Check whether a profile is valid combination of
2484 * required and/or optional profile types
2486 * @param array $required
2487 * Array of types those are required.
2488 * @param array $optional
2489 * Array of types those are optional.
2492 * associative array of profiles
2494 public static function getValidProfiles($required, $optional = NULL) {
2495 if (!is_array($required) ||
empty($required)) {
2500 $ufGroups = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_UFField', 'uf_group_id');
2502 CRM_Utils_Hook
::aclGroup(CRM_Core_Permission
::ADMIN
, NULL, 'civicrm_uf_group', $ufGroups, $ufGroups);
2504 foreach ($ufGroups as $id => $title) {
2505 $type = CRM_Core_BAO_UFField
::checkValidProfileType($id, $required, $optional);
2507 $profiles[$id] = $title;
2515 * Check whether a profile is valid combination of
2516 * required profile fields
2518 * @param array $ufId
2519 * Integer id of the profile.
2520 * @param array $required
2521 * Array of fields those are required in the profile.
2524 * associative array of profiles
2526 public static function checkValidProfile($ufId, $required = NULL) {
2527 $validProfile = FALSE;
2529 return $validProfile;
2532 if (!CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $ufId, 'is_active')) {
2533 return $validProfile;
2536 $profileFields = self
::getFields($ufId, FALSE, CRM_Core_Action
::VIEW
, NULL,
2537 NULL, FALSE, NULL, FALSE, NULL,
2538 CRM_Core_Permission
::CREATE
, NULL
2542 if (!empty($profileFields)) {
2543 $fields = array_keys($profileFields);
2544 foreach ($fields as $val) {
2545 foreach ($required as $key => $field) {
2546 if (strpos($val, $field) === 0) {
2547 unset($required[$key]);
2552 $validProfile = (empty($required));
2555 return $validProfile;
2559 * Get default value for Register.
2561 * @param array $fields
2562 * @param array $defaults
2566 public static function setRegisterDefaults(&$fields, &$defaults) {
2567 $config = CRM_Core_Config
::singleton();
2568 foreach ($fields as $name => $field) {
2569 if (substr($name, 0, 8) == 'country-') {
2570 if (!empty($config->defaultContactCountry
)) {
2571 $defaults[$name] = $config->defaultContactCountry
;
2574 elseif (substr($name, 0, 15) == 'state_province-') {
2575 if (!empty($config->defaultContactStateProvince
)) {
2576 $defaults[$name] = $config->defaultContactStateProvince
;
2584 * make a copy of a profile, including
2585 * all the fields in the profile
2588 * The profile id to copy.
2590 * @return \CRM_Core_DAO
2592 public static function copy($id) {
2593 $maxId = CRM_Core_DAO
::singleValueQuery("SELECT max(id) FROM civicrm_uf_group");
2595 $title = ts('[Copy id %1]', [1 => $maxId +
1]);
2598 'title' => ' ' . $title,
2599 'name' => '__Copy_id_' . ($maxId +
1) . '_',
2603 $copy = CRM_Core_DAO
::copyGeneric('CRM_Core_DAO_UFGroup',
2609 if ($pos = strrpos($copy->name
, "_{$id}")) {
2610 $copy->name
= substr_replace($copy->name
, '', $pos);
2612 $copy->name
= CRM_Utils_String
::munge($copy->name
, '_', 56) . "_{$copy->id}";
2615 $copyUFJoin = CRM_Core_DAO
::copyGeneric('CRM_Core_DAO_UFJoin',
2616 ['uf_group_id' => $id],
2617 ['uf_group_id' => $copy->id
],
2622 $copyUFField = CRM_Core_DAO
::copyGeneric('CRM_Core_BAO_UFField',
2623 ['uf_group_id' => $id],
2624 ['uf_group_id' => $copy->id
]
2627 $maxWeight = CRM_Utils_Weight
::getMax('CRM_Core_DAO_UFJoin', NULL, 'weight');
2631 UPDATE civicrm_uf_join
2633 WHERE uf_group_id = %2
2634 AND ( entity_id IS NULL OR entity_id <= 0 )
2637 1 => [$maxWeight +
1, 'Integer'],
2638 2 => [$copy->id
, 'Integer'],
2640 CRM_Core_DAO
::executeQuery($query, $p);
2641 if ($copy->is_reserved
) {
2642 $query = "UPDATE civicrm_uf_group SET is_reserved = 0 WHERE id = %1";
2643 $params = [1 => [$copy->id
, 'Integer']];
2644 CRM_Core_DAO
::executeQuery($query, $params);
2646 CRM_Utils_Hook
::copy('UFGroup', $copy);
2652 * Process that send notification e-mails
2654 * @param int $contactID
2656 * @param array $values
2657 * Associative array of name/value pair.
2659 public static function commonSendMail($contactID, &$values) {
2660 if (!$contactID ||
!$values) {
2664 $template = CRM_Core_Smarty
::singleton();
2666 $displayName = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
2671 self
::profileDisplay($values['id'], $values['values'], $template);
2672 $emailList = explode(',', $values['email']);
2674 $contactLink = CRM_Utils_System
::url('civicrm/contact/view',
2675 "reset=1&cid=$contactID",
2676 TRUE, NULL, FALSE, FALSE, TRUE
2679 //get the default domain email address.
2680 list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain
::getNameAndEmail();
2682 if (!$domainEmailAddress ||
$domainEmailAddress == 'info@EXAMPLE.ORG') {
2683 $fixUrl = CRM_Utils_System
::url('civicrm/admin/domain', 'action=update&reset=1');
2684 CRM_Core_Error
::statusBounce(ts('The site administrator needs to enter a valid \'FROM Email Address\' in <a href="%1">Administer CiviCRM » Communications » FROM Email Addresses</a>. The email address used may need to be a valid mail account with your email service provider.', [1 => $fixUrl]));
2687 foreach ($emailList as $emailTo) {
2688 // FIXME: take the below out of the foreach loop
2689 CRM_Core_BAO_MessageTemplate
::sendTemplate(
2691 'groupName' => 'msg_tpl_workflow_uf',
2692 'valueName' => 'uf_notify',
2693 'contactId' => $contactID,
2695 'displayName' => $displayName,
2696 'currentDate' => date('r'),
2697 'contactLink' => $contactLink,
2699 'from' => "$domainEmailName <$domainEmailAddress>",
2700 'toEmail' => $emailTo,
2707 * Given a contact id and a group id, returns the field values from the db
2708 * for this group and notify email only if group's notify field is
2709 * set and field values are not empty
2715 * @param array $params
2716 * @param bool $skipCheck
2720 public function checkFieldsEmptyValues($gid, $cid, $params, $skipCheck = FALSE) {
2722 if (CRM_Core_BAO_UFGroup
::filterUFGroups($gid, $cid) ||
$skipCheck) {
2724 $fields = CRM_Core_BAO_UFGroup
::getFields($gid, FALSE, CRM_Core_Action
::VIEW
);
2725 CRM_Core_BAO_UFGroup
::getValues($cid, $fields, $values, FALSE, $params, TRUE);
2727 $email = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'notify');
2729 if (!empty($values) &&
2734 'values' => $values,
2745 * Assign uf fields to template.
2749 * @param array $values
2750 * @param CRM_Core_Smarty $template
2752 public static function profileDisplay($gid, $values, $template) {
2753 $groupTitle = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'title');
2754 $template->assign('grouptitle', $groupTitle);
2755 if (count($values)) {
2756 $template->assign('values', $values);
2761 * Format fields for dupe Contact Matching.
2763 * @param array $params
2765 * @param int $contactId
2768 * associated formatted array
2770 public static function formatFields($params, $contactId = NULL) {
2772 // get the primary location type id and email
2773 list($name, $primaryEmail, $primaryLocationType) = CRM_Contact_BAO_Contact_Location
::getEmailDetails($contactId);
2776 $defaultLocationType = CRM_Core_BAO_LocationType
::getDefault();
2777 $primaryLocationType = $defaultLocationType->id
;
2783 $primaryLocation = 0;
2784 foreach ($params as $key => $value) {
2785 list($fieldName, $locTypeId, $phoneTypeId) = explode('-', $key);
2787 if ($locTypeId == 'Primary') {
2788 $locTypeId = $primaryLocationType;
2791 if (is_numeric($locTypeId)) {
2792 if (!in_array($locTypeId, $locationType)) {
2793 $locationType[$count] = $locTypeId;
2796 $loc = CRM_Utils_Array
::key($locTypeId, $locationType);
2798 $data['location'][$loc]['location_type_id'] = $locTypeId;
2800 // if we are getting in a new primary email, dont overwrite the new one
2801 if ($locTypeId == $primaryLocationType) {
2802 if (!empty($params['email-' . $primaryLocationType])) {
2803 $data['location'][$loc]['email'][$loc]['email'] = $fields['email-' . $primaryLocationType];
2805 elseif (isset($primaryEmail)) {
2806 $data['location'][$loc]['email'][$loc]['email'] = $primaryEmail;
2812 $data['location'][$loc]['is_primary'] = 1;
2814 if ($fieldName == 'phone') {
2816 $data['location'][$loc]['phone'][$loc]['phone_type_id'] = $phoneTypeId;
2819 $data['location'][$loc]['phone'][$loc]['phone_type_id'] = '';
2821 $data['location'][$loc]['phone'][$loc]['phone'] = $value;
2823 elseif ($fieldName == 'email') {
2824 $data['location'][$loc]['email'][$loc]['email'] = $value;
2826 elseif ($fieldName == 'im') {
2827 $data['location'][$loc]['im'][$loc]['name'] = $value;
2830 if ($fieldName === 'state_province') {
2831 $data['location'][$loc]['address']['state_province_id'] = $value;
2833 elseif ($fieldName === 'country') {
2834 $data['location'][$loc]['address']['country_id'] = $value;
2837 $data['location'][$loc]['address'][$fieldName] = $value;
2842 // TODO: prefix, suffix and gender translation may no longer be necessary - check inputs
2843 if ($key === 'individual_suffix') {
2844 $data['suffix_id'] = $value;
2846 elseif ($key === 'individual_prefix') {
2847 $data['prefix_id'] = $value;
2849 elseif ($key === 'gender') {
2850 $data['gender_id'] = $value;
2852 elseif (substr($key, 0, 6) === 'custom') {
2853 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) {
2855 if ($customFields[$customFieldID]['html_type'] == 'CheckBox') {
2856 $value = implode(CRM_Core_DAO
::VALUE_SEPARATOR
, array_keys($value));
2858 // fix the date field
2859 if ($customFields[$customFieldID]['data_type'] == 'Date') {
2860 $date = CRM_Utils_Date
::format($value);
2867 $data['custom'][$customFieldID] = [
2870 'extends' => $customFields[$customFieldID]['extends'],
2871 'type' => $customFields[$customFieldID]['data_type'],
2872 'custom_field_id' => $customFieldID,
2876 elseif ($key == 'edit') {
2880 $data[$key] = $value;
2885 if (!$primaryLocation) {
2887 $data['location'][$loc]['email'][$loc]['email'] = $primaryEmail;
2894 * Calculate the profile type 'group_type' as per profile fields.
2898 * @param bool $includeTypeValues
2899 * @param int $ignoreFieldId
2900 * Ignore particular profile field.
2903 * list of calculated group type
2905 public static function calculateGroupType($gId, $includeTypeValues = FALSE, $ignoreFieldId = NULL) {
2906 //get the profile fields.
2907 $ufFields = self
::getFields($gId, FALSE, NULL, NULL, NULL, TRUE, NULL, TRUE);
2908 return self
::_calculateGroupType($ufFields, $includeTypeValues, $ignoreFieldId);
2912 * Calculate the profile type 'group_type' as per profile fields.
2915 * @param bool $includeTypeValues
2916 * @param int $ignoreFieldId
2917 * Ignore perticular profile field.
2920 * list of calculated group type
2922 public static function _calculateGroupType($ufFields, $includeTypeValues = FALSE, $ignoreFieldId = NULL) {
2923 $groupType = $groupTypeValues = $customFieldIds = [];
2924 if (!empty($ufFields)) {
2925 foreach ($ufFields as $fieldName => $fieldValue) {
2926 //ignore field from group type when provided.
2927 //in case of update profile field.
2928 if ($ignoreFieldId && ($ignoreFieldId == $fieldValue['field_id'])) {
2931 if (!in_array($fieldValue['field_type'], $groupType)) {
2932 $groupType[$fieldValue['field_type']] = $fieldValue['field_type'];
2935 if ($includeTypeValues && ($fldId = CRM_Core_BAO_CustomField
::getKeyID($fieldName))) {
2936 $customFieldIds[$fldId] = $fldId;
2941 if (!empty($customFieldIds)) {
2942 $query = 'SELECT DISTINCT(cg.id), cg.extends, cg.extends_entity_column_id, cg.extends_entity_column_value FROM civicrm_custom_group cg LEFT JOIN civicrm_custom_field cf ON cf.custom_group_id = cg.id WHERE cg.extends_entity_column_value IS NOT NULL AND cf.id IN (' . implode(',', $customFieldIds) . ')';
2944 $customGroups = CRM_Core_DAO
::executeQuery($query);
2945 while ($customGroups->fetch()) {
2946 if (!$customGroups->extends_entity_column_value
) {
2950 $groupTypeName = "{$customGroups->extends}Type";
2951 if ($customGroups->extends == 'Participant' && $customGroups->extends_entity_column_id
) {
2952 $groupTypeName = CRM_Core_PseudoConstant
::getName('CRM_Core_DAO_CustomGroup', 'extends_entity_column_id', $customGroups->extends_entity_column_id
);
2955 foreach (explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $customGroups->extends_entity_column_value
) as $val) {
2957 $groupTypeValues[$groupTypeName][$val] = $val;
2962 if (!empty($groupTypeValues)) {
2963 $groupType = array_merge($groupType, $groupTypeValues);
2971 * Update the profile type 'group_type' as per profile fields including group types and group subtype values.
2972 * Build and store string like: group_type1,group_type2[VALUE_SEPARATOR]group_type1Type:1:2:3,group_type2Type:1:2
2975 * BirthDate + Email Individual,Contact
2976 * BirthDate + Subject Individual,Activity
2977 * BirthDate + Subject + SurveyOnlyField Individual,Activity\0ActivityType:28
2978 * BirthDate + Subject + SurveyOnlyField + PhoneOnlyField (Not allowed)
2979 * BirthDate + SurveyOnlyField Individual,Activity\0ActivityType:28
2980 * BirthDate + Subject + SurveyOrPhoneField Individual,Activity\0ActivityType:2:28
2981 * BirthDate + SurveyOrPhoneField Individual,Activity\0ActivityType:2:28
2982 * BirthDate + SurveyOrPhoneField + SurveyOnlyField Individual,Activity\0ActivityType:2:28
2983 * BirthDate + StudentField + Subject + SurveyOnlyField Individual,Activity,Student\0ActivityType:28
2986 * @param array $groupTypes
2987 * With key having group type names.
2991 public static function updateGroupTypes($gId, $groupTypes = []) {
2992 if (!is_array($groupTypes) ||
!$gId) {
2996 // If empty group types set group_type as 'null'
2997 if (empty($groupTypes)) {
2998 return CRM_Core_DAO
::setFieldValue('CRM_Core_DAO_UFGroup', $gId, 'group_type', 'null');
3001 $componentGroupTypes = ['Contribution', 'Participant', 'Membership', 'Activity', 'Case'];
3002 $validGroupTypes = array_merge([
3007 ], $componentGroupTypes, CRM_Contact_BAO_ContactType
::subTypes());
3009 $gTypes = $gTypeValues = [];
3011 $participantExtends = ['ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
3012 // Get valid group type and group subtypes
3013 foreach ($groupTypes as $groupType => $value) {
3014 if (in_array($groupType, $validGroupTypes) && !in_array($groupType, $gTypes)) {
3015 $gTypes[] = $groupType;
3020 if (in_array($groupType, $participantExtends)) {
3021 $subTypesOf = $groupType;
3023 elseif (strpos($groupType, 'Type') > 0) {
3024 $subTypesOf = substr($groupType, 0, strpos($groupType, 'Type'));
3030 if (!empty($value) &&
3031 (in_array($subTypesOf, $componentGroupTypes) ||
3032 in_array($subTypesOf, $participantExtends)
3035 $gTypeValues[$subTypesOf] = $groupType . ":" . implode(':', $value);
3039 if (empty($gTypes)) {
3043 // Build String to store group types and group subtypes
3044 $groupTypeString = implode(',', $gTypes);
3045 if (!empty($gTypeValues)) {
3046 $groupTypeString .= CRM_Core_DAO
::VALUE_SEPARATOR
. implode(',', $gTypeValues);
3049 return CRM_Core_DAO
::setFieldValue('CRM_Core_DAO_UFGroup', $gId, 'group_type', $groupTypeString);
3053 * Create a "group_type" string.
3055 * @param array $coreTypes
3056 * E.g. array('Individual','Contact','Student').
3057 * @param array $subTypes
3058 * E.g. array('ActivityType' => array(7, 11)).
3059 * @param string $delim
3062 * @throws CRM_Core_Exception
3064 public static function encodeGroupType($coreTypes, $subTypes, $delim = CRM_Core_DAO
::VALUE_SEPARATOR
) {
3065 $groupTypeExpr = '';
3067 $groupTypeExpr .= implode(',', $coreTypes);
3070 //CRM-15427 Allow Multiple subtype filtering
3071 //if (count($subTypes) > 1) {
3072 //throw new CRM_Core_Exception("Multiple subtype filtering is not currently supported by widget.");
3074 foreach ($subTypes as $subType => $subTypeIds) {
3075 $groupTypeExpr .= $delim . $subType . ':' . implode(':', $subTypeIds);
3078 return $groupTypeExpr;
3082 * setDefault component specific profile fields.
3084 * @param array $fields
3086 * @param int $componentId
3088 * @param string $component
3090 * @param array $defaults
3091 * An array of default values.
3093 * @param bool $isStandalone
3095 public static function setComponentDefaults(&$fields, $componentId, $component, &$defaults, $isStandalone = FALSE) {
3096 if (!$componentId ||
3097 !in_array($component, ['Contribute', 'Membership', 'Event', 'Activity', 'Case'])
3102 $componentBAO = $componentSubType = NULL;
3103 switch ($component) {
3105 $componentBAO = 'CRM_Member_BAO_Membership';
3106 $componentBAOName = 'Membership';
3107 $componentSubType = ['membership_type_id'];
3111 $componentBAO = 'CRM_Contribute_BAO_Contribution';
3112 $componentBAOName = 'Contribution';
3113 $componentSubType = ['financial_type_id'];
3117 $componentBAO = 'CRM_Event_BAO_Participant';
3118 $componentBAOName = 'Participant';
3119 $componentSubType = ['role_id', 'event_id', 'event_type_id'];
3123 $componentBAO = 'CRM_Activity_BAO_Activity';
3124 $componentBAOName = 'Activity';
3125 $componentSubType = ['activity_type_id'];
3129 $componentBAO = 'CRM_Case_BAO_Case';
3130 $componentBAOName = 'Case';
3131 $componentSubType = ['case_type_id'];
3136 $params = ['id' => $componentId];
3138 //get the component values.
3139 CRM_Core_DAO
::commonRetrieve($componentBAO, $params, $values);
3140 if ($componentBAOName == 'Participant') {
3141 $values +
= ['event_type_id' => CRM_Core_DAO
::getFieldValue('CRM_Event_DAO_Event', $values['event_id'], 'event_type_id')];
3144 $formattedGroupTree = [];
3146 foreach ($fields as $name => $field) {
3147 $fldName = $isStandalone ?
$name : "field[$componentId][$name]";
3148 if (array_key_exists($name, $values)) {
3149 $defaults[$fldName] = $values[$name];
3151 elseif ($name == 'participant_note') {
3152 $noteDetails = CRM_Core_BAO_Note
::getNote($componentId, 'civicrm_participant');
3153 $defaults[$fldName] = array_pop($noteDetails);
3155 elseif (in_array($name, [
3157 'payment_instrument',
3158 'participant_status',
3161 $defaults[$fldName] = $values["{$name}_id"];
3163 elseif ($name == 'membership_type') {
3164 // since membership_type field is a hierselect -
3165 $defaults[$fldName][0]
3166 = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_MembershipType', $values['membership_type_id'], 'member_of_contact_id', 'id');
3167 $defaults[$fldName][1] = $values['membership_type_id'];
3169 elseif ($name == 'membership_status') {
3170 $defaults[$fldName] = $values['status_id'];
3172 elseif ($name == 'case_status') {
3173 $defaults[$fldName] = $values['case_status_id'];
3175 elseif (CRM_Core_BAO_CustomField
::getKeyID($name, TRUE) !== [NULL, NULL]) {
3176 if (empty($formattedGroupTree)) {
3177 //get the groupTree as per subTypes.
3179 foreach ($componentSubType as $subType) {
3180 $subTree = CRM_Core_BAO_CustomGroup
::getTree($componentBAOName, NULL,
3181 $componentId, 0, $values[$subType]
3183 $groupTree = CRM_Utils_Array
::crmArrayMerge($groupTree, $subTree);
3185 $formattedGroupTree = CRM_Core_BAO_CustomGroup
::formatGroupTree($groupTree, 1);
3186 CRM_Core_BAO_CustomGroup
::setDefaults($formattedGroupTree, $defaults);
3189 //FIX ME: We need to loop defaults, but once we move to custom_1_x convention this code can be simplified.
3190 foreach ($defaults as $customKey => $customValue) {
3191 if ($customFieldDetails = CRM_Core_BAO_CustomField
::getKeyID($customKey, TRUE)) {
3192 if ($name == 'custom_' . $customFieldDetails[0]) {
3194 //hack to set default for checkbox
3195 //basically this is for weired field name like field[33][custom_19]
3196 //we are converting this field name to array structure and assign value.
3199 foreach ($formattedGroupTree as $tree) {
3200 if (!empty($tree['fields'][$customFieldDetails[0]])) {
3201 if ('CheckBox' == CRM_Utils_Array
::value('html_type', $tree['fields'][$customFieldDetails[0]])) {
3203 $defaults['field'][$componentId][$name] = $customValue;
3206 elseif (CRM_Utils_Array
::value('data_type', $tree['fields'][$customFieldDetails[0]]) == 'Date') {
3209 // CRM-6681, $default contains formatted date, time values.
3210 $defaults[$fldName] = $customValue;
3211 if (!empty($defaults[$customKey . '_time'])) {
3212 $defaults['field'][$componentId][$name . '_time'] = $defaults[$customKey . '_time'];
3218 if (!$skipValue ||
$isStandalone) {
3219 $defaults[$fldName] = $customValue;
3221 unset($defaults[$customKey]);
3227 elseif (isset($values[$fldName])) {
3228 $defaults[$fldName] = $values[$fldName];
3234 * Retrieve groups of profiles.
3236 * @param int $profileID
3237 * Id of the profile.
3242 public static function profileGroups($profileID) {
3244 $profileTypes = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $profileID, 'group_type');
3245 if ($profileTypes) {
3246 $groupTypeParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $profileTypes);
3247 $groupTypes = explode(',', $groupTypeParts[0]);
3253 * Alter contact params by filtering existing subscribed groups and returns
3254 * unsubscribed groups array for subscription.
3256 * @param array $params
3258 * @param int $contactId
3262 * This contains array of groups for subscription
3264 public static function getDoubleOptInGroupIds(&$params, $contactId = NULL) {
3265 $config = CRM_Core_Config
::singleton();
3266 $subscribeGroupIds = [];
3268 // process further only if profileDoubleOptIn enabled and if groups exist
3269 if (!array_key_exists('group', $params) ||
3270 !self
::isProfileDoubleOptin() ||
3271 CRM_Utils_System
::isNull($params['group'])
3273 return $subscribeGroupIds;
3276 //check if contact email exist.
3278 foreach ($params as $name => $value) {
3279 if (strpos($name, 'email-') !== FALSE) {
3285 //Proceed furthur only if email present
3287 return $subscribeGroupIds;
3290 //do check for already subscriptions.
3291 $contactGroups = [];
3295 FROM civicrm_group_contact
3296 WHERE status = 'Added'
3297 AND contact_id = %1";
3299 $dao = CRM_Core_DAO
::executeQuery($query, [1 => [$contactId, 'Integer']]);
3300 while ($dao->fetch()) {
3301 $contactGroups[$dao->group_id
] = $dao->group_id
;
3305 //since we don't have names, compare w/ label.
3306 $mailingListGroupType = array_search('Mailing List', CRM_Core_OptionGroup
::values('group_type'));
3308 //actual processing start.
3309 foreach ($params['group'] as $groupId => $isSelected) {
3310 //unset group those are not selected.
3312 unset($params['group'][$groupId]);
3316 $groupTypes = explode(CRM_Core_DAO
::VALUE_SEPARATOR
,
3317 CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Group', $groupId, 'group_type', 'id')
3319 //get only mailing type group and unset it from params
3320 if (in_array($mailingListGroupType, $groupTypes) && !in_array($groupId, $contactGroups)) {
3321 $subscribeGroupIds[$groupId] = $groupId;
3322 unset($params['group'][$groupId]);
3326 return $subscribeGroupIds;
3330 * Check if we are rendering mixed profiles.
3332 * @param array $profileIds
3333 * Associated array of profile ids.
3336 * true if profile is mixed
3338 public static function checkForMixProfiles($profileIds) {
3339 $mixProfile = FALSE;
3341 $contactTypes = CRM_Contact_BAO_ContactType
::basicTypes(TRUE);
3343 $components = ['Contribution', 'Participant', 'Membership', 'Activity'];
3345 $typeCount = ['ctype' => [], 'subtype' => []];
3346 foreach ($profileIds as $gid) {
3347 $profileType = CRM_Core_BAO_UFField
::getProfileType($gid);
3348 // ignore profile of type Contact
3349 if ($profileType == 'Contact') {
3352 if (in_array($profileType, $contactTypes, TRUE)) {
3353 if (!isset($typeCount['ctype'][$profileType])) {
3354 $typeCount['ctype'][$profileType] = 1;
3357 // check if we are rendering profile of different contact types
3358 if (count($typeCount['ctype']) == 2) {
3363 elseif (in_array($profileType, $components, TRUE)) {
3368 if (!isset($typeCount['subtype'][$profileType])) {
3369 $typeCount['subtype'][$profileType] = 1;
3371 // check if we are rendering profile of different contact sub types
3372 if (count($typeCount['subtype']) == 2) {
3382 * Determine of we show overlay profile or not.
3385 * true if profile should be shown else false
3387 public static function showOverlayProfile() {
3388 $showOverlay = TRUE;
3390 // get the id of overlay profile
3391 $overlayProfileId = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', 'summary_overlay', 'id', 'name');
3392 $query = "SELECT count(id) FROM civicrm_uf_field WHERE uf_group_id = {$overlayProfileId} AND visibility IN ('Public Pages', 'Public Pages and Listings') ";
3394 $count = CRM_Core_DAO
::singleValueQuery($query);
3396 //check if there are no public fields and use is anonymous
3397 $session = CRM_Core_Session
::singleton();
3398 if (!$count && !$session->get('userID')) {
3399 $showOverlay = FALSE;
3402 return $showOverlay;
3406 * Get group type values of the profile.
3408 * @param int $profileId
3409 * @param string $groupType
3414 public static function groupTypeValues($profileId, $groupType = NULL) {
3415 $groupTypeValue = [];
3416 $groupTypes = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $profileId, 'group_type');
3418 $groupTypeParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $groupTypes);
3419 if (empty($groupTypeParts[1])) {
3420 return $groupTypeValue;
3422 $participantExtends = ['ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
3424 foreach (explode(',', $groupTypeParts[1]) as $groupTypeValues) {
3426 $valueParts = explode(':', $groupTypeValues);
3428 ($valueParts[0] != "{$groupType}Type" ||
3429 ($groupType == 'Participant' &&
3430 !in_array($valueParts[0], $participantExtends)
3436 foreach ($valueParts as $val) {
3437 if (CRM_Utils_Rule
::integer($val)) {
3438 $values[$val] = $val;
3441 if (!empty($values)) {
3442 $typeName = substr($valueParts[0], 0, -4);
3443 if (in_array($valueParts[0], $participantExtends)) {
3444 $typeName = $valueParts[0];
3446 $groupTypeValue[$typeName] = $values;
3450 return $groupTypeValue;
3454 * @return bool|object
3456 public static function isProfileDoubleOptin() {
3457 // check for double optin
3458 if (CRM_Core_Component
::isEnabled('CiviMail')) {
3459 return Civi
::settings()->get('profile_double_optin');
3465 * @return bool|object
3467 public static function isProfileAddToGroupDoubleOptin() {
3468 // check for add to group double optin
3469 if (CRM_Core_Component
::isEnabled('CiviMail')) {
3470 return Civi
::settings()->get('profile_add_to_group_double_optin');
3476 * Get profiles used for batch entry.
3479 * profileIds profile ids
3481 public static function getBatchProfiles() {
3483 FROM civicrm_uf_group
3484 WHERE name IN ('contribution_batch_entry', 'membership_batch_entry')";
3485 $dao = CRM_Core_DAO
::executeQuery($query);
3487 while ($dao->fetch()) {
3488 $profileIds[$dao->id
] = $dao->id
;
3495 * @param $destination
3496 * @param bool $returnMultiSummaryFields
3498 * @return array|null
3499 * @todo what do I do?
3501 public static function shiftMultiRecordFields(&$source, &$destination, $returnMultiSummaryFields = FALSE) {
3502 $multiSummaryFields = $returnMultiSummaryFields ?
[] : NULL;
3503 foreach ($source as $field => $properties) {
3504 if (!CRM_Core_BAO_CustomField
::getKeyID($field)) {
3507 if (CRM_Core_BAO_CustomField
::isMultiRecordField($field)) {
3508 $destination[$field] = $properties;
3509 if ($returnMultiSummaryFields) {
3510 if ($properties['is_multi_summary']) {
3511 $multiSummaryFields[$field] = $properties;
3514 unset($source[$field]);
3517 return $multiSummaryFields;
3521 * This is function is used to format pseudo fields.
3523 * @param array $fields
3524 * Associated array of profile fields.
3527 public static function reformatProfileFields(&$fields) {
3528 //reformat fields array
3529 foreach ($fields as $name => $field) {
3530 //reformat phone and extension field
3531 if (substr($field['name'], 0, 13) == 'phone_and_ext') {
3532 $fieldSuffix = str_replace('phone_and_ext-', '', $field['name']);
3534 // retain existing element properties and just update and replace key
3535 CRM_Utils_Array
::crmReplaceKey($fields, $name, "phone-{$fieldSuffix}");
3536 $fields["phone-{$fieldSuffix}"]['name'] = "phone-{$fieldSuffix}";
3537 $fields["phone-{$fieldSuffix}"]['where'] = 'civicrm_phone.phone';
3539 // add additional phone extension field
3540 $fields["phone_ext-{$fieldSuffix}"] = $field;
3541 $fields["phone_ext-{$fieldSuffix}"]['title'] = $field['title'] . ' - ' . ts('Ext.');
3542 $fields["phone_ext-{$fieldSuffix}"]['name'] = "phone_ext-{$fieldSuffix}";
3543 $fields["phone_ext-{$fieldSuffix}"]['where'] = 'civicrm_phone.phone_ext';
3544 $fields["phone_ext-{$fieldSuffix}"]['skipDisplay'] = 1;
3545 //ignore required for extension field
3546 $fields["phone_ext-{$fieldSuffix}"]['is_required'] = 0;
3552 * Get the frontend_title for the profile, falling back on 'title' if none.
3554 * @param int $profileID
3558 * @throws \CiviCRM_API3_Exception
3560 public static function getFrontEndTitle(int $profileID) {
3561 $profile = civicrm_api3('UFGroup', 'getsingle', ['id' => $profileID, 'return' => ['title', 'frontend_title']]);
3562 return $profile['frontend_title'] ??
$profile['title'];
3566 * Format custom field value for use in prepopulating a quickform profile field.
3568 * @param array $field
3570 * @param string $value
3574 * String or array, depending on the html type
3576 private static function formatCustomValue($field, $value) {
3577 if (CRM_Core_BAO_CustomField
::isSerialized($field)) {
3578 $value = CRM_Utils_Array
::explodePadded($value);
3580 // This may not be required now.
3581 if ($field['html_type'] === 'CheckBox') {
3583 foreach (array_filter($value) as $item) {
3584 $checkboxes[$item] = 1;
3585 // CRM-2969 seems like we need this for QF style checkboxes in profile where its multiindexed
3586 $checkboxes["[{$item}]"] = 1;