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,
506 'html_type' => $fieldMetaData['html']['type'] ??
NULL,
509 $formattedField = CRM_Utils_Date
::addDateMetadataToField($fieldMetaData, $formattedField);
511 //adding custom field property
512 if (substr($field->field_name
, 0, 6) == 'custom' ||
513 substr($field->field_name
, 0, 14) === 'address_custom'
515 // if field is not present in customFields, that means the user
516 // DOES NOT HAVE permission to access that field
517 if (array_key_exists($field->field_name
, $customFields)) {
518 $formattedField['serialize'] = !empty($customFields[$field->field_name
]['serialize']);
519 $formattedField['is_search_range'] = $customFields[$field->field_name
]['is_search_range'];
521 $formattedField['options_per_line'] = $customFields[$field->field_name
]['options_per_line'];
522 $formattedField['html_type'] = $customFields[$field->field_name
]['html_type'];
524 if (CRM_Utils_Array
::value('html_type', $formattedField) == 'Select Date') {
525 $formattedField['date_format'] = $customFields[$field->field_name
]['date_format'];
526 $formattedField['time_format'] = $customFields[$field->field_name
]['time_format'];
527 $formattedField['is_datetime_field'] = TRUE;
528 $formattedField['smarty_view_format'] = CRM_Utils_Date
::getDateFieldViewFormat($formattedField['date_format']);
531 $formattedField['is_multi_summary'] = $field->is_multi_summary
;
532 return [$name, $formattedField];
535 $formattedField = NULL;
536 return [$name, $formattedField];
539 return [$name, $formattedField];
543 * Create a query to find all visible UFFields in a UFGroup.
545 * This is the SQL-variant of checkUFFieldDisplayable().
547 * @param int $groupId
548 * @param bool $searchable
549 * @param bool $showAll
550 * @param int $visibility
551 * @param string $orderBy
552 * Comma-delimited list of SQL columns.
556 protected static function createUFFieldQuery($groupId, $searchable, $showAll, $visibility, $orderBy) {
557 $where = " WHERE uf_group_id = {$groupId}";
560 $where .= " AND is_searchable = 1";
564 $where .= " AND is_active = 1";
569 if ($visibility & self
::PUBLIC_VISIBILITY
) {
570 $clause[] = 'visibility = "Public Pages"';
572 if ($visibility & self
::ADMIN_VISIBILITY
) {
573 $clause[] = 'visibility = "User and User Admin Only"';
575 if ($visibility & self
::LISTINGS_VISIBILITY
) {
576 $clause[] = 'visibility = "Public Pages and Listings"';
578 if (!empty($clause)) {
579 $where .= ' AND ( ' . implode(' OR ', $clause) . ' ) ';
583 $query = "SELECT * FROM civicrm_uf_field $where ORDER BY weight";
585 $query .= ", " . $orderBy;
592 * Create a query to find all visible UFFields in a UFGroup.
594 * This is the PHP in-memory variant of createUFFieldQuery().
596 * @param CRM_Core_DAO_UFField|CRM_Core_DAO $field
597 * @param bool $searchable
598 * @param bool $showAll
599 * @param int $visibility
602 * TRUE if field is displayable
604 protected static function filterUFField($field, $searchable, $showAll, $visibility) {
605 if ($searchable && $field->is_searchable
!= 1) {
609 if (!$showAll && $field->is_active
!= 1) {
614 $allowedVisibilities = [];
615 if ($visibility & self
::PUBLIC_VISIBILITY
) {
616 $allowedVisibilities[] = 'Public Pages';
618 if ($visibility & self
::ADMIN_VISIBILITY
) {
619 $allowedVisibilities[] = 'User and User Admin Only';
621 if ($visibility & self
::LISTINGS_VISIBILITY
) {
622 $allowedVisibilities[] = 'Public Pages and Listings';
624 // !empty($allowedVisibilities) seems silly to me, but it is equivalent to the pre-existing SQL
625 if (!empty($allowedVisibilities) && !in_array($field->visibility
, $allowedVisibilities)) {
634 * Get a list of filtered field metadata.
637 * @param $profileType
638 * @param $contactActivityProfile
639 * @param bool $filterMode
640 * Filter mode means you are using importable fields for filtering rather than just getting metadata.
641 * With filter mode = FALSE BOTH activity fields and component fields are returned.
642 * I can't see why you would ever want to use this function in filter mode as the component fields are
643 * still unfiltered. However, I feel scared enough to leave it as it is. I have marked this function as
644 * deprecated and am recommending the wrapper 'getProfileFieldMetadata' in order to try to
645 * send this confusion to history.
648 * @deprecated use getProfileFieldMetadata
651 protected static function getImportableFields($showAll, $profileType, $contactActivityProfile, $filterMode = TRUE) {
653 $importableFields = CRM_Contact_BAO_Contact
::importableFields('All', FALSE, FALSE, FALSE, TRUE, TRUE);
656 $importableFields = CRM_Contact_BAO_Contact
::importableFields('All', FALSE, TRUE, FALSE, TRUE, TRUE);
659 $activityFields = CRM_Activity_BAO_Activity
::getProfileFields();
660 $componentFields = CRM_Core_Component
::getQueryFields();
661 if ($filterMode == TRUE) {
662 if ($profileType == 'Activity' ||
$contactActivityProfile) {
663 $importableFields = array_merge($importableFields, $activityFields);
666 $importableFields = array_merge($importableFields, $componentFields);
670 $importableFields = array_merge($importableFields, $activityFields, $componentFields);
673 $importableFields['group']['title'] = ts('Group(s)');
674 $importableFields['group']['where'] = NULL;
675 $importableFields['tag']['title'] = ts('Tag(s)');
676 $importableFields['tag']['where'] = NULL;
677 return $importableFields;
681 * Get the metadata for all potential profile fields.
683 * @param bool $isIncludeInactive
684 * Should disabled fields be included.
687 * Field metadata for all fields that might potentially be in a profile.
689 protected static function getProfileFieldMetadata($isIncludeInactive) {
690 return self
::getImportableFields($isIncludeInactive, NULL, NULL, NULL, TRUE);
694 * Get the fields relating to locations.
698 public static function getLocationFields() {
699 static $locationFields = [
701 'supplemental_address_1',
702 'supplemental_address_2',
703 'supplemental_address_3',
706 'postal_code_suffix',
719 return $locationFields;
724 * @param int|bool $checkPermission
727 protected static function getCustomFields($ctype, $checkPermission = CRM_Core_Permission
::VIEW
) {
728 // Only Edit and View is supported in ACL for custom field.
729 if ($checkPermission == CRM_Core_Permission
::CREATE
) {
730 $checkPermission = CRM_Core_Permission
::EDIT
;
732 $cacheKey = 'uf_group_custom_fields_' . $ctype . '_' . (int) $checkPermission;
733 if (!Civi
::cache('metadata')->has($cacheKey)) {
734 $customFields = CRM_Core_BAO_CustomField
::getFieldsForImport($ctype, FALSE, FALSE, FALSE, $checkPermission, TRUE);
736 // hack to add custom data for components
737 $components = ['Contribution', 'Participant', 'Membership', 'Activity', 'Case'];
738 foreach ($components as $value) {
739 $customFields = array_merge($customFields, CRM_Core_BAO_CustomField
::getFieldsForImport($value));
741 $addressCustomFields = CRM_Core_BAO_CustomField
::getFieldsForImport('Address');
742 $customFields = array_merge($customFields, $addressCustomFields);
743 Civi
::cache('metadata')->set($cacheKey, [$customFields, $addressCustomFields]);
745 return Civi
::cache('metadata')->get($cacheKey);
749 * Check the data validity.
752 * The user id that we are actually editing.
753 * @param string $name
754 * The machine-name of the group we are interested in.
755 * @param bool $register
757 * The action of the form.
759 * @pram boolean $register is this the registrtion form
761 * true if form is valid
763 public static function isValid($userID, $name, $register = FALSE, $action = NULL) {
765 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
766 ts('Dynamic Form Creator'),
769 $controller->set('id', $userID);
770 $controller->set('register', 1);
771 $controller->process();
772 return $controller->validate();
775 // make sure we have a valid group
776 $group = new CRM_Core_DAO_UFGroup();
778 $group->name
= $name;
780 if ($group->find(TRUE) && $userID) {
781 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic', ts('Dynamic Form Creator'), $action);
782 $controller->set('gid', $group->id
);
783 $controller->set('id', $userID);
784 $controller->set('register', 0);
785 $controller->process();
786 return $controller->validate();
793 * Get the html for the form that represents this particular group.
796 * The user id that we are actually editing.
797 * @param string $title
798 * The title of the group we are interested in.
800 * The action of the form.
801 * @param bool $register
802 * Is this the registration form.
804 * Should we reset the form?.
805 * @param int $profileID
806 * Do we have the profile ID?.
808 * @param bool $doNotProcess
812 * the html for the form on success, otherwise empty string
814 public static function getEditHTML(
821 $doNotProcess = FALSE,
826 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
827 ts('Dynamic Form Creator'),
830 if ($reset ||
$doNotProcess) {
831 // hack to make sure we do not process this form
832 $oldQFDefault = CRM_Utils_Array
::value('_qf_default',
835 unset($_POST['_qf_default']);
836 unset($_REQUEST['_qf_default']);
838 $controller->reset();
842 $controller->set('id', $userID);
843 $controller->set('register', 1);
844 $controller->set('skipPermission', 1);
845 $controller->set('ctype', $ctype);
846 $controller->process();
847 if ($doNotProcess ||
!empty($_POST)) {
848 $controller->validate();
850 $controller->setEmbedded(TRUE);
852 //CRM-5839 - though we want to process form, get the control back.
853 $controller->setSkipRedirection(!$doNotProcess);
857 // we are done processing so restore the POST/REQUEST vars
858 if (($reset ||
$doNotProcess) && $oldQFDefault) {
859 $_POST['_qf_default'] = $_REQUEST['_qf_default'] = $oldQFDefault;
862 $template = CRM_Core_Smarty
::singleton();
864 // Hide CRM error messages if they are displayed using drupal form_set_error.
865 if (!empty($_POST)) {
866 $template->assign('suppressForm', TRUE);
869 return trim($template->fetch('CRM/Profile/Form/Dynamic.tpl'));
873 // make sure we have a valid group
874 $group = new CRM_Core_DAO_UFGroup();
876 $group->title
= $title;
878 if ($group->find(TRUE)) {
879 $profileID = $group->id
;
884 // make sure profileID and ctype match if ctype exists
886 $profileType = CRM_Core_BAO_UFField
::getProfileType($profileID);
887 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
888 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
891 if (($profileType != 'Contact') && ($profileType != $ctype)) {
896 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
897 ts('Dynamic Form Creator'),
901 $controller->reset();
903 $controller->set('gid', $profileID);
904 $controller->set('id', $userID);
905 $controller->set('register', 0);
906 $controller->set('skipPermission', 1);
908 $controller->set('ctype', $ctype);
910 $controller->process();
911 $controller->setEmbedded(TRUE);
913 //CRM-5846 - give the control back to drupal.
914 $controller->setSkipRedirection(!$doNotProcess);
917 $template = CRM_Core_Smarty
::singleton();
919 // Hide CRM error messages if they are displayed using drupal form_set_error.
920 if (!empty($_POST) && CRM_Core_Config
::singleton()->userFramework
== 'Drupal') {
921 if (arg(0) == 'user' ||
(arg(0) == 'admin' && arg(1) == 'people')) {
922 $template->assign('suppressForm', TRUE);
926 $templateFile = "CRM/Profile/Form/{$profileID}/Dynamic.tpl";
927 if (!$template->template_exists($templateFile)) {
928 $templateFile = 'CRM/Profile/Form/Dynamic.tpl';
930 return trim($template->fetch($templateFile));
933 $userEmail = CRM_Contact_BAO_Contact_Location
::getEmailDetails($userID);
935 // if post not empty then only proceed
936 if (!empty($_POST)) {
938 $config = CRM_Core_Config
::singleton();
939 $email = $_POST['mail'] ??
NULL;
941 if (CRM_Utils_Rule
::email($email) && ($email != $userEmail[1])) {
942 CRM_Core_BAO_UFMatch
::updateContactEmail($userID, $email);
951 * Given a contact id and a field set, return the values from the db.
954 * @param array $fields
955 * The profile fields of interest.
956 * @param array $values
957 * The values for the above fields.
958 * @param bool $searchable
960 * @param array $componentWhere
961 * Component condition.
962 * @param bool $absolute
963 * Return urls in absolute form (useful when sending an email).
964 * @param null $additionalWhereClause
968 public static function getValues(
969 $cid, &$fields, &$values,
970 $searchable = TRUE, $componentWhere = NULL,
971 $absolute = FALSE, $additionalWhereClause = NULL
973 if (empty($cid) && empty($componentWhere)) {
977 // get the contact details (hier)
978 $returnProperties = CRM_Contact_BAO_Contact
::makeHierReturnProperties($fields);
979 $params = $cid ?
[['contact_id', '=', $cid, 0, 0]] : [];
981 // add conditions specified by components. eg partcipant_id etc
982 if (!empty($componentWhere)) {
983 $params = array_merge($params, $componentWhere);
986 $query = new CRM_Contact_BAO_Query($params, $returnProperties, $fields);
988 $details = $query->searchQuery(0, 0, NULL, FALSE, FALSE,
989 FALSE, FALSE, FALSE, $additionalWhereClause);
990 while ($details->fetch()) {
995 $query->convertToPseudoNames($details);
997 $locationTypes = CRM_Core_BAO_Address
::buildOptions('location_type_id', 'validate');
998 $imProviders = CRM_Core_DAO_IM
::buildOptions('provider_id');
999 $websiteTypes = CRM_Core_DAO_Website
::buildOptions('website_type_id');
1001 $multipleFields = ['url'];
1003 //start of code to set the default values
1004 foreach ($fields as $name => $field) {
1006 if ($name == 'id') {
1007 $name = 'contact_id';
1010 // skip fields that should not be displayed separately
1011 if (!empty($field['skipDisplay'])) {
1015 // Create a unique, non-empty index for each field.
1016 $index = $field['title'];
1017 if ($index === '') {
1020 while (array_key_exists($index, $values)) {
1024 $params[$index] = $values[$index] = '';
1025 $customFieldName = NULL;
1027 if (isset($details->$name) ||
$name == 'group' ||
$name == 'tag') {
1028 // to handle gender / suffix / prefix
1029 if (in_array(substr($name, 0, -3), ['gender', 'prefix', 'suffix'])) {
1030 $params[$index] = $details->$name;
1031 $values[$index] = $details->$name;
1033 elseif (in_array($name, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
1034 $dname = $name . '_display';
1035 $values[$index] = $details->$dname;
1036 $name = $name . '_id';
1037 $params[$index] = $details->$name;
1039 elseif (in_array($name, [
1044 $values[$index] = $details->$name;
1045 $idx = $name . '_id';
1046 $params[$index] = $details->$idx;
1048 elseif ($name === 'preferred_language') {
1049 $params[$index] = $details->$name;
1050 $values[$index] = CRM_Core_PseudoConstant
::getLabel('CRM_Contact_DAO_Contact', 'preferred_language', $details->$name);
1052 elseif ($name == 'group') {
1053 $groups = CRM_Contact_BAO_GroupContact
::getContactGroup($cid, 'Added', NULL, FALSE, TRUE);
1056 foreach ($groups as $g) {
1057 // CRM-8362: User and User Admin visibility groups should be included in display if user has
1058 // VIEW permission on that group
1059 $groupPerm = CRM_Contact_BAO_Group
::checkPermission($g['group_id'], TRUE);
1061 if ($g['visibility'] != 'User and User Admin Only' ||
1062 CRM_Utils_Array
::key(CRM_Core_Permission
::VIEW
, $groupPerm)
1064 $title[] = $g['title'];
1065 if ($g['visibility'] == 'Public Pages') {
1066 $ids[] = $g['group_id'];
1070 $values[$index] = implode(', ', $title);
1071 $params[$index] = implode(',', $ids);
1073 elseif ($name == 'tag') {
1074 $entityTags = CRM_Core_BAO_EntityTag
::getTag($cid);
1075 $allTags = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]);
1077 foreach ($entityTags as $tagId) {
1078 $title[] = $allTags[$tagId];
1080 $values[$index] = implode(', ', $title);
1081 $params[$index] = implode(',', $entityTags);
1083 elseif ($name == 'activity_status_id') {
1084 $activityStatus = CRM_Core_PseudoConstant
::activityStatus();
1085 $values[$index] = $activityStatus[$details->$name];
1086 $params[$index] = $details->$name;
1088 elseif ($name == 'activity_date_time') {
1089 $values[$index] = CRM_Utils_Date
::customFormat($details->$name);
1090 $params[$index] = $details->$name;
1092 elseif ($name == 'contact_sub_type') {
1093 $contactSubTypeNames = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $details->$name);
1094 if (!empty($contactSubTypeNames)) {
1095 $contactSubTypeLabels = [];
1096 // get all contact subtypes
1097 $allContactSubTypes = CRM_Contact_BAO_ContactType
::subTypeInfo();
1098 // build contact subtype labels array
1099 foreach ($contactSubTypeNames as $cstName) {
1101 $contactSubTypeLabels[] = $allContactSubTypes[$cstName]['label'];
1104 $values[$index] = implode(',', $contactSubTypeLabels);
1107 $params[$index] = $details->$name;
1110 if (substr($name, 0, 7) === 'do_not_' ||
substr($name, 0, 3) === 'is_') {
1111 if ($details->$name) {
1112 $values[$index] = '[ x ]';
1116 if ($cfID = CRM_Core_BAO_CustomField
::getKeyID($name)) {
1117 $htmlType = $field['html_type'];
1119 // field_type is only set when we are retrieving profile values
1120 // when sending email, we call the same function to get custom field
1121 // values etc, i.e. emulating a profile
1122 $fieldType = $field['field_type'] ??
NULL;
1124 if ($htmlType == 'File') {
1127 $fieldType == 'Activity' && !empty($componentWhere[0][2])
1129 $entityId = $componentWhere[0][2];
1132 $fileURL = CRM_Core_BAO_CustomField
::getFileURL($entityId,
1136 $additionalWhereClause
1138 $params[$index] = $values[$index] = $fileURL['file_url'];
1142 if (isset($dao) && property_exists($dao, 'data_type') &&
1143 ($dao->data_type
== 'Int' ||
1144 $dao->data_type
== 'Boolean'
1147 $customVal = (int ) ($details->{$name});
1149 elseif (isset($dao) && property_exists($dao, 'data_type')
1150 && $dao->data_type
== 'Float'
1152 $customVal = (float ) ($details->{$name});
1154 elseif (!CRM_Utils_System
::isNull(explode(CRM_Core_DAO
::VALUE_SEPARATOR
,
1158 $customVal = $details->{$name};
1162 if (CRM_Utils_System
::isNull($customVal)) {
1166 $params[$index] = $customVal;
1167 $values[$index] = CRM_Core_BAO_CustomField
::displayValue($customVal, $cfID);
1168 if ($field['data_type'] == 'ContactReference') {
1169 $params[$index] = $values[$index];
1171 if (CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_CustomField',
1172 $cfID, 'is_search_range'
1175 $customFieldName = "{$name}_from";
1179 elseif ($name == 'image_URL') {
1180 list($width, $height) = getimagesize(CRM_Utils_String
::unstupifyUrl($details->$name));
1181 list($thumbWidth, $thumbHeight) = CRM_Contact_BAO_Contact
::getThumbSize($width, $height);
1183 $image_URL = '<img src="' . $details->$name . '" height= ' . $thumbHeight . ' width= ' . $thumbWidth . ' />';
1184 $values[$index] = "<a href='#' onclick='contactImagePopUp(\"{$details->$name}\", {$width}, {$height});'>{$image_URL}</a>";
1186 elseif (in_array($name, [
1190 // @todo this set should be determined from metadata, not hard-coded.
1191 $values[$index] = CRM_Utils_Date
::customFormat($details->$name);
1192 $params[$index] = CRM_Utils_Date
::isoToMysql($details->$name);
1196 if ($index == 'Campaign') {
1197 $dao = 'CRM_Campaign_DAO_Campaign';
1199 elseif ($index == 'Contribution Page') {
1200 $dao = 'CRM_Contribute_DAO_ContributionPage';
1203 $value = CRM_Core_DAO
::getFieldValue($dao, $details->$name, 'title');
1206 $value = $details->$name;
1208 $values[$index] = $value;
1213 elseif (strpos($name, '-') !== FALSE) {
1214 list($fieldName, $id, $type) = CRM_Utils_System
::explode('-', $name, 3);
1216 if (!in_array($fieldName, $multipleFields)) {
1217 if ($id == 'Primary') {
1219 // not sure why we'd every use Primary location type id
1220 // we need to fix the source if we are using it
1221 // $locationTypeName = CRM_Contact_BAO_Contact::getPrimaryLocationType( $cid );
1222 $locationTypeName = 1;
1225 $locationTypeName = $locationTypes[$id] ??
NULL;
1228 if (!$locationTypeName) {
1232 $detailName = "{$locationTypeName}-{$fieldName}";
1233 $detailName = str_replace(' ', '_', $detailName);
1235 if (in_array($fieldName, [
1242 $detailName .= "-{$type}";
1246 if (in_array($fieldName, [
1251 $values[$index] = $details->$detailName;
1252 $idx = $detailName . '_id';
1253 $params[$index] = $details->$idx;
1255 elseif ($fieldName == 'im') {
1256 $providerId = $detailName . '-provider_id';
1257 if (isset($imProviders[$details->$providerId])) {
1258 $values[$index] = $details->$detailName . " (" . $imProviders[$details->$providerId] . ")";
1261 $values[$index] = $details->$detailName;
1263 $params[$index] = $details->$detailName;
1265 elseif ($fieldName == 'phone') {
1266 $phoneExtField = str_replace('phone', 'phone_ext', $detailName);
1267 if (isset($details->$phoneExtField)) {
1268 $values[$index] = $details->$detailName . " (" . $details->$phoneExtField . ")";
1271 $values[$index] = $details->$detailName;
1273 $params[$index] = $details->$detailName;
1276 $values[$index] = $params[$index] = $details->$detailName;
1280 $detailName = "website-{$id}-{$fieldName}";
1281 $url = CRM_Utils_System
::fixURL($details->$detailName);
1282 if ($details->$detailName) {
1283 $websiteTypeId = "website-{$id}-website_type_id";
1284 $websiteType = $websiteTypes[$details->$websiteTypeId];
1285 $values[$index] = "<a href=\"$url\">{$details->$detailName} ( {$websiteType} )</a>";
1288 $values[$index] = '';
1293 if ((CRM_Utils_Array
::value('visibility', $field) == 'Public Pages and Listings') &&
1294 CRM_Core_Permission
::check('profile listings and forms')
1297 if (CRM_Utils_System
::isNull($params[$index])) {
1298 $params[$index] = $values[$index];
1300 if (!isset($params[$index])) {
1303 if (!$customFieldName) {
1304 $fieldName = $field['name'];
1307 $fieldName = $customFieldName;
1311 if (CRM_Core_BAO_CustomField
::getKeyID($field['name'])) {
1312 $htmlType = $field['html_type'];
1313 if ($htmlType == 'Link') {
1314 $url = $params[$index];
1316 elseif (!empty($field['serialize'])) {
1317 $valSeparator = CRM_Core_DAO
::VALUE_SEPARATOR
;
1318 $selectedOptions = explode($valSeparator, $params[$index]);
1320 foreach ($selectedOptions as $key => $multiOption) {
1322 $url[] = CRM_Utils_System
::url('civicrm/profile',
1323 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1324 urlencode($fieldName) .
1326 urlencode($multiOption)
1332 $url = CRM_Utils_System
::url('civicrm/profile',
1333 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1334 urlencode($fieldName) .
1336 urlencode($params[$index])
1341 $url = CRM_Utils_System
::url('civicrm/profile',
1342 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1343 urlencode($fieldName) .
1345 urlencode($params[$index])
1350 !empty($values[$index]) &&
1354 if (is_array($url) && !empty($url)) {
1356 $eachMultiValue = explode(', ', $values[$index]);
1357 foreach ($eachMultiValue as $key => $valueLabel) {
1358 $links[] = '<a href="' . $url[$key] . '">' . $valueLabel . '</a>';
1360 $values[$index] = implode(', ', $links);
1363 $values[$index] = '<a href="' . $url . '">' . $values[$index] . '</a>';
1371 * Check if profile Group used by any module.
1379 public static function usedByModule($id) {
1380 //check whether this group is used by any module(check uf join records)
1382 FROM civicrm_uf_join
1383 WHERE civicrm_uf_join.uf_group_id=$id";
1385 $dao = new CRM_Core_DAO();
1387 if ($dao->fetch()) {
1396 * Delete the profile Group.
1404 public static function del($id) {
1405 CRM_Utils_Hook
::pre('delete', 'UFGroup', $id);
1407 //check whether this group contains any profile fields
1408 $profileField = new CRM_Core_DAO_UFField();
1409 $profileField->uf_group_id
= $id;
1410 $profileField->find();
1411 while ($profileField->fetch()) {
1412 CRM_Core_BAO_UFField
::del($profileField->id
);
1415 //delete records from uf join table
1416 $ufJoin = new CRM_Core_DAO_UFJoin();
1417 $ufJoin->uf_group_id
= $id;
1420 //delete profile group
1421 $group = new CRM_Core_DAO_UFGroup();
1425 CRM_Utils_Hook
::post('delete', 'UFGroup', $id, $group);
1432 * @param array $params
1433 * Reference array contains the values submitted by the form.
1440 public static function add(&$params, $ids = []) {
1441 if (empty($params['id']) && !empty($ids['ufgroup'])) {
1442 $params['id'] = $ids['ufgroup'];
1443 CRM_Core_Error
::deprecatedWarning('ids parameter is deprecated');
1446 // Convert parameter names but don't overwrite existing data on updates
1447 // unless explicitly specified. And allow setting to null, so use
1448 // array_key_exists. i.e. we need to treat missing and empty separately.
1449 if (array_key_exists('group', $params)) {
1450 $params['limit_listings_group_id'] = $params['group'];
1452 if (array_key_exists('add_contact_to_group', $params)) {
1453 $params['add_to_group_id'] = $params['add_contact_to_group'];
1457 if (!empty($params['group_type']) && is_array($params['group_type'])) {
1458 $params['group_type'] = implode(',', $params['group_type']);
1461 $hook = empty($params['id']) ?
'create' : 'edit';
1462 CRM_Utils_Hook
::pre($hook, 'UFGroup', ($params['id'] ??
NULL), $params);
1464 $ufGroup = new CRM_Core_DAO_UFGroup();
1465 $ufGroup->copyValues($params);
1467 $ufGroupID = CRM_Utils_Array
::value('ufgroup', $ids, CRM_Utils_Array
::value('id', $params));
1468 if (!$ufGroupID && empty($params['name'])) {
1469 $ufGroup->name
= CRM_Utils_String
::munge($ufGroup->title
, '_', 56);
1471 $ufGroup->id
= $ufGroupID;
1475 if (!$ufGroupID && empty($params['name'])) {
1476 $ufGroup->name
= $ufGroup->name
. "_{$ufGroup->id}";
1480 CRM_Utils_Hook
::post($hook, 'UFGroup', $ufGroup->id
, $ufGroup);
1486 * Make uf join entries for an uf group.
1488 * @param int $weight
1489 * @param array $groupTypes
1490 * An assoc array of name/value pairs.
1491 * @param int $ufGroupId
1494 public static function createUFJoin($weight, $groupTypes, $ufGroupId) {
1496 // get ufjoin records for uf group
1497 $ufGroupRecord = CRM_Core_BAO_UFGroup
::getUFJoinRecord($ufGroupId);
1499 // get the list of all ufgroup types
1500 $allUFGroupType = CRM_Core_SelectValues
::ufGroupTypes();
1502 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
1503 if (!is_array($groupTypes)) {
1507 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
1508 if (!is_array($ufGroupRecord)) {
1509 $ufGroupRecord = [];
1512 // check which values has to be inserted/deleted for contact
1513 $menuRebuild = FALSE;
1514 foreach ($allUFGroupType as $key => $value) {
1516 $joinParams['uf_group_id'] = $ufGroupId;
1517 $joinParams['module'] = $key;
1518 if ($key === 'User Account') {
1519 $menuRebuild = TRUE;
1521 if (array_key_exists($key, $groupTypes) && !in_array($key, $ufGroupRecord)) {
1522 // insert a new record
1523 CRM_Core_BAO_UFGroup
::addUFJoin($joinParams);
1525 elseif (!array_key_exists($key, $groupTypes) && in_array($key, $ufGroupRecord)) {
1526 // delete a record for existing ufgroup
1527 CRM_Core_BAO_UFGroup
::delUFJoin($joinParams);
1533 UPDATE civicrm_uf_join
1535 WHERE uf_group_id = %2
1536 AND ( entity_id IS NULL OR entity_id <= 0 )
1539 1 => [$weight, 'Integer'],
1540 2 => [$ufGroupId, 'Integer'],
1542 CRM_Core_DAO
::executeQuery($query, $p);
1544 // Do a menu rebuild, so it gets all the new menu entries for user account
1546 $config = CRM_Core_Config
::singleton();
1547 $config->userSystem
->updateCategories();
1552 * Get the UF Join records for an ufgroup id.
1554 * @param int $ufGroupId
1556 * @param int $displayName
1557 * If set return display name in array.
1558 * @param int $status
1559 * If set return module other than default modules (User Account/User registration/Profile).
1564 public static function getUFJoinRecord($ufGroupId = NULL, $displayName = NULL, $status = NULL) {
1567 $UFGroupType = CRM_Core_SelectValues
::ufGroupTypes();
1571 $dao = new CRM_Core_DAO_UFJoin();
1574 $dao->uf_group_id
= $ufGroupId;
1580 while ($dao->fetch()) {
1581 if (!$displayName) {
1582 $ufJoin[$dao->id
] = $dao->module
;
1585 if (isset($UFGroupType[$dao->module
])) {
1586 // skip the default modules
1588 $ufJoin[$dao->id
] = $UFGroupType[$dao->module
];
1590 // added for CRM-1475
1592 elseif (!CRM_Utils_Array
::key($dao->module
, $ufJoin)) {
1593 $ufJoin[$dao->id
] = $dao->module
;
1601 * Function takes an associative array and creates a ufjoin record for ufgroup.
1603 * @param array $params
1604 * (reference) an assoc array of name/value pairs.
1606 * @return CRM_Core_DAO_UFJoin
1608 public static function addUFJoin(&$params) {
1609 $ufJoin = new CRM_Core_DAO_UFJoin();
1610 $ufJoin->copyValues($params);
1616 * Delete the uf join record for an uf group.
1618 * @param array $params
1619 * (reference) an assoc array of name/value pairs.
1621 public static function delUFJoin(&$params) {
1622 $ufJoin = new CRM_Core_DAO_UFJoin();
1623 $ufJoin->copyValues($params);
1628 * Get the weight for ufjoin record.
1630 * @param int $ufGroupId
1631 * If $ufGroupId get update weight or add weight.
1634 * weight of the UFGroup
1636 public static function getWeight($ufGroupId = NULL) {
1637 //calculate the weight
1640 $queryString = "SELECT ( MAX(civicrm_uf_join.weight)+1) as new_weight
1641 FROM civicrm_uf_join
1642 WHERE module = 'User Registration' OR module = 'User Account' OR module = 'Profile'";
1645 $queryString = "SELECT MAX(civicrm_uf_join.weight) as new_weight
1646 FROM civicrm_uf_join
1647 WHERE civicrm_uf_join.uf_group_id = %1
1648 AND ( entity_id IS NULL OR entity_id <= 0 )";
1649 $p[1] = [$ufGroupId, 'Integer'];
1652 $dao = CRM_Core_DAO
::executeQuery($queryString, $p);
1654 return ($dao->new_weight
) ?
$dao->new_weight
: 1;
1658 * Get the uf group for a module.
1660 * @param string $moduleName
1663 * No to increment the weight.
1664 * @param bool $skipPermission
1666 * Which operation (view, edit, create, etc) to check permission for.
1667 * @param array|null $returnFields
1670 * array of ufgroups for a module
1672 public static function getModuleUFGroup($moduleName = NULL, $count = 0, $skipPermission = TRUE, $op = CRM_Core_Permission
::VIEW
, $returnFields = NULL) {
1673 $selectFields = ['id', 'title', 'created_id', 'is_active', 'is_reserved', 'group_type', 'description'];
1675 if (CRM_Core_BAO_SchemaHandler
::checkIfFieldExists('civicrm_uf_group', 'frontend_title')) {
1676 $selectFields[] = 'frontend_title';
1679 if (!empty($returnFields)) {
1680 $selectFields = array_merge($returnFields, array_diff($selectFields, $returnFields));
1683 $queryString = 'SELECT civicrm_uf_group.' . implode(', civicrm_uf_group.', $selectFields) . '
1684 FROM civicrm_uf_group
1685 LEFT JOIN civicrm_uf_join ON (civicrm_uf_group.id = uf_group_id)';
1688 $queryString .= ' AND civicrm_uf_group.is_active = 1
1689 WHERE civicrm_uf_join.module = %2';
1690 $p[2] = [$moduleName, 'String'];
1693 // add permissioning for profiles only if not registration
1694 if (!$skipPermission) {
1695 $permissionClause = CRM_Core_Permission
::ufGroupClause($op, 'civicrm_uf_group.');
1696 if (strpos($queryString, 'WHERE') !== FALSE) {
1697 $queryString .= " AND $permissionClause ";
1700 $queryString .= " $permissionClause ";
1704 $queryString .= ' ORDER BY civicrm_uf_join.weight, civicrm_uf_group.title';
1705 $dao = CRM_Core_DAO
::executeQuery($queryString, $p);
1708 while ($dao->fetch()) {
1709 //skip mix profiles in user Registration / User Account
1710 if (($moduleName === 'User Registration' ||
$moduleName === 'User Account') &&
1711 CRM_Core_BAO_UFField
::checkProfileType($dao->id
)
1715 foreach ($selectFields as $key => $field) {
1716 if ($field === 'id') {
1719 $ufGroups[$dao->id
][$field] = $dao->$field;
1723 // Allow other modules to alter/override the UFGroups.
1724 CRM_Utils_Hook
::buildUFGroupsForModule($moduleName, $ufGroups);
1730 * Filter ufgroups based on logged in user contact type.
1732 * @param int $ufGroupId
1733 * Uf group id (profile id).
1734 * @param int $contactID
1739 public static function filterUFGroups($ufGroupId, $contactID = NULL) {
1741 $session = CRM_Core_Session
::singleton();
1742 $contactID = $session->get('userID');
1746 //get the contact type
1747 $contactType = CRM_Contact_BAO_Contact
::getContactType($contactID);
1749 //match if exixting contact type is same as profile contact type
1750 $profileType = CRM_Core_BAO_UFField
::getProfileType($ufGroupId);
1752 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
1753 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
1755 //in some cases getBasicType() returns a cached array instead of string. Example: array ('sponsor' => 'organization')
1756 if (is_array($profileType)) {
1757 $profileType = array_shift($profileType);
1761 //allow special mix profiles for Contribution and Participant
1762 $specialProfiles = ['Contribution', 'Participant', 'Membership'];
1764 if (in_array($profileType, $specialProfiles)) {
1768 if (($contactType == $profileType) ||
$profileType == 'Contact') {
1777 * Add profile field to a form.
1779 * @param CRM_Core_Form $form
1780 * @param array $field
1784 * @param int $contactId
1785 * @param bool $online
1786 * @param string $usedFor
1787 * For building up prefixed fieldname for special cases (e.g. onBehalf, Honor).
1788 * @param int $rowNumber
1789 * @param string $prefix
1793 public static function buildProfile(
1803 $defaultValues = [];
1804 $fieldName = $field['name'];
1805 $title = $field['title'];
1806 $attributes = $field['attributes'];
1807 $rule = $field['rule'];
1808 $view = $field['is_view'];
1809 $required = ($mode == CRM_Profile_Form
::MODE_SEARCH
) ?
FALSE : $field['is_required'];
1810 $search = $mode == CRM_Profile_Form
::MODE_SEARCH
;
1811 $isShared = CRM_Utils_Array
::value('is_shared', $field, 0);
1813 // do not display view fields in drupal registration form
1815 if ($view && $mode == CRM_Profile_Form
::MODE_REGISTER
) {
1819 if ($usedFor == 'onbehalf') {
1820 $name = "onbehalf[$fieldName]";
1822 elseif ($usedFor == 'honor') {
1823 $name = "honor[$fieldName]";
1825 elseif ($contactId && !$online) {
1826 $name = "field[$contactId][$fieldName]";
1828 elseif ($rowNumber) {
1829 $name = "field[$rowNumber][$fieldName]";
1831 elseif (!empty($prefix)) {
1832 $name = $prefix . "[$fieldName]";
1838 $selectAttributes = ['class' => 'crm-select2', 'placeholder' => TRUE];
1840 if ($fieldName == 'image_URL' && $mode == CRM_Profile_Form
::MODE_EDIT
) {
1841 $deleteExtra = json_encode(ts('Are you sure you want to delete contact image.'));
1843 CRM_Core_Action
::DELETE
=> [
1844 'name' => ts('Delete Contact Image'),
1845 'url' => 'civicrm/contact/image',
1846 'qs' => 'reset=1&id=%%id%%&gid=%%gid%%&action=delete',
1847 'extra' => 'onclick = "' . htmlspecialchars("if (confirm($deleteExtra)) this.href+='&confirmed=1'; else return false;") . '"',
1850 $deleteURL = CRM_Core_Action
::formLink($deleteURL,
1851 CRM_Core_Action
::DELETE
,
1853 'id' => $form->get('id'),
1854 'gid' => $form->get('gid'),
1858 'contact.profileimage.delete',
1862 $form->assign('deleteURL', $deleteURL);
1864 $addressOptions = CRM_Core_BAO_Setting
::valueOptions(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
1865 'address_options', TRUE, NULL, TRUE
1868 if (substr($fieldName, 0, 14) === 'state_province') {
1869 $form->addChainSelect($name, ['label' => $title, 'required' => $required]);
1870 $config = CRM_Core_Config
::singleton();
1871 if (!in_array($mode, [CRM_Profile_Form
::MODE_EDIT
, CRM_Profile_Form
::MODE_SEARCH
]) &&
1872 $config->defaultContactStateProvince
1874 $defaultValues[$name] = $config->defaultContactStateProvince
;
1875 $form->setDefaults($defaultValues);
1878 elseif (substr($fieldName, 0, 7) === 'country') {
1879 $form->add('select', $name, $title, CRM_Core_PseudoConstant
::country(), $required, $selectAttributes);
1880 $config = CRM_Core_Config
::singleton();
1881 if (!in_array($mode, [CRM_Profile_Form
::MODE_EDIT
, CRM_Profile_Form
::MODE_SEARCH
]) &&
1882 $config->defaultContactCountry
1884 $defaultValues[$name] = $config->defaultContactCountry
;
1885 $form->setDefaults($defaultValues);
1888 elseif (substr($fieldName, 0, 6) === 'county') {
1889 if ($addressOptions['county']) {
1890 $form->addChainSelect($name, ['label' => $title, 'required' => $required]);
1893 elseif (substr($fieldName, 0, 9) === 'image_URL') {
1894 $form->add('file', $name, $title, $attributes, $required);
1895 $form->addUploadElement($name);
1897 elseif (substr($fieldName, 0, 2) === 'im') {
1898 $form->add('text', $name, $title, $attributes, $required);
1901 if (substr($name, -1) === ']') {
1902 $providerName = substr($name, 0, -1) . '-provider_id]';
1904 $form->add('select', $providerName, NULL,
1905 CRM_Core_PseudoConstant
::get('CRM_Core_DAO_IM', 'provider_id'), $required
1909 $form->add('select', $name . '-provider_id', $title,
1910 CRM_Core_PseudoConstant
::get('CRM_Core_DAO_IM', 'provider_id'), $required
1914 if ($view && $mode != CRM_Profile_Form
::MODE_SEARCH
) {
1915 $form->freeze($name . '-provider_id');
1919 elseif (CRM_Utils_Array
::value('name', $field) == 'membership_type') {
1920 list($orgInfo, $types) = CRM_Member_BAO_MembershipType
::getMembershipTypeInfo();
1921 $sel = &$form->addElement('hierselect', $name, $title);
1922 $select = ['' => ts('- select membership type -')];
1923 if (count($orgInfo) == 1 && $field['is_required']) {
1924 // we only have one org - so we should default to it. Not sure about defaulting to first type
1925 // as it could be missed - so adding a select
1926 // however, possibly that is more similar to the membership form
1927 if (count($types[1]) > 1) {
1928 $types[1] = $select +
$types[1];
1932 $orgInfo = $select +
$orgInfo;
1934 $sel->setOptions([$orgInfo, $types]);
1936 elseif (CRM_Utils_Array
::value('name', $field) == 'membership_status') {
1937 $form->add('select', $name, $title,
1938 CRM_Member_PseudoConstant
::membershipStatus(NULL, NULL, 'label'), $required
1941 elseif (in_array($fieldName, ['gender_id', 'communication_style_id'])) {
1943 $pseudoValues = CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', $fieldName);
1944 $form->addRadio($name, ts('%1', [1 => $title]), $pseudoValues, ['allowClear' => !$required], NULL, $required);
1946 elseif ($fieldName === 'prefix_id' ||
$fieldName === 'suffix_id') {
1947 $form->addSelect($name, [
1949 'entity' => 'contact',
1950 'field' => $fieldName,
1952 'placeholder' => '',
1955 elseif ($fieldName === 'contact_sub_type') {
1956 $gId = $form->get('gid') ?
$form->get('gid') : CRM_Utils_Array
::value('group_id', $field);
1957 if ($usedFor == 'onbehalf') {
1958 $profileType = 'Organization';
1960 elseif ($usedFor == 'honor') {
1961 $profileType = CRM_Core_BAO_UFField
::getProfileType($form->_params
['honoree_profile_id']);
1964 $profileType = $gId ? CRM_Core_BAO_UFField
::getProfileType($gId) : NULL;
1965 if ($profileType == 'Contact') {
1966 $profileType = 'Individual';
1970 $setSubtype = FALSE;
1971 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
1972 $setSubtype = $profileType;
1973 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
1976 $subtypes = $profileType ? CRM_Contact_BAO_ContactType
::subTypePairs($profileType) : [];
1980 $subtypeList[$setSubtype] = $subtypes[$setSubtype];
1983 $subtypeList = $subtypes;
1986 $form->add('select', $name, $title, $subtypeList, $required, ['class' => 'crm-select2', 'multiple' => TRUE]);
1988 elseif (in_array($fieldName, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
1989 // Get contact type for greeting selector
1990 $gId = $form->get('gid') ?
: CRM_Utils_Array
::value('group_id', $field);
1991 $profileType = CRM_Core_BAO_UFField
::getProfileType($gId, TRUE, FALSE, TRUE);
1993 if (!$profileType ||
in_array($profileType, ['Contact', 'Contribution', 'Participant', 'Membership'])) {
1994 $profileType = ($profileType == 'Contact' && $form->get('id')) ? CRM_Contact_BAO_Contact
::getContactType($form->get('id')) : 'Individual';
1996 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
1997 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
2000 'contact_type' => $profileType,
2001 'greeting_type' => $fieldName,
2003 $form->add('select', $name, $title, CRM_Core_PseudoConstant
::greeting($greeting), $required, ['placeholder' => TRUE]);
2004 // add custom greeting element
2005 $form->add('text', $fieldName . '_custom', ts('Custom %1', [1 => ucwords(str_replace('_', ' ', $fieldName))]),
2009 elseif ($fieldName === 'preferred_communication_method') {
2010 $communicationFields = CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'preferred_communication_method');
2011 foreach ($communicationFields as $key => $var) {
2015 $communicationOptions[] = $form->createElement('checkbox', $key, NULL, $var);
2017 $form->addGroup($communicationOptions, $name, $title, '<br/>');
2019 elseif ($fieldName === 'preferred_mail_format') {
2020 $form->add('select', $name, $title, CRM_Core_SelectValues
::pmf());
2022 elseif ($fieldName === 'preferred_language') {
2023 $form->add('select', $name, $title, CRM_Contact_BAO_Contact
::buildOptions('preferred_language'), $required, ['placeholder' => TRUE]);
2025 elseif ($fieldName == 'external_identifier') {
2026 $form->add('text', $name, $title, $attributes, $required);
2027 $contID = $contactId;
2029 $contID = $form->get('id');
2031 $form->addRule($name,
2032 ts('External ID already exists in Database.'),
2034 ['CRM_Contact_DAO_Contact', $contID, 'external_identifier']
2037 elseif ($fieldName === 'group') {
2038 CRM_Contact_Form_Edit_TagsAndGroups
::buildQuickForm($form, $contactId,
2039 CRM_Contact_Form_Edit_TagsAndGroups
::GROUP
,
2041 $title, NULL, $name, 'checkbox', TRUE
2044 elseif ($fieldName === 'tag') {
2045 CRM_Contact_Form_Edit_TagsAndGroups
::buildQuickForm($form, $contactId,
2046 CRM_Contact_Form_Edit_TagsAndGroups
::TAG
,
2051 elseif (substr($fieldName, 0, 4) === 'url-') {
2052 $form->add('text', $name, $title, CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Website', 'url'), $required);
2053 $form->addRule($name, ts('Enter a valid web address beginning with \'http://\' or \'https://\'.'), 'url');
2055 // Note should be rendered as textarea
2056 elseif (substr($fieldName, -4) == 'note') {
2057 $form->add('textarea', $name, $title, $attributes, $required);
2059 elseif (substr($fieldName, 0, 6) === 'custom') {
2060 $customFieldID = CRM_Core_BAO_CustomField
::getKeyID($fieldName);
2061 if ($customFieldID) {
2062 CRM_Core_BAO_CustomField
::addQuickFormElement($form, $name, $customFieldID, $required, $search, $title);
2065 elseif (substr($fieldName, 0, 14) === 'address_custom') {
2066 list($fName, $locTypeId) = CRM_Utils_System
::explode('-', $fieldName, 2);
2067 $customFieldID = CRM_Core_BAO_CustomField
::getKeyID(substr($fName, 8));
2068 if ($customFieldID) {
2069 CRM_Core_BAO_CustomField
::addQuickFormElement($form, $name, $customFieldID, $required, $search, $title);
2072 elseif ($fieldName == 'send_receipt') {
2073 $form->addElement('checkbox', $name, $title);
2075 elseif ($fieldName == 'soft_credit') {
2076 $form->addEntityRef("soft_credit_contact_id[$rowNumber]", ts('Soft Credit To'), ['create' => TRUE]);
2077 $form->addMoney("soft_credit_amount[{$rowNumber}]", ts('Amount'), FALSE, NULL, FALSE);
2079 elseif ($fieldName === 'product_name') {
2080 list($products, $options) = CRM_Contribute_BAO_Premium
::getPremiumProductInfo();
2081 $sel = &$form->addElement('hierselect', $name, $title);
2082 $products = ['0' => ts('- select %1 -', [1 => $title])] +
$products;
2083 $sel->setOptions([$products, $options]);
2085 elseif ($fieldName === 'payment_instrument') {
2086 $form->add('select', $name, $title,
2087 CRM_Contribute_PseudoConstant
::paymentInstrument(), $required, ['placeholder' => TRUE]);
2089 elseif ($fieldName === 'financial_type') {
2090 $form->add('select', $name, $title,
2091 CRM_Contribute_PseudoConstant
::financialType(), $required, ['placeholder' => TRUE]
2094 elseif ($fieldName === 'contribution_status_id') {
2095 $contributionStatuses = CRM_Contribute_BAO_Contribution_Utils
::getPendingCompleteFailedAndCancelledStatuses();
2097 $form->add('select', $name, $title,
2098 $contributionStatuses, $required, ['placeholder' => TRUE]
2101 elseif ($fieldName === 'soft_credit_type') {
2102 $name = "soft_credit_type[$rowNumber]";
2103 $form->add('select', $name, $title,
2104 CRM_Core_OptionGroup
::values("soft_credit_type"), ['placeholder' => TRUE]
2106 //CRM-15350: choose SCT field default value as 'Gift' for membership use
2107 //else (for contribution), use configured SCT default value
2108 $SCTDefaultValue = CRM_Core_OptionGroup
::getDefaultValue("soft_credit_type");
2109 if ($field['field_type'] == 'Membership') {
2110 $SCTDefaultValue = CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_ContributionSoft', 'soft_credit_type_id', 'gift');
2112 $form->addElement('hidden', 'sct_default_id', $SCTDefaultValue, ['id' => 'sct_default_id']);
2114 elseif ($fieldName == 'contribution_soft_credit_pcp_id') {
2115 CRM_Contribute_Form_SoftCredit
::addPCPFields($form, "[$rowNumber]");
2117 elseif ($fieldName == 'currency') {
2118 $form->addCurrency($name, $title, $required, NULL, FALSE, FALSE);
2120 elseif ($fieldName == 'contribution_page_id') {
2121 $form->add('select', $name, $title,
2122 CRM_Contribute_PseudoConstant
::contributionPage(), $required, [
2124 'placeholder' => TRUE,
2128 elseif ($fieldName == 'activity_status_id') {
2129 $form->add('select', $name, $title,
2130 CRM_Core_PseudoConstant
::activityStatus(), $required, ['placeholder' => TRUE]
2133 elseif ($fieldName == 'activity_engagement_level') {
2134 $form->add('select', $name, $title,
2135 CRM_Campaign_PseudoConstant
::engagementLevel(), $required, ['placeholder' => TRUE]
2138 elseif ($fieldName == 'participant_status') {
2140 if ($online == TRUE) {
2141 $cond = 'visibility_id = 1';
2143 $form->add('select', $name, $title,
2144 CRM_Event_PseudoConstant
::participantStatus(NULL, $cond, 'label'), $required, ['placeholder' => TRUE]
2147 elseif ($fieldName == 'participant_role') {
2148 if (!empty($field['is_multiple'])) {
2149 $form->addCheckBox($name, $title, CRM_Event_PseudoConstant
::participantRole(), NULL, NULL, NULL, NULL, ' ', TRUE);
2152 $form->add('select', $name, $title,
2153 CRM_Event_PseudoConstant
::participantRole(), $required, ['placeholder' => TRUE]
2157 elseif ($fieldName == 'world_region') {
2158 $form->add('select', $name, $title, CRM_Core_PseudoConstant
::worldRegion(), $required, $selectAttributes);
2160 elseif ($fieldName == 'signature_html') {
2161 $form->add('wysiwyg', $name, $title, CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Email', $fieldName));
2163 elseif ($fieldName == 'signature_text') {
2164 $form->add('textarea', $name, $title, CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Email', $fieldName));
2166 elseif (substr($fieldName, -11) == 'campaign_id') {
2167 if (CRM_Campaign_BAO_Campaign
::isCampaignEnable()) {
2168 $campaigns = CRM_Campaign_BAO_Campaign
::getCampaigns(CRM_Utils_Array
::value($contactId,
2169 $form->_componentCampaigns
2171 $form->add('select', $name, $title,
2172 $campaigns, $required,
2174 'class' => 'crm-select2 big',
2175 'placeholder' => TRUE,
2180 elseif ($fieldName == 'activity_details') {
2181 $form->add('wysiwyg', $name, $title, ['rows' => 4, 'cols' => 60], $required);
2183 elseif ($fieldName == 'activity_duration') {
2184 $form->add('text', $name, $title, $attributes, $required);
2185 $form->addRule($name, ts('Please enter the duration as number of minutes (integers only).'), 'positiveInteger');
2187 elseif ($fieldName == 'case_status') {
2188 $form->add('select', $name, $title,
2189 CRM_Case_BAO_Case
::buildOptions('case_status_id', 'create'),
2190 $required, ['placeholder' => TRUE]
2194 if (substr($fieldName, 0, 3) === 'is_' or substr($fieldName, 0, 7) === 'do_not_') {
2195 $form->add('advcheckbox', $name, $title, $attributes, $required);
2197 elseif (CRM_Utils_Array
::value('html_type', $field) === 'Select Date') {
2198 $extra = isset($field['datepicker']) ?
$field['datepicker']['extra'] : CRM_Utils_Date
::getDatePickerExtra($field);
2199 $attributes = isset($field['datepicker']) ?
$field['datepicker']['attributes'] : CRM_Utils_Date
::getDatePickerAttributes($field);
2200 $form->add('datepicker', $name, $title, $attributes, $required, $extra);
2203 $form->add('text', $name, $title, $attributes, $required);
2207 static $hiddenSubtype = FALSE;
2208 if (!$hiddenSubtype && CRM_Contact_BAO_ContactType
::isaSubType($field['field_type'])) {
2209 // In registration mode params are submitted via POST and we don't have any clue
2210 // about profile-id or the profile-type (which could be a subtype)
2211 // To generalize the behavior and simplify the process,
2212 // lets always add the hidden
2213 //subtype value if there is any, and we won't have to
2214 // compute it while processing.
2216 $form->addElement('hidden', $usedFor . '[contact_sub_type]', $field['field_type']);
2219 $form->addElement('hidden', 'contact_sub_type_hidden', $field['field_type']);
2221 $hiddenSubtype = TRUE;
2224 if (($view && $mode != CRM_Profile_Form
::MODE_SEARCH
) ||
$isShared) {
2225 $form->freeze($name);
2229 if (in_array($fieldName, [
2230 'non_deductible_amount',
2235 $form->addRule($name, ts('Please enter a valid amount.'), 'money');
2238 if (!($rule == 'email' && $mode == CRM_Profile_Form
::MODE_SEARCH
)) {
2239 $form->addRule($name, ts('Please enter a valid %1', [1 => $title]), $rule);
2245 * Set profile defaults.
2247 * @param int $contactId
2249 * @param array $fields
2250 * Associative array of fields.
2251 * @param array $defaults
2253 * @param bool $singleProfile
2254 * True for single profile else false(Update multiple items).
2255 * @param int $componentId
2256 * Id for specific components like contribute, event etc.
2257 * @param null $component
2259 public static function setProfileDefaults(
2260 $contactId, &$fields, &$defaults,
2261 $singleProfile = TRUE, $componentId = NULL, $component = NULL
2263 if (!$componentId) {
2264 //get the contact details
2265 $contactDetails = CRM_Contact_BAO_Contact
::getHierContactDetails($contactId, $fields);
2266 $details = $contactDetails[$contactId] ??
NULL;
2267 $multipleFields = ['website' => 'url'];
2269 //start of code to set the default values
2270 foreach ($fields as $name => $field) {
2271 // skip pseudo fields
2272 if (substr($name, 0, 9) == 'phone_ext') {
2276 //set the field name depending upon the profile mode(single/multiple)
2277 if ($singleProfile) {
2281 $fldName = "field[$contactId][$name]";
2284 if ($name == 'group') {
2285 CRM_Contact_Form_Edit_TagsAndGroups
::setDefaults($contactId, $defaults, CRM_Contact_Form_Edit_TagsAndGroups
::GROUP
, $fldName);
2287 if ($name == 'tag') {
2288 CRM_Contact_Form_Edit_TagsAndGroups
::setDefaults($contactId, $defaults, CRM_Contact_Form_Edit_TagsAndGroups
::TAG
, $fldName);
2291 if (!empty($details[$name]) ||
isset($details[$name])) {
2292 //to handle custom data (checkbox) to be written
2293 // to handle birth/deceased date, greeting_type and few other fields
2294 if (in_array($name, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
2295 $defaults[$fldName] = $details[$name . '_id'];
2296 $defaults[$name . '_custom'] = $details[$name . '_custom'];
2298 elseif ($name == 'preferred_communication_method') {
2299 $v = $details[$name];
2300 if (!is_array($details[$name])) {
2301 $v = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $v);
2303 foreach ($v as $item) {
2305 $defaults[$fldName . "[$item]"] = 1;
2309 elseif ($name == 'contact_sub_type') {
2310 $defaults[$fldName] = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, trim($details[$name], CRM_Core_DAO
::VALUE_SEPARATOR
));
2312 elseif ($name == 'world_region') {
2313 $defaults[$fldName] = $details['worldregion_id'];
2315 elseif (CRM_Core_BAO_CustomField
::getKeyID($name)) {
2316 $defaults[$fldName] = self
::formatCustomValue($field, $details[$name]);
2317 if (!$singleProfile && $field['html_type'] === 'CheckBox') {
2318 // For batch update profile there needs to be a key lik
2319 // $defaults['field[166]['custom_8'][2]'] => 1 where
2320 // 166 is the conntact id, 8 is the field id and 2 is the checkbox option.
2321 foreach ($defaults[$fldName] as $itemKey => $itemValue) {
2322 $defaults[$fldName . '[' . $itemKey . ']'] = $itemValue;
2327 $defaults[$fldName] = $details[$name];
2331 $blocks = ['email', 'phone', 'im', 'openid'];
2332 list($fieldName, $locTypeId, $phoneTypeId) = CRM_Utils_System
::explode('-', $name, 3);
2333 if (!in_array($fieldName, $multipleFields)) {
2334 if (is_array($details)) {
2335 foreach ($details as $key => $value) {
2336 // when we fixed CRM-5319 - get primary loc
2337 // type as per loc field and removed below code.
2338 $primaryLocationType = FALSE;
2339 if ($locTypeId == 'Primary') {
2340 if (is_array($value) && array_key_exists($fieldName, $value)) {
2341 $primaryLocationType = TRUE;
2342 if (in_array($fieldName, $blocks)) {
2343 $locTypeId = CRM_Contact_BAO_Contact
::getPrimaryLocationType($contactId, FALSE, $fieldName);
2346 $locTypeId = CRM_Contact_BAO_Contact
::getPrimaryLocationType($contactId, FALSE, 'address');
2351 // fixed for CRM-665
2352 if (is_numeric($locTypeId)) {
2353 if ($primaryLocationType ||
$locTypeId == CRM_Utils_Array
::value('location_type_id', $value)) {
2354 if (!empty($value[$fieldName])) {
2355 //to handle stateprovince and country
2356 if ($fieldName == 'state_province') {
2357 $defaults[$fldName] = $value['state_province_id'];
2359 elseif ($fieldName == 'county') {
2360 $defaults[$fldName] = $value['county_id'];
2362 elseif ($fieldName == 'country') {
2363 if (!isset($value['country_id']) ||
!$value['country_id']) {
2364 $config = CRM_Core_Config
::singleton();
2365 if ($config->defaultContactCountry
) {
2366 $defaults[$fldName] = $config->defaultContactCountry
;
2370 $defaults[$fldName] = $value['country_id'];
2373 elseif ($fieldName == 'phone') {
2375 if (isset($value['phone'][$phoneTypeId])) {
2376 $defaults[$fldName] = $value['phone'][$phoneTypeId];
2378 if (isset($value['phone_ext'][$phoneTypeId])) {
2379 $defaults[str_replace('phone', 'phone_ext', $fldName)] = $value['phone_ext'][$phoneTypeId];
2383 $phoneDefault = $value['phone'] ??
NULL;
2385 if (!is_array($phoneDefault)) {
2386 $defaults[$fldName] = $phoneDefault;
2390 elseif ($fieldName == 'email') {
2391 //adding the first email (currently we don't support multiple emails of same location type)
2392 $defaults[$fldName] = $value['email'];
2394 elseif ($fieldName == 'im') {
2395 //adding the first im (currently we don't support multiple ims of same location type)
2396 $defaults[$fldName] = $value['im'];
2397 $defaults[$fldName . '-provider_id'] = $value['im_provider_id'];
2400 $defaults[$fldName] = $value[$fieldName];
2403 elseif (strpos($fieldName, 'address_custom') === 0 && !empty($value[substr($fieldName, 8)])) {
2404 $defaults[$fldName] = self
::formatCustomValue($field, $value[substr($fieldName, 8)]);
2408 elseif (strpos($fieldName, 'address_custom') === 0 && !empty($value[substr($fieldName, 8)])) {
2409 $defaults[$fldName] = self
::formatCustomValue($field, $value[substr($fieldName, 8)]);
2415 if (is_array($details)) {
2416 if ($fieldName === 'url'
2417 && !empty($details['website'])
2418 && !empty($details['website'][$locTypeId])
2420 $defaults[$fldName] = $details['website'][$locTypeId]['url'] ??
NULL;
2428 // Handling Contribution Part of the batch profile
2429 if (CRM_Core_Permission
::access('CiviContribute') && $component == 'Contribute') {
2430 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2433 // Handling Event Participation Part of the batch profile
2434 if (CRM_Core_Permission
::access('CiviEvent') && $component == 'Event') {
2435 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2438 // Handling membership Part of the batch profile
2439 if (CRM_Core_Permission
::access('CiviMember') && $component == 'Membership') {
2440 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2443 // Handling Activity Part of the batch profile
2444 if ($component == 'Activity') {
2445 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2448 // Handling Case Part of the batch profile
2449 if (CRM_Core_Permission
::access('CiviCase') && $component == 'Case') {
2450 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2455 * Get profiles by type eg: pure Individual etc
2457 * @param array $types
2458 * Associative array of types eg: types('Individual').
2459 * @param bool $onlyPure
2460 * True if only pure profiles are required.
2463 * associative array of profiles
2465 public static function getProfiles($types, $onlyPure = FALSE) {
2467 $ufGroups = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_UFField', 'uf_group_id');
2469 CRM_Utils_Hook
::aclGroup(CRM_Core_Permission
::ADMIN
, NULL, 'civicrm_uf_group', $ufGroups, $ufGroups);
2471 // Exclude Batch Data Entry profiles - CRM-10901
2472 $batchProfiles = CRM_Core_BAO_UFGroup
::getBatchProfiles();
2474 foreach ($ufGroups as $id => $title) {
2475 $ptype = CRM_Core_BAO_UFField
::getProfileType($id, FALSE, $onlyPure);
2476 if (in_array($ptype, $types) && !array_key_exists($id, $batchProfiles)) {
2477 $profiles[$id] = $title;
2484 * Check whether a profile is valid combination of
2485 * required and/or optional profile types
2487 * @param array $required
2488 * Array of types those are required.
2489 * @param array $optional
2490 * Array of types those are optional.
2493 * associative array of profiles
2495 public static function getValidProfiles($required, $optional = NULL) {
2496 if (!is_array($required) ||
empty($required)) {
2501 $ufGroups = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_UFField', 'uf_group_id');
2503 CRM_Utils_Hook
::aclGroup(CRM_Core_Permission
::ADMIN
, NULL, 'civicrm_uf_group', $ufGroups, $ufGroups);
2505 foreach ($ufGroups as $id => $title) {
2506 $type = CRM_Core_BAO_UFField
::checkValidProfileType($id, $required, $optional);
2508 $profiles[$id] = $title;
2516 * Check whether a profile is valid combination of
2517 * required profile fields
2519 * @param array $ufId
2520 * Integer id of the profile.
2521 * @param array $required
2522 * Array of fields those are required in the profile.
2525 * associative array of profiles
2527 public static function checkValidProfile($ufId, $required = NULL) {
2528 $validProfile = FALSE;
2530 return $validProfile;
2533 if (!CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $ufId, 'is_active')) {
2534 return $validProfile;
2537 $profileFields = self
::getFields($ufId, FALSE, CRM_Core_Action
::VIEW
, NULL,
2538 NULL, FALSE, NULL, FALSE, NULL,
2539 CRM_Core_Permission
::CREATE
, NULL
2543 if (!empty($profileFields)) {
2544 $fields = array_keys($profileFields);
2545 foreach ($fields as $val) {
2546 foreach ($required as $key => $field) {
2547 if (strpos($val, $field) === 0) {
2548 unset($required[$key]);
2553 $validProfile = (empty($required));
2556 return $validProfile;
2560 * Get default value for Register.
2562 * @param array $fields
2563 * @param array $defaults
2567 public static function setRegisterDefaults(&$fields, &$defaults) {
2568 $config = CRM_Core_Config
::singleton();
2569 foreach ($fields as $name => $field) {
2570 if (substr($name, 0, 8) == 'country-') {
2571 if (!empty($config->defaultContactCountry
)) {
2572 $defaults[$name] = $config->defaultContactCountry
;
2575 elseif (substr($name, 0, 15) == 'state_province-') {
2576 if (!empty($config->defaultContactStateProvince
)) {
2577 $defaults[$name] = $config->defaultContactStateProvince
;
2585 * make a copy of a profile, including
2586 * all the fields in the profile
2589 * The profile id to copy.
2591 * @return \CRM_Core_DAO
2593 public static function copy($id) {
2594 $maxId = CRM_Core_DAO
::singleValueQuery("SELECT max(id) FROM civicrm_uf_group");
2596 $title = ts('[Copy id %1]', [1 => $maxId +
1]);
2599 'title' => ' ' . $title,
2600 'name' => '__Copy_id_' . ($maxId +
1) . '_',
2604 $copy = CRM_Core_DAO
::copyGeneric('CRM_Core_DAO_UFGroup',
2610 if ($pos = strrpos($copy->name
, "_{$id}")) {
2611 $copy->name
= substr_replace($copy->name
, '', $pos);
2613 $copy->name
= CRM_Utils_String
::munge($copy->name
, '_', 56) . "_{$copy->id}";
2616 $copyUFJoin = CRM_Core_DAO
::copyGeneric('CRM_Core_DAO_UFJoin',
2617 ['uf_group_id' => $id],
2618 ['uf_group_id' => $copy->id
],
2623 $copyUFField = CRM_Core_DAO
::copyGeneric('CRM_Core_BAO_UFField',
2624 ['uf_group_id' => $id],
2625 ['uf_group_id' => $copy->id
]
2628 $maxWeight = CRM_Utils_Weight
::getMax('CRM_Core_DAO_UFJoin', NULL, 'weight');
2632 UPDATE civicrm_uf_join
2634 WHERE uf_group_id = %2
2635 AND ( entity_id IS NULL OR entity_id <= 0 )
2638 1 => [$maxWeight +
1, 'Integer'],
2639 2 => [$copy->id
, 'Integer'],
2641 CRM_Core_DAO
::executeQuery($query, $p);
2642 if ($copy->is_reserved
) {
2643 $query = "UPDATE civicrm_uf_group SET is_reserved = 0 WHERE id = %1";
2644 $params = [1 => [$copy->id
, 'Integer']];
2645 CRM_Core_DAO
::executeQuery($query, $params);
2647 CRM_Utils_Hook
::copy('UFGroup', $copy);
2653 * Process that send notification e-mails
2655 * @param int $contactID
2657 * @param array $values
2658 * Associative array of name/value pair.
2660 public static function commonSendMail($contactID, &$values) {
2661 if (!$contactID ||
!$values) {
2665 $template = CRM_Core_Smarty
::singleton();
2667 $displayName = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
2672 self
::profileDisplay($values['id'], $values['values'], $template);
2673 $emailList = explode(',', $values['email']);
2675 $contactLink = CRM_Utils_System
::url('civicrm/contact/view',
2676 "reset=1&cid=$contactID",
2677 TRUE, NULL, FALSE, FALSE, TRUE
2680 //get the default domain email address.
2681 list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain
::getNameAndEmail();
2683 if (!$domainEmailAddress ||
$domainEmailAddress == 'info@EXAMPLE.ORG') {
2684 $fixUrl = CRM_Utils_System
::url('civicrm/admin/domain', 'action=update&reset=1');
2685 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]));
2688 foreach ($emailList as $emailTo) {
2689 // FIXME: take the below out of the foreach loop
2690 CRM_Core_BAO_MessageTemplate
::sendTemplate(
2692 'groupName' => 'msg_tpl_workflow_uf',
2693 'valueName' => 'uf_notify',
2694 'contactId' => $contactID,
2696 'displayName' => $displayName,
2697 'currentDate' => date('r'),
2698 'contactLink' => $contactLink,
2700 'from' => "$domainEmailName <$domainEmailAddress>",
2701 'toEmail' => $emailTo,
2708 * Given a contact id and a group id, returns the field values from the db
2709 * for this group and notify email only if group's notify field is
2710 * set and field values are not empty
2716 * @param array $params
2717 * @param bool $skipCheck
2721 public static function checkFieldsEmptyValues($gid, $cid, $params, $skipCheck = FALSE) {
2723 if (CRM_Core_BAO_UFGroup
::filterUFGroups($gid, $cid) ||
$skipCheck) {
2725 $fields = CRM_Core_BAO_UFGroup
::getFields($gid, FALSE, CRM_Core_Action
::VIEW
);
2726 CRM_Core_BAO_UFGroup
::getValues($cid, $fields, $values, FALSE, $params, TRUE);
2728 $email = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'notify');
2730 if (!empty($values) &&
2735 'values' => $values,
2746 * Assign uf fields to template.
2750 * @param array $values
2751 * @param CRM_Core_Smarty $template
2753 public static function profileDisplay($gid, $values, $template) {
2754 $groupTitle = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'title');
2755 $template->assign('grouptitle', $groupTitle);
2756 if (count($values)) {
2757 $template->assign('values', $values);
2762 * Format fields for dupe Contact Matching.
2764 * @param array $params
2766 * @param int $contactId
2769 * associated formatted array
2771 public static function formatFields($params, $contactId = NULL) {
2773 // get the primary location type id and email
2774 list($name, $primaryEmail, $primaryLocationType) = CRM_Contact_BAO_Contact_Location
::getEmailDetails($contactId);
2777 $defaultLocationType = CRM_Core_BAO_LocationType
::getDefault();
2778 $primaryLocationType = $defaultLocationType->id
;
2784 $primaryLocation = 0;
2785 foreach ($params as $key => $value) {
2786 list($fieldName, $locTypeId, $phoneTypeId) = explode('-', $key);
2788 if ($locTypeId == 'Primary') {
2789 $locTypeId = $primaryLocationType;
2792 if (is_numeric($locTypeId)) {
2793 if (!in_array($locTypeId, $locationType)) {
2794 $locationType[$count] = $locTypeId;
2797 $loc = CRM_Utils_Array
::key($locTypeId, $locationType);
2799 $data['location'][$loc]['location_type_id'] = $locTypeId;
2801 // if we are getting in a new primary email, dont overwrite the new one
2802 if ($locTypeId == $primaryLocationType) {
2803 if (!empty($params['email-' . $primaryLocationType])) {
2804 $data['location'][$loc]['email'][$loc]['email'] = $fields['email-' . $primaryLocationType];
2806 elseif (isset($primaryEmail)) {
2807 $data['location'][$loc]['email'][$loc]['email'] = $primaryEmail;
2813 $data['location'][$loc]['is_primary'] = 1;
2815 if ($fieldName == 'phone') {
2817 $data['location'][$loc]['phone'][$loc]['phone_type_id'] = $phoneTypeId;
2820 $data['location'][$loc]['phone'][$loc]['phone_type_id'] = '';
2822 $data['location'][$loc]['phone'][$loc]['phone'] = $value;
2824 elseif ($fieldName == 'email') {
2825 $data['location'][$loc]['email'][$loc]['email'] = $value;
2827 elseif ($fieldName == 'im') {
2828 $data['location'][$loc]['im'][$loc]['name'] = $value;
2831 if ($fieldName === 'state_province') {
2832 $data['location'][$loc]['address']['state_province_id'] = $value;
2834 elseif ($fieldName === 'country') {
2835 $data['location'][$loc]['address']['country_id'] = $value;
2838 $data['location'][$loc]['address'][$fieldName] = $value;
2843 // TODO: prefix, suffix and gender translation may no longer be necessary - check inputs
2844 if ($key === 'individual_suffix') {
2845 $data['suffix_id'] = $value;
2847 elseif ($key === 'individual_prefix') {
2848 $data['prefix_id'] = $value;
2850 elseif ($key === 'gender') {
2851 $data['gender_id'] = $value;
2853 elseif (substr($key, 0, 6) === 'custom') {
2854 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) {
2856 if ($customFields[$customFieldID]['html_type'] == 'CheckBox') {
2857 $value = implode(CRM_Core_DAO
::VALUE_SEPARATOR
, array_keys($value));
2859 // fix the date field
2860 if ($customFields[$customFieldID]['data_type'] == 'Date') {
2861 $date = CRM_Utils_Date
::format($value);
2868 $data['custom'][$customFieldID] = [
2871 'extends' => $customFields[$customFieldID]['extends'],
2872 'type' => $customFields[$customFieldID]['data_type'],
2873 'custom_field_id' => $customFieldID,
2877 elseif ($key == 'edit') {
2881 $data[$key] = $value;
2886 if (!$primaryLocation) {
2888 $data['location'][$loc]['email'][$loc]['email'] = $primaryEmail;
2895 * Calculate the profile type 'group_type' as per profile fields.
2899 * @param bool $includeTypeValues
2900 * @param int $ignoreFieldId
2901 * Ignore particular profile field.
2904 * list of calculated group type
2906 public static function calculateGroupType($gId, $includeTypeValues = FALSE, $ignoreFieldId = NULL) {
2907 //get the profile fields.
2908 $ufFields = self
::getFields($gId, FALSE, NULL, NULL, NULL, TRUE, NULL, TRUE);
2909 return self
::_calculateGroupType($ufFields, $includeTypeValues, $ignoreFieldId);
2913 * Calculate the profile type 'group_type' as per profile fields.
2916 * @param bool $includeTypeValues
2917 * @param int $ignoreFieldId
2918 * Ignore perticular profile field.
2921 * list of calculated group type
2923 public static function _calculateGroupType($ufFields, $includeTypeValues = FALSE, $ignoreFieldId = NULL) {
2924 $groupType = $groupTypeValues = $customFieldIds = [];
2925 if (!empty($ufFields)) {
2926 foreach ($ufFields as $fieldName => $fieldValue) {
2927 //ignore field from group type when provided.
2928 //in case of update profile field.
2929 if ($ignoreFieldId && ($ignoreFieldId == $fieldValue['field_id'])) {
2932 if (!in_array($fieldValue['field_type'], $groupType)) {
2933 $groupType[$fieldValue['field_type']] = $fieldValue['field_type'];
2936 if ($includeTypeValues && ($fldId = CRM_Core_BAO_CustomField
::getKeyID($fieldName))) {
2937 $customFieldIds[$fldId] = $fldId;
2942 if (!empty($customFieldIds)) {
2943 $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) . ')';
2945 $customGroups = CRM_Core_DAO
::executeQuery($query);
2946 while ($customGroups->fetch()) {
2947 if (!$customGroups->extends_entity_column_value
) {
2951 $groupTypeName = "{$customGroups->extends}Type";
2952 if ($customGroups->extends == 'Participant' && $customGroups->extends_entity_column_id
) {
2953 $groupTypeName = CRM_Core_PseudoConstant
::getName('CRM_Core_DAO_CustomGroup', 'extends_entity_column_id', $customGroups->extends_entity_column_id
);
2956 foreach (explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $customGroups->extends_entity_column_value
) as $val) {
2958 $groupTypeValues[$groupTypeName][$val] = $val;
2963 if (!empty($groupTypeValues)) {
2964 $groupType = array_merge($groupType, $groupTypeValues);
2972 * Update the profile type 'group_type' as per profile fields including group types and group subtype values.
2973 * Build and store string like: group_type1,group_type2[VALUE_SEPARATOR]group_type1Type:1:2:3,group_type2Type:1:2
2976 * BirthDate + Email Individual,Contact
2977 * BirthDate + Subject Individual,Activity
2978 * BirthDate + Subject + SurveyOnlyField Individual,Activity\0ActivityType:28
2979 * BirthDate + Subject + SurveyOnlyField + PhoneOnlyField (Not allowed)
2980 * BirthDate + SurveyOnlyField Individual,Activity\0ActivityType:28
2981 * BirthDate + Subject + SurveyOrPhoneField Individual,Activity\0ActivityType:2:28
2982 * BirthDate + SurveyOrPhoneField Individual,Activity\0ActivityType:2:28
2983 * BirthDate + SurveyOrPhoneField + SurveyOnlyField Individual,Activity\0ActivityType:2:28
2984 * BirthDate + StudentField + Subject + SurveyOnlyField Individual,Activity,Student\0ActivityType:28
2987 * @param array $groupTypes
2988 * With key having group type names.
2992 public static function updateGroupTypes($gId, $groupTypes = []) {
2993 if (!is_array($groupTypes) ||
!$gId) {
2997 // If empty group types set group_type as 'null'
2998 if (empty($groupTypes)) {
2999 return CRM_Core_DAO
::setFieldValue('CRM_Core_DAO_UFGroup', $gId, 'group_type', 'null');
3002 $componentGroupTypes = ['Contribution', 'Participant', 'Membership', 'Activity', 'Case'];
3003 $validGroupTypes = array_merge([
3008 ], $componentGroupTypes, CRM_Contact_BAO_ContactType
::subTypes());
3010 $gTypes = $gTypeValues = [];
3012 $participantExtends = ['ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
3013 // Get valid group type and group subtypes
3014 foreach ($groupTypes as $groupType => $value) {
3015 if (in_array($groupType, $validGroupTypes) && !in_array($groupType, $gTypes)) {
3016 $gTypes[] = $groupType;
3021 if (in_array($groupType, $participantExtends)) {
3022 $subTypesOf = $groupType;
3024 elseif (strpos($groupType, 'Type') > 0) {
3025 $subTypesOf = substr($groupType, 0, strpos($groupType, 'Type'));
3031 if (!empty($value) &&
3032 (in_array($subTypesOf, $componentGroupTypes) ||
3033 in_array($subTypesOf, $participantExtends)
3036 $gTypeValues[$subTypesOf] = $groupType . ":" . implode(':', $value);
3040 if (empty($gTypes)) {
3044 // Build String to store group types and group subtypes
3045 $groupTypeString = implode(',', $gTypes);
3046 if (!empty($gTypeValues)) {
3047 $groupTypeString .= CRM_Core_DAO
::VALUE_SEPARATOR
. implode(',', $gTypeValues);
3050 return CRM_Core_DAO
::setFieldValue('CRM_Core_DAO_UFGroup', $gId, 'group_type', $groupTypeString);
3054 * Create a "group_type" string.
3056 * @param array $coreTypes
3057 * E.g. array('Individual','Contact','Student').
3058 * @param array $subTypes
3059 * E.g. array('ActivityType' => array(7, 11)).
3060 * @param string $delim
3063 * @throws CRM_Core_Exception
3065 public static function encodeGroupType($coreTypes, $subTypes, $delim = CRM_Core_DAO
::VALUE_SEPARATOR
) {
3066 $groupTypeExpr = '';
3068 $groupTypeExpr .= implode(',', $coreTypes);
3071 //CRM-15427 Allow Multiple subtype filtering
3072 //if (count($subTypes) > 1) {
3073 //throw new CRM_Core_Exception("Multiple subtype filtering is not currently supported by widget.");
3075 foreach ($subTypes as $subType => $subTypeIds) {
3076 $groupTypeExpr .= $delim . $subType . ':' . implode(':', $subTypeIds);
3079 return $groupTypeExpr;
3083 * setDefault component specific profile fields.
3085 * @param array $fields
3087 * @param int $componentId
3089 * @param string $component
3091 * @param array $defaults
3092 * An array of default values.
3094 * @param bool $isStandalone
3096 public static function setComponentDefaults(&$fields, $componentId, $component, &$defaults, $isStandalone = FALSE) {
3097 if (!$componentId ||
3098 !in_array($component, ['Contribute', 'Membership', 'Event', 'Activity', 'Case'])
3103 $componentBAO = $componentSubType = NULL;
3104 switch ($component) {
3106 $componentBAO = 'CRM_Member_BAO_Membership';
3107 $componentBAOName = 'Membership';
3108 $componentSubType = ['membership_type_id'];
3112 $componentBAO = 'CRM_Contribute_BAO_Contribution';
3113 $componentBAOName = 'Contribution';
3114 $componentSubType = ['financial_type_id'];
3118 $componentBAO = 'CRM_Event_BAO_Participant';
3119 $componentBAOName = 'Participant';
3120 $componentSubType = ['role_id', 'event_id', 'event_type_id'];
3124 $componentBAO = 'CRM_Activity_BAO_Activity';
3125 $componentBAOName = 'Activity';
3126 $componentSubType = ['activity_type_id'];
3130 $componentBAO = 'CRM_Case_BAO_Case';
3131 $componentBAOName = 'Case';
3132 $componentSubType = ['case_type_id'];
3137 $params = ['id' => $componentId];
3139 //get the component values.
3140 CRM_Core_DAO
::commonRetrieve($componentBAO, $params, $values);
3141 if ($componentBAOName == 'Participant') {
3142 $values +
= ['event_type_id' => CRM_Core_DAO
::getFieldValue('CRM_Event_DAO_Event', $values['event_id'], 'event_type_id')];
3145 $formattedGroupTree = [];
3147 foreach ($fields as $name => $field) {
3148 $fldName = $isStandalone ?
$name : "field[$componentId][$name]";
3149 if (array_key_exists($name, $values)) {
3150 $defaults[$fldName] = $values[$name];
3152 elseif ($name == 'participant_note') {
3153 $noteDetails = CRM_Core_BAO_Note
::getNote($componentId, 'civicrm_participant');
3154 $defaults[$fldName] = array_pop($noteDetails);
3156 elseif (in_array($name, [
3158 'payment_instrument',
3159 'participant_status',
3162 $defaults[$fldName] = $values["{$name}_id"];
3164 elseif ($name == 'membership_type') {
3165 // since membership_type field is a hierselect -
3166 $defaults[$fldName][0]
3167 = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_MembershipType', $values['membership_type_id'], 'member_of_contact_id', 'id');
3168 $defaults[$fldName][1] = $values['membership_type_id'];
3170 elseif ($name == 'membership_status') {
3171 $defaults[$fldName] = $values['status_id'];
3173 elseif ($name == 'case_status') {
3174 $defaults[$fldName] = $values['case_status_id'];
3176 elseif (CRM_Core_BAO_CustomField
::getKeyID($name, TRUE) !== [NULL, NULL]) {
3177 if (empty($formattedGroupTree)) {
3178 //get the groupTree as per subTypes.
3180 foreach ($componentSubType as $subType) {
3181 $subTree = CRM_Core_BAO_CustomGroup
::getTree($componentBAOName, NULL,
3182 $componentId, 0, $values[$subType]
3184 $groupTree = CRM_Utils_Array
::crmArrayMerge($groupTree, $subTree);
3186 $formattedGroupTree = CRM_Core_BAO_CustomGroup
::formatGroupTree($groupTree, 1);
3187 CRM_Core_BAO_CustomGroup
::setDefaults($formattedGroupTree, $defaults);
3190 //FIX ME: We need to loop defaults, but once we move to custom_1_x convention this code can be simplified.
3191 foreach ($defaults as $customKey => $customValue) {
3192 if ($customFieldDetails = CRM_Core_BAO_CustomField
::getKeyID($customKey, TRUE)) {
3193 if ($name == 'custom_' . $customFieldDetails[0]) {
3195 //hack to set default for checkbox
3196 //basically this is for weired field name like field[33][custom_19]
3197 //we are converting this field name to array structure and assign value.
3200 foreach ($formattedGroupTree as $tree) {
3201 if (!empty($tree['fields'][$customFieldDetails[0]])) {
3202 if ('CheckBox' == CRM_Utils_Array
::value('html_type', $tree['fields'][$customFieldDetails[0]])) {
3204 $defaults['field'][$componentId][$name] = $customValue;
3207 elseif (CRM_Utils_Array
::value('data_type', $tree['fields'][$customFieldDetails[0]]) == 'Date') {
3210 // CRM-6681, $default contains formatted date, time values.
3211 $defaults[$fldName] = $customValue;
3212 if (!empty($defaults[$customKey . '_time'])) {
3213 $defaults['field'][$componentId][$name . '_time'] = $defaults[$customKey . '_time'];
3219 if (!$skipValue ||
$isStandalone) {
3220 $defaults[$fldName] = $customValue;
3222 unset($defaults[$customKey]);
3228 elseif (isset($values[$fldName])) {
3229 $defaults[$fldName] = $values[$fldName];
3235 * Retrieve groups of profiles.
3237 * @param int $profileID
3238 * Id of the profile.
3243 public static function profileGroups($profileID) {
3245 $profileTypes = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $profileID, 'group_type');
3246 if ($profileTypes) {
3247 $groupTypeParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $profileTypes);
3248 $groupTypes = explode(',', $groupTypeParts[0]);
3254 * Alter contact params by filtering existing subscribed groups and returns
3255 * unsubscribed groups array for subscription.
3257 * @param array $params
3259 * @param int $contactId
3263 * This contains array of groups for subscription
3265 public static function getDoubleOptInGroupIds(&$params, $contactId = NULL) {
3266 $config = CRM_Core_Config
::singleton();
3267 $subscribeGroupIds = [];
3269 // process further only if profileDoubleOptIn enabled and if groups exist
3270 if (!array_key_exists('group', $params) ||
3271 !self
::isProfileDoubleOptin() ||
3272 CRM_Utils_System
::isNull($params['group'])
3274 return $subscribeGroupIds;
3277 //check if contact email exist.
3279 foreach ($params as $name => $value) {
3280 if (strpos($name, 'email-') !== FALSE) {
3286 //Proceed furthur only if email present
3288 return $subscribeGroupIds;
3291 //do check for already subscriptions.
3292 $contactGroups = [];
3296 FROM civicrm_group_contact
3297 WHERE status = 'Added'
3298 AND contact_id = %1";
3300 $dao = CRM_Core_DAO
::executeQuery($query, [1 => [$contactId, 'Integer']]);
3301 while ($dao->fetch()) {
3302 $contactGroups[$dao->group_id
] = $dao->group_id
;
3306 //since we don't have names, compare w/ label.
3307 $mailingListGroupType = array_search('Mailing List', CRM_Core_OptionGroup
::values('group_type'));
3309 //actual processing start.
3310 foreach ($params['group'] as $groupId => $isSelected) {
3311 //unset group those are not selected.
3313 unset($params['group'][$groupId]);
3317 $groupTypes = explode(CRM_Core_DAO
::VALUE_SEPARATOR
,
3318 CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Group', $groupId, 'group_type', 'id')
3320 //get only mailing type group and unset it from params
3321 if (in_array($mailingListGroupType, $groupTypes) && !in_array($groupId, $contactGroups)) {
3322 $subscribeGroupIds[$groupId] = $groupId;
3323 unset($params['group'][$groupId]);
3327 return $subscribeGroupIds;
3331 * Check if we are rendering mixed profiles.
3333 * @param array $profileIds
3334 * Associated array of profile ids.
3337 * true if profile is mixed
3339 public static function checkForMixProfiles($profileIds) {
3340 $mixProfile = FALSE;
3342 $contactTypes = CRM_Contact_BAO_ContactType
::basicTypes(TRUE);
3344 $components = ['Contribution', 'Participant', 'Membership', 'Activity'];
3346 $typeCount = ['ctype' => [], 'subtype' => []];
3347 foreach ($profileIds as $gid) {
3348 $profileType = CRM_Core_BAO_UFField
::getProfileType($gid);
3349 // ignore profile of type Contact
3350 if ($profileType == 'Contact') {
3353 if (in_array($profileType, $contactTypes, TRUE)) {
3354 if (!isset($typeCount['ctype'][$profileType])) {
3355 $typeCount['ctype'][$profileType] = 1;
3358 // check if we are rendering profile of different contact types
3359 if (count($typeCount['ctype']) == 2) {
3364 elseif (in_array($profileType, $components, TRUE)) {
3369 if (!isset($typeCount['subtype'][$profileType])) {
3370 $typeCount['subtype'][$profileType] = 1;
3372 // check if we are rendering profile of different contact sub types
3373 if (count($typeCount['subtype']) == 2) {
3383 * Determine of we show overlay profile or not.
3386 * true if profile should be shown else false
3388 public static function showOverlayProfile() {
3389 $showOverlay = TRUE;
3391 // get the id of overlay profile
3392 $overlayProfileId = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', 'summary_overlay', 'id', 'name');
3393 $query = "SELECT count(id) FROM civicrm_uf_field WHERE uf_group_id = {$overlayProfileId} AND visibility IN ('Public Pages', 'Public Pages and Listings') ";
3395 $count = CRM_Core_DAO
::singleValueQuery($query);
3397 //check if there are no public fields and use is anonymous
3398 $session = CRM_Core_Session
::singleton();
3399 if (!$count && !$session->get('userID')) {
3400 $showOverlay = FALSE;
3403 return $showOverlay;
3407 * Get group type values of the profile.
3409 * @param int $profileId
3410 * @param string $groupType
3415 public static function groupTypeValues($profileId, $groupType = NULL) {
3416 $groupTypeValue = [];
3417 $groupTypes = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $profileId, 'group_type');
3419 $groupTypeParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $groupTypes);
3420 if (empty($groupTypeParts[1])) {
3421 return $groupTypeValue;
3423 $participantExtends = ['ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
3425 foreach (explode(',', $groupTypeParts[1]) as $groupTypeValues) {
3427 $valueParts = explode(':', $groupTypeValues);
3429 ($valueParts[0] != "{$groupType}Type" ||
3430 ($groupType == 'Participant' &&
3431 !in_array($valueParts[0], $participantExtends)
3437 foreach ($valueParts as $val) {
3438 if (CRM_Utils_Rule
::integer($val)) {
3439 $values[$val] = $val;
3442 if (!empty($values)) {
3443 $typeName = substr($valueParts[0], 0, -4);
3444 if (in_array($valueParts[0], $participantExtends)) {
3445 $typeName = $valueParts[0];
3447 $groupTypeValue[$typeName] = $values;
3451 return $groupTypeValue;
3455 * @return bool|object
3457 public static function isProfileDoubleOptin() {
3458 // check for double optin
3459 if (CRM_Core_Component
::isEnabled('CiviMail')) {
3460 return Civi
::settings()->get('profile_double_optin');
3466 * @return bool|object
3468 public static function isProfileAddToGroupDoubleOptin() {
3469 // check for add to group double optin
3470 if (CRM_Core_Component
::isEnabled('CiviMail')) {
3471 return Civi
::settings()->get('profile_add_to_group_double_optin');
3477 * Get profiles used for batch entry.
3480 * profileIds profile ids
3482 public static function getBatchProfiles() {
3484 FROM civicrm_uf_group
3485 WHERE name IN ('contribution_batch_entry', 'membership_batch_entry')";
3486 $dao = CRM_Core_DAO
::executeQuery($query);
3488 while ($dao->fetch()) {
3489 $profileIds[$dao->id
] = $dao->id
;
3496 * @param $destination
3497 * @param bool $returnMultiSummaryFields
3499 * @return array|null
3500 * @todo what do I do?
3502 public static function shiftMultiRecordFields(&$source, &$destination, $returnMultiSummaryFields = FALSE) {
3503 $multiSummaryFields = $returnMultiSummaryFields ?
[] : NULL;
3504 foreach ($source as $field => $properties) {
3505 if (!CRM_Core_BAO_CustomField
::getKeyID($field)) {
3508 if (CRM_Core_BAO_CustomField
::isMultiRecordField($field)) {
3509 $destination[$field] = $properties;
3510 if ($returnMultiSummaryFields) {
3511 if ($properties['is_multi_summary']) {
3512 $multiSummaryFields[$field] = $properties;
3515 unset($source[$field]);
3518 return $multiSummaryFields;
3522 * This is function is used to format pseudo fields.
3524 * @param array $fields
3525 * Associated array of profile fields.
3528 public static function reformatProfileFields(&$fields) {
3529 //reformat fields array
3530 foreach ($fields as $name => $field) {
3531 //reformat phone and extension field
3532 if (substr($field['name'], 0, 13) == 'phone_and_ext') {
3533 $fieldSuffix = str_replace('phone_and_ext-', '', $field['name']);
3535 // retain existing element properties and just update and replace key
3536 CRM_Utils_Array
::crmReplaceKey($fields, $name, "phone-{$fieldSuffix}");
3537 $fields["phone-{$fieldSuffix}"]['name'] = "phone-{$fieldSuffix}";
3538 $fields["phone-{$fieldSuffix}"]['where'] = 'civicrm_phone.phone';
3540 // add additional phone extension field
3541 $fields["phone_ext-{$fieldSuffix}"] = $field;
3542 $fields["phone_ext-{$fieldSuffix}"]['title'] = $field['title'] . ' - ' . ts('Ext.');
3543 $fields["phone_ext-{$fieldSuffix}"]['name'] = "phone_ext-{$fieldSuffix}";
3544 $fields["phone_ext-{$fieldSuffix}"]['where'] = 'civicrm_phone.phone_ext';
3545 $fields["phone_ext-{$fieldSuffix}"]['skipDisplay'] = 1;
3546 //ignore required for extension field
3547 $fields["phone_ext-{$fieldSuffix}"]['is_required'] = 0;
3553 * Get the frontend_title for the profile, falling back on 'title' if none.
3555 * @param int $profileID
3559 * @throws \CiviCRM_API3_Exception
3561 public static function getFrontEndTitle(int $profileID) {
3562 $profile = civicrm_api3('UFGroup', 'getsingle', ['id' => $profileID, 'return' => ['title', 'frontend_title']]);
3563 return $profile['frontend_title'] ??
$profile['title'];
3567 * Format custom field value for use in prepopulating a quickform profile field.
3569 * @param array $field
3571 * @param string $value
3575 * String or array, depending on the html type
3577 private static function formatCustomValue($field, $value) {
3578 if (CRM_Core_BAO_CustomField
::isSerialized($field)) {
3579 $value = CRM_Utils_Array
::explodePadded($value);
3581 // This may not be required now.
3582 if ($field['html_type'] === 'CheckBox') {
3584 foreach (array_filter($value) as $item) {
3585 $checkboxes[$item] = 1;
3586 // CRM-2969 seems like we need this for QF style checkboxes in profile where its multiindexed
3587 $checkboxes["[{$item}]"] = 1;