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.
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 null $orderProfiles
266 * @param bool $eventProfile
269 * The fields that belong to this ufgroup(s)
271 * @throws \CRM_Core_Exception
273 public static function getFields(
281 $skipPermission = FALSE,
283 $permissionType = CRM_Core_Permission
::CREATE
,
284 $orderBy = 'field_name',
285 $orderProfiles = NULL,
286 $eventProfile = FALSE
288 if (!is_array($id)) {
289 $id = CRM_Utils_Type
::escape($id, 'Positive');
296 $gids = implode(',', $profileIds);
299 $query = "SELECT g.* from civicrm_uf_group g
300 LEFT JOIN civicrm_uf_join j ON (j.uf_group_id = g.id)
301 WHERE g.id IN ( {$gids} )
302 AND ((j.uf_group_id IN ( {$gids} ) AND j.module = %1) OR g.is_reserved = 1 )
304 $params = [1 => [$restrict, 'String']];
307 $query = "SELECT g.* from civicrm_uf_group g WHERE g.id IN ( {$gids} ) ";
311 $query .= " AND g.is_active = 1";
316 'administer CiviCRM',
317 'manage event profiles',
320 if ($eventProfile && CRM_Core_Permission
::check($checkPermission)) {
321 $skipPermission = TRUE;
324 // add permissioning for profiles only if not registration
325 if (!$skipPermission) {
326 $permissionClause = CRM_Core_Permission
::ufGroupClause($permissionType, 'g.');
327 $query .= " AND $permissionClause ";
330 if ($orderProfiles and count($profileIds) > 1) {
331 $query .= " ORDER BY FIELD( g.id, {$gids} )";
333 $group = CRM_Core_DAO
::executeQuery($query, $params);
337 while ($group->fetch()) {
339 $query = self
::createUFFieldQuery($group->id
, $searchable, $showAll, $visibility, $orderBy);
340 $field = CRM_Core_DAO
::executeQuery($query);
342 $importableFields = self
::getProfileFieldMetadata($showAll);
343 list($customFields, $addressCustomFields) = self
::getCustomFields($ctype);
345 while ($field->fetch()) {
346 list($name, $formattedField) = self
::formatUFField($group, $field, $customFields, $addressCustomFields, $importableFields, $permissionType);
347 if ($formattedField !== NULL) {
348 $fields[$name] = $formattedField;
353 if (empty($fields) && !$validGroup) {
354 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.',
355 [1 => implode(',', $profileIds)]
359 self
::reformatProfileFields($fields);
366 * Format a list of UFFields for use with buildProfile. This is the in-memory analog
369 * @param array $groupArr
370 * (mimic CRM_UF_DAO_UFGroup).
371 * @param array $fieldArrs
372 * List of fields (each mimics CRM_UF_DAO_UFField).
373 * @param bool $visibility
374 * Visibility of fields we are interested in.
375 * @param bool $searchable
376 * @param bool $showAll
378 * @param int $permissionType
381 * @see self::getFields
383 public static function formatUFFields(
390 $permissionType = CRM_Core_Permission
::CREATE
392 // $group = new CRM_Core_DAO_UFGroup();
393 // $group->copyValues($groupArr); // no... converts string('') to string('null')
394 $group = (object) $groupArr;
396 // Refactoring note: The $fieldArrs here may be slightly different than the $ufFields
397 // used by calculateGroupType, but I don't think the missing fields matter, and -- if
398 // they did -- the obvious fix would produce mutual recursion.
399 $ufGroupType = self
::_calculateGroupType($fieldArrs);
400 $profileType = CRM_Core_BAO_UFField
::calculateProfileType(implode(',', $ufGroupType));
401 $contactActivityProfile = CRM_Core_BAO_UFField
::checkContactActivityProfileTypeByGroupType(implode(',', $ufGroupType));
402 $importableFields = self
::getImportableFields($showAll, $profileType, $contactActivityProfile);
403 list($customFields, $addressCustomFields) = self
::getCustomFields($ctype);
405 $formattedFields = [];
406 foreach ($fieldArrs as $fieldArr) {
407 $field = (object) $fieldArr;
408 if (!self
::filterUFField($field, $searchable, $showAll, $visibility)) {
412 list($name, $formattedField) = self
::formatUFField($group, $field, $customFields, $addressCustomFields, $importableFields, $permissionType);
413 if ($formattedField !== NULL) {
414 $formattedFields[$name] = $formattedField;
417 return $formattedFields;
421 * Prepare a field for rendering with CRM_Core_BAO_UFGroup::buildProfile.
423 * @param CRM_Core_DAO_UFGroup|CRM_Core_DAO $group
424 * @param CRM_Core_DAO_UFField|CRM_Core_DAO $field
425 * @param array $customFields
426 * @param array $addressCustomFields
427 * @param array $importableFields
428 * @param int $permissionType
429 * Eg CRM_Core_Permission::CREATE.
433 protected static function formatUFField(
437 $addressCustomFields,
439 $permissionType = CRM_Core_Permission
::CREATE
441 $name = $field->field_name
;
442 $title = $field->label
;
444 $addressCustom = FALSE;
445 if (in_array($permissionType, [CRM_Core_Permission
::CREATE
, CRM_Core_Permission
::EDIT
]) &&
446 in_array($field->field_name
, array_keys($addressCustomFields))
448 $addressCustom = TRUE;
449 $name = "address_{$name}";
451 if ($field->field_name
== 'url') {
452 $name .= "-{$field->website_type_id}";
454 elseif (!empty($field->location_type_id
)) {
455 $name .= "-{$field->location_type_id}";
458 $locationFields = self
::getLocationFields();
459 if (in_array($field->field_name
, $locationFields) ||
$addressCustom) {
464 if (isset($field->phone_type_id
)) {
465 $name .= "-{$field->phone_type_id}";
467 $fieldMetaData = CRM_Utils_Array
::value($name, $importableFields, ($importableFields[$field->field_name
] ??
[]));
469 // No lie: this is bizarre; why do we need to mix so many UFGroup properties into UFFields?
470 // I guess to make field self sufficient with all the required data and avoid additional calls
473 'groupTitle' => $group->title
,
474 'groupName' => $group->name
,
475 'groupDisplayTitle' => (!empty($group->frontend_title
)) ?
$group->frontend_title
: $group->title
,
476 'groupHelpPre' => empty($group->help_pre
) ?
'' : $group->help_pre
,
477 'groupHelpPost' => empty($group->help_post
) ?
'' : $group->help_post
,
479 'where' => CRM_Utils_Array
::value('where', CRM_Utils_Array
::value($field->field_name
, $importableFields)),
480 'attributes' => CRM_Core_DAO
::makeAttribute(CRM_Utils_Array
::value($field->field_name
, $importableFields)),
481 'is_required' => $field->is_required
,
482 'is_view' => $field->is_view
,
483 'help_pre' => $field->help_pre
,
484 'help_post' => $field->help_post
,
485 'visibility' => $field->visibility
,
486 'in_selector' => $field->in_selector
,
487 'rule' => CRM_Utils_Array
::value('rule', CRM_Utils_Array
::value($field->field_name
, $importableFields)),
488 'location_type_id' => $field->location_type_id ??
NULL,
489 'website_type_id' => $field->website_type_id ??
NULL,
490 'phone_type_id' => $field->phone_type_id ??
NULL,
491 'group_id' => $group->id
,
492 'add_to_group_id' => $group->add_to_group_id ??
NULL,
493 'add_captcha' => $group->add_captcha ??
NULL,
494 'field_type' => $field->field_type
,
495 'field_id' => $field->id
,
496 'pseudoconstant' => CRM_Utils_Array
::value(
498 CRM_Utils_Array
::value($field->field_name
, $importableFields)
500 // obsolete this when we remove the name / dbName discrepancy with gender/suffix/prefix
501 'dbName' => CRM_Utils_Array
::value(
503 CRM_Utils_Array
::value($field->field_name
, $importableFields)
506 'data_type' => CRM_Utils_Type
::getDataTypeFromFieldMetadata($fieldMetaData),
507 'bao' => $fieldMetaData['bao'] ??
NULL,
510 $formattedField = CRM_Utils_Date
::addDateMetadataToField($fieldMetaData, $formattedField);
512 //adding custom field property
513 if (substr($field->field_name
, 0, 6) == 'custom' ||
514 substr($field->field_name
, 0, 14) === 'address_custom'
516 // if field is not present in customFields, that means the user
517 // DOES NOT HAVE permission to access that field
518 if (array_key_exists($field->field_name
, $customFields)) {
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;
727 protected static function getCustomFields($ctype) {
728 $cacheKey = 'uf_group_custom_fields_' . $ctype;
729 if (!Civi
::cache('metadata')->has($cacheKey)) {
730 $customFields = CRM_Core_BAO_CustomField
::getFieldsForImport($ctype, FALSE, FALSE, FALSE, TRUE, TRUE);
732 // hack to add custom data for components
733 $components = ['Contribution', 'Participant', 'Membership', 'Activity', 'Case'];
734 foreach ($components as $value) {
735 $customFields = array_merge($customFields, CRM_Core_BAO_CustomField
::getFieldsForImport($value));
737 $addressCustomFields = CRM_Core_BAO_CustomField
::getFieldsForImport('Address');
738 $customFields = array_merge($customFields, $addressCustomFields);
739 Civi
::cache('metadata')->set($cacheKey, [$customFields, $addressCustomFields]);
741 return Civi
::cache('metadata')->get($cacheKey);
745 * Check the data validity.
748 * The user id that we are actually editing.
749 * @param string $name
750 * The machine-name of the group we are interested in.
751 * @param bool $register
753 * The action of the form.
755 * @pram boolean $register is this the registrtion form
757 * true if form is valid
759 public static function isValid($userID, $name, $register = FALSE, $action = NULL) {
761 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
762 ts('Dynamic Form Creator'),
765 $controller->set('id', $userID);
766 $controller->set('register', 1);
767 $controller->process();
768 return $controller->validate();
771 // make sure we have a valid group
772 $group = new CRM_Core_DAO_UFGroup();
774 $group->name
= $name;
776 if ($group->find(TRUE) && $userID) {
777 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic', ts('Dynamic Form Creator'), $action);
778 $controller->set('gid', $group->id
);
779 $controller->set('id', $userID);
780 $controller->set('register', 0);
781 $controller->process();
782 return $controller->validate();
789 * Get the html for the form that represents this particular group.
792 * The user id that we are actually editing.
793 * @param string $title
794 * The title of the group we are interested in.
796 * The action of the form.
797 * @param bool $register
798 * Is this the registration form.
800 * Should we reset the form?.
801 * @param int $profileID
802 * Do we have the profile ID?.
804 * @param bool $doNotProcess
808 * the html for the form on success, otherwise empty string
810 public static function getEditHTML(
817 $doNotProcess = FALSE,
822 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
823 ts('Dynamic Form Creator'),
826 if ($reset ||
$doNotProcess) {
827 // hack to make sure we do not process this form
828 $oldQFDefault = CRM_Utils_Array
::value('_qf_default',
831 unset($_POST['_qf_default']);
832 unset($_REQUEST['_qf_default']);
834 $controller->reset();
838 $controller->set('id', $userID);
839 $controller->set('register', 1);
840 $controller->set('skipPermission', 1);
841 $controller->set('ctype', $ctype);
842 $controller->process();
843 if ($doNotProcess ||
!empty($_POST)) {
844 $controller->validate();
846 $controller->setEmbedded(TRUE);
848 //CRM-5839 - though we want to process form, get the control back.
849 $controller->setSkipRedirection(!$doNotProcess);
853 // we are done processing so restore the POST/REQUEST vars
854 if (($reset ||
$doNotProcess) && $oldQFDefault) {
855 $_POST['_qf_default'] = $_REQUEST['_qf_default'] = $oldQFDefault;
858 $template = CRM_Core_Smarty
::singleton();
860 // Hide CRM error messages if they are displayed using drupal form_set_error.
861 if (!empty($_POST)) {
862 $template->assign('suppressForm', TRUE);
865 return trim($template->fetch('CRM/Profile/Form/Dynamic.tpl'));
869 // make sure we have a valid group
870 $group = new CRM_Core_DAO_UFGroup();
872 $group->title
= $title;
874 if ($group->find(TRUE)) {
875 $profileID = $group->id
;
880 // make sure profileID and ctype match if ctype exists
882 $profileType = CRM_Core_BAO_UFField
::getProfileType($profileID);
883 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
884 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
887 if (($profileType != 'Contact') && ($profileType != $ctype)) {
892 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
893 ts('Dynamic Form Creator'),
897 $controller->reset();
899 $controller->set('gid', $profileID);
900 $controller->set('id', $userID);
901 $controller->set('register', 0);
902 $controller->set('skipPermission', 1);
904 $controller->set('ctype', $ctype);
906 $controller->process();
907 $controller->setEmbedded(TRUE);
909 //CRM-5846 - give the control back to drupal.
910 $controller->setSkipRedirection(!$doNotProcess);
913 $template = CRM_Core_Smarty
::singleton();
915 // Hide CRM error messages if they are displayed using drupal form_set_error.
916 if (!empty($_POST) && CRM_Core_Config
::singleton()->userFramework
== 'Drupal') {
917 if (arg(0) == 'user' ||
(arg(0) == 'admin' && arg(1) == 'people')) {
918 $template->assign('suppressForm', TRUE);
922 $templateFile = "CRM/Profile/Form/{$profileID}/Dynamic.tpl";
923 if (!$template->template_exists($templateFile)) {
924 $templateFile = 'CRM/Profile/Form/Dynamic.tpl';
926 return trim($template->fetch($templateFile));
929 $userEmail = CRM_Contact_BAO_Contact_Location
::getEmailDetails($userID);
931 // if post not empty then only proceed
932 if (!empty($_POST)) {
934 $config = CRM_Core_Config
::singleton();
935 $email = $_POST['mail'] ??
NULL;
937 if (CRM_Utils_Rule
::email($email) && ($email != $userEmail[1])) {
938 CRM_Core_BAO_UFMatch
::updateContactEmail($userID, $email);
947 * Given a contact id and a field set, return the values from the db.
950 * @param array $fields
951 * The profile fields of interest.
952 * @param array $values
953 * The values for the above fields.
954 * @param bool $searchable
956 * @param array $componentWhere
957 * Component condition.
958 * @param bool $absolute
959 * Return urls in absolute form (useful when sending an email).
960 * @param null $additionalWhereClause
964 public static function getValues(
965 $cid, &$fields, &$values,
966 $searchable = TRUE, $componentWhere = NULL,
967 $absolute = FALSE, $additionalWhereClause = NULL
969 if (empty($cid) && empty($componentWhere)) {
973 // get the contact details (hier)
974 $returnProperties = CRM_Contact_BAO_Contact
::makeHierReturnProperties($fields);
975 $params = $cid ?
[['contact_id', '=', $cid, 0, 0]] : [];
977 // add conditions specified by components. eg partcipant_id etc
978 if (!empty($componentWhere)) {
979 $params = array_merge($params, $componentWhere);
982 $query = new CRM_Contact_BAO_Query($params, $returnProperties, $fields);
984 $details = $query->searchQuery(0, 0, NULL, FALSE, FALSE,
985 FALSE, FALSE, FALSE, $additionalWhereClause);
986 while ($details->fetch()) {
991 $query->convertToPseudoNames($details);
993 $locationTypes = CRM_Core_BAO_Address
::buildOptions('location_type_id', 'validate');
994 $imProviders = CRM_Core_DAO_IM
::buildOptions('provider_id');
995 $websiteTypes = CRM_Core_DAO_Website
::buildOptions('website_type_id');
997 $multipleFields = ['url'];
999 //start of code to set the default values
1000 foreach ($fields as $name => $field) {
1002 if ($name == 'id') {
1003 $name = 'contact_id';
1006 // skip fields that should not be displayed separately
1007 if (!empty($field['skipDisplay'])) {
1011 // Create a unique, non-empty index for each field.
1012 $index = $field['title'];
1013 if ($index === '') {
1016 while (array_key_exists($index, $values)) {
1020 $params[$index] = $values[$index] = '';
1021 $customFieldName = NULL;
1023 if (isset($details->$name) ||
$name == 'group' ||
$name == 'tag') {
1024 // to handle gender / suffix / prefix
1025 if (in_array(substr($name, 0, -3), ['gender', 'prefix', 'suffix'])) {
1026 $params[$index] = $details->$name;
1027 $values[$index] = $details->$name;
1029 elseif (in_array($name, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
1030 $dname = $name . '_display';
1031 $values[$index] = $details->$dname;
1032 $name = $name . '_id';
1033 $params[$index] = $details->$name;
1035 elseif (in_array($name, [
1040 $values[$index] = $details->$name;
1041 $idx = $name . '_id';
1042 $params[$index] = $details->$idx;
1044 elseif ($name === 'preferred_language') {
1045 $params[$index] = $details->$name;
1046 $values[$index] = CRM_Core_PseudoConstant
::getLabel('CRM_Contact_DAO_Contact', 'preferred_language', $details->$name);
1048 elseif ($name == 'group') {
1049 $groups = CRM_Contact_BAO_GroupContact
::getContactGroup($cid, 'Added', NULL, FALSE, TRUE);
1052 foreach ($groups as $g) {
1053 // CRM-8362: User and User Admin visibility groups should be included in display if user has
1054 // VIEW permission on that group
1055 $groupPerm = CRM_Contact_BAO_Group
::checkPermission($g['group_id'], TRUE);
1057 if ($g['visibility'] != 'User and User Admin Only' ||
1058 CRM_Utils_Array
::key(CRM_Core_Permission
::VIEW
, $groupPerm)
1060 $title[] = $g['title'];
1061 if ($g['visibility'] == 'Public Pages') {
1062 $ids[] = $g['group_id'];
1066 $values[$index] = implode(', ', $title);
1067 $params[$index] = implode(',', $ids);
1069 elseif ($name == 'tag') {
1070 $entityTags = CRM_Core_BAO_EntityTag
::getTag($cid);
1071 $allTags = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]);
1073 foreach ($entityTags as $tagId) {
1074 $title[] = $allTags[$tagId];
1076 $values[$index] = implode(', ', $title);
1077 $params[$index] = implode(',', $entityTags);
1079 elseif ($name == 'activity_status_id') {
1080 $activityStatus = CRM_Core_PseudoConstant
::activityStatus();
1081 $values[$index] = $activityStatus[$details->$name];
1082 $params[$index] = $details->$name;
1084 elseif ($name == 'activity_date_time') {
1085 $values[$index] = CRM_Utils_Date
::customFormat($details->$name);
1086 $params[$index] = $details->$name;
1088 elseif ($name == 'contact_sub_type') {
1089 $contactSubTypeNames = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $details->$name);
1090 if (!empty($contactSubTypeNames)) {
1091 $contactSubTypeLabels = [];
1092 // get all contact subtypes
1093 $allContactSubTypes = CRM_Contact_BAO_ContactType
::subTypeInfo();
1094 // build contact subtype labels array
1095 foreach ($contactSubTypeNames as $cstName) {
1097 $contactSubTypeLabels[] = $allContactSubTypes[$cstName]['label'];
1100 $values[$index] = implode(',', $contactSubTypeLabels);
1103 $params[$index] = $details->$name;
1106 if (substr($name, 0, 7) === 'do_not_' ||
substr($name, 0, 3) === 'is_') {
1107 if ($details->$name) {
1108 $values[$index] = '[ x ]';
1112 if ($cfID = CRM_Core_BAO_CustomField
::getKeyID($name)) {
1113 $htmlType = $field['html_type'];
1115 // field_type is only set when we are retrieving profile values
1116 // when sending email, we call the same function to get custom field
1117 // values etc, i.e. emulating a profile
1118 $fieldType = $field['field_type'] ??
NULL;
1120 if ($htmlType == 'File') {
1123 $fieldType == 'Activity' && !empty($componentWhere[0][2])
1125 $entityId = $componentWhere[0][2];
1128 $fileURL = CRM_Core_BAO_CustomField
::getFileURL($entityId,
1132 $additionalWhereClause
1134 $params[$index] = $values[$index] = $fileURL['file_url'];
1138 if (isset($dao) && property_exists($dao, 'data_type') &&
1139 ($dao->data_type
== 'Int' ||
1140 $dao->data_type
== 'Boolean'
1143 $customVal = (int ) ($details->{$name});
1145 elseif (isset($dao) && property_exists($dao, 'data_type')
1146 && $dao->data_type
== 'Float'
1148 $customVal = (float ) ($details->{$name});
1150 elseif (!CRM_Utils_System
::isNull(explode(CRM_Core_DAO
::VALUE_SEPARATOR
,
1154 $customVal = $details->{$name};
1158 if (CRM_Utils_System
::isNull($customVal)) {
1162 $params[$index] = $customVal;
1163 $values[$index] = CRM_Core_BAO_CustomField
::displayValue($customVal, $cfID);
1164 if ($field['data_type'] == 'ContactReference') {
1165 $params[$index] = $values[$index];
1167 if (CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_CustomField',
1168 $cfID, 'is_search_range'
1171 $customFieldName = "{$name}_from";
1175 elseif ($name == 'image_URL') {
1176 list($width, $height) = getimagesize(CRM_Utils_String
::unstupifyUrl($details->$name));
1177 list($thumbWidth, $thumbHeight) = CRM_Contact_BAO_Contact
::getThumbSize($width, $height);
1179 $image_URL = '<img src="' . $details->$name . '" height= ' . $thumbHeight . ' width= ' . $thumbWidth . ' />';
1180 $values[$index] = "<a href='#' onclick='contactImagePopUp(\"{$details->$name}\", {$width}, {$height});'>{$image_URL}</a>";
1182 elseif (in_array($name, [
1186 // @todo this set should be determined from metadata, not hard-coded.
1187 $values[$index] = CRM_Utils_Date
::customFormat($details->$name);
1188 $params[$index] = CRM_Utils_Date
::isoToMysql($details->$name);
1192 if ($index == 'Campaign') {
1193 $dao = 'CRM_Campaign_DAO_Campaign';
1195 elseif ($index == 'Contribution Page') {
1196 $dao = 'CRM_Contribute_DAO_ContributionPage';
1199 $value = CRM_Core_DAO
::getFieldValue($dao, $details->$name, 'title');
1202 $value = $details->$name;
1204 $values[$index] = $value;
1209 elseif (strpos($name, '-') !== FALSE) {
1210 list($fieldName, $id, $type) = CRM_Utils_System
::explode('-', $name, 3);
1212 if (!in_array($fieldName, $multipleFields)) {
1213 if ($id == 'Primary') {
1215 // not sure why we'd every use Primary location type id
1216 // we need to fix the source if we are using it
1217 // $locationTypeName = CRM_Contact_BAO_Contact::getPrimaryLocationType( $cid );
1218 $locationTypeName = 1;
1221 $locationTypeName = $locationTypes[$id] ??
NULL;
1224 if (!$locationTypeName) {
1228 $detailName = "{$locationTypeName}-{$fieldName}";
1229 $detailName = str_replace(' ', '_', $detailName);
1231 if (in_array($fieldName, [
1238 $detailName .= "-{$type}";
1242 if (in_array($fieldName, [
1247 $values[$index] = $details->$detailName;
1248 $idx = $detailName . '_id';
1249 $params[$index] = $details->$idx;
1251 elseif ($fieldName == 'im') {
1252 $providerId = $detailName . '-provider_id';
1253 if (isset($imProviders[$details->$providerId])) {
1254 $values[$index] = $details->$detailName . " (" . $imProviders[$details->$providerId] . ")";
1257 $values[$index] = $details->$detailName;
1259 $params[$index] = $details->$detailName;
1261 elseif ($fieldName == 'phone') {
1262 $phoneExtField = str_replace('phone', 'phone_ext', $detailName);
1263 if (isset($details->$phoneExtField)) {
1264 $values[$index] = $details->$detailName . " (" . $details->$phoneExtField . ")";
1267 $values[$index] = $details->$detailName;
1269 $params[$index] = $details->$detailName;
1272 $values[$index] = $params[$index] = $details->$detailName;
1276 $detailName = "website-{$id}-{$fieldName}";
1277 $url = CRM_Utils_System
::fixURL($details->$detailName);
1278 if ($details->$detailName) {
1279 $websiteTypeId = "website-{$id}-website_type_id";
1280 $websiteType = $websiteTypes[$details->$websiteTypeId];
1281 $values[$index] = "<a href=\"$url\">{$details->$detailName} ( {$websiteType} )</a>";
1284 $values[$index] = '';
1289 if ((CRM_Utils_Array
::value('visibility', $field) == 'Public Pages and Listings') &&
1290 CRM_Core_Permission
::check('profile listings and forms')
1293 if (CRM_Utils_System
::isNull($params[$index])) {
1294 $params[$index] = $values[$index];
1296 if (!isset($params[$index])) {
1299 if (!$customFieldName) {
1300 $fieldName = $field['name'];
1303 $fieldName = $customFieldName;
1307 if (CRM_Core_BAO_CustomField
::getKeyID($field['name'])) {
1308 $htmlType = $field['html_type'];
1309 if ($htmlType == 'Link') {
1310 $url = $params[$index];
1312 elseif (in_array($htmlType, [
1315 'Multi-Select State/Province',
1316 'Multi-Select Country',
1318 $valSeperator = CRM_Core_DAO
::VALUE_SEPARATOR
;
1319 $selectedOptions = explode($valSeperator, $params[$index]);
1321 foreach ($selectedOptions as $key => $multiOption) {
1323 $url[] = CRM_Utils_System
::url('civicrm/profile',
1324 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1325 urlencode($fieldName) .
1327 urlencode($multiOption)
1333 $url = CRM_Utils_System
::url('civicrm/profile',
1334 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1335 urlencode($fieldName) .
1337 urlencode($params[$index])
1342 $url = CRM_Utils_System
::url('civicrm/profile',
1343 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1344 urlencode($fieldName) .
1346 urlencode($params[$index])
1351 !empty($values[$index]) &&
1355 if (is_array($url) && !empty($url)) {
1357 $eachMultiValue = explode(', ', $values[$index]);
1358 foreach ($eachMultiValue as $key => $valueLabel) {
1359 $links[] = '<a href="' . $url[$key] . '">' . $valueLabel . '</a>';
1361 $values[$index] = implode(', ', $links);
1364 $values[$index] = '<a href="' . $url . '">' . $values[$index] . '</a>';
1372 * Check if profile Group used by any module.
1380 public static function usedByModule($id) {
1381 //check whether this group is used by any module(check uf join records)
1383 FROM civicrm_uf_join
1384 WHERE civicrm_uf_join.uf_group_id=$id";
1386 $dao = new CRM_Core_DAO();
1388 if ($dao->fetch()) {
1397 * Delete the profile Group.
1405 public static function del($id) {
1406 //check whether this group contains any profile fields
1407 $profileField = new CRM_Core_DAO_UFField();
1408 $profileField->uf_group_id
= $id;
1409 $profileField->find();
1410 while ($profileField->fetch()) {
1411 CRM_Core_BAO_UFField
::del($profileField->id
);
1414 //delete records from uf join table
1415 $ufJoin = new CRM_Core_DAO_UFJoin();
1416 $ufJoin->uf_group_id
= $id;
1419 //delete profile group
1420 $group = new CRM_Core_DAO_UFGroup();
1429 * @param array $params
1430 * Reference array contains the values submitted by the form.
1432 * Reference array contains the id.
1437 public static function add(&$params, $ids = []) {
1447 foreach ($fields as $field) {
1448 $params[$field] = CRM_Utils_Array
::value($field, $params, FALSE);
1451 $params['limit_listings_group_id'] = $params['group'] ??
NULL;
1452 $params['add_to_group_id'] = $params['add_contact_to_group'] ??
NULL;
1455 if (!empty($params['group_type']) && is_array($params['group_type'])) {
1456 $params['group_type'] = implode(',', $params['group_type']);
1458 $ufGroup = new CRM_Core_DAO_UFGroup();
1459 $ufGroup->copyValues($params);
1461 $ufGroupID = CRM_Utils_Array
::value('ufgroup', $ids, CRM_Utils_Array
::value('id', $params));
1462 if (!$ufGroupID && empty($params['name'])) {
1463 $ufGroup->name
= CRM_Utils_String
::munge($ufGroup->title
, '_', 56);
1465 $ufGroup->id
= $ufGroupID;
1469 if (!$ufGroupID && empty($params['name'])) {
1470 $ufGroup->name
= $ufGroup->name
. "_{$ufGroup->id}";
1478 * Make uf join entries for an uf group.
1480 * @param int $weight
1481 * @param array $groupTypes
1482 * An assoc array of name/value pairs.
1483 * @param int $ufGroupId
1486 public static function createUFJoin($weight, $groupTypes, $ufGroupId) {
1488 // get ufjoin records for uf group
1489 $ufGroupRecord = CRM_Core_BAO_UFGroup
::getUFJoinRecord($ufGroupId);
1491 // get the list of all ufgroup types
1492 $allUFGroupType = CRM_Core_SelectValues
::ufGroupTypes();
1494 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
1495 if (!is_array($groupTypes)) {
1499 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
1500 if (!is_array($ufGroupRecord)) {
1501 $ufGroupRecord = [];
1504 // check which values has to be inserted/deleted for contact
1505 $menuRebuild = FALSE;
1506 foreach ($allUFGroupType as $key => $value) {
1508 $joinParams['uf_group_id'] = $ufGroupId;
1509 $joinParams['module'] = $key;
1510 if ($key === 'User Account') {
1511 $menuRebuild = TRUE;
1513 if (array_key_exists($key, $groupTypes) && !in_array($key, $ufGroupRecord)) {
1514 // insert a new record
1515 CRM_Core_BAO_UFGroup
::addUFJoin($joinParams);
1517 elseif (!array_key_exists($key, $groupTypes) && in_array($key, $ufGroupRecord)) {
1518 // delete a record for existing ufgroup
1519 CRM_Core_BAO_UFGroup
::delUFJoin($joinParams);
1525 UPDATE civicrm_uf_join
1527 WHERE uf_group_id = %2
1528 AND ( entity_id IS NULL OR entity_id <= 0 )
1531 1 => [$weight, 'Integer'],
1532 2 => [$ufGroupId, 'Integer'],
1534 CRM_Core_DAO
::executeQuery($query, $p);
1536 // Do a menu rebuild, so it gets all the new menu entries for user account
1538 $config = CRM_Core_Config
::singleton();
1539 $config->userSystem
->updateCategories();
1544 * Get the UF Join records for an ufgroup id.
1546 * @param int $ufGroupId
1548 * @param int $displayName
1549 * If set return display name in array.
1550 * @param int $status
1551 * If set return module other than default modules (User Account/User registration/Profile).
1556 public static function getUFJoinRecord($ufGroupId = NULL, $displayName = NULL, $status = NULL) {
1559 $UFGroupType = CRM_Core_SelectValues
::ufGroupTypes();
1563 $dao = new CRM_Core_DAO_UFJoin();
1566 $dao->uf_group_id
= $ufGroupId;
1572 while ($dao->fetch()) {
1573 if (!$displayName) {
1574 $ufJoin[$dao->id
] = $dao->module
;
1577 if (isset($UFGroupType[$dao->module
])) {
1578 // skip the default modules
1580 $ufJoin[$dao->id
] = $UFGroupType[$dao->module
];
1582 // added for CRM-1475
1584 elseif (!CRM_Utils_Array
::key($dao->module
, $ufJoin)) {
1585 $ufJoin[$dao->id
] = $dao->module
;
1593 * Function takes an associative array and creates a ufjoin record for ufgroup.
1595 * @param array $params
1596 * (reference) an assoc array of name/value pairs.
1598 * @return CRM_Core_BAO_UFJoin
1600 public static function addUFJoin(&$params) {
1601 $ufJoin = new CRM_Core_DAO_UFJoin();
1602 $ufJoin->copyValues($params);
1608 * Delete the uf join record for an uf group.
1610 * @param array $params
1611 * (reference) an assoc array of name/value pairs.
1613 public static function delUFJoin(&$params) {
1614 $ufJoin = new CRM_Core_DAO_UFJoin();
1615 $ufJoin->copyValues($params);
1620 * Get the weight for ufjoin record.
1622 * @param int $ufGroupId
1623 * If $ufGroupId get update weight or add weight.
1626 * weight of the UFGroup
1628 public static function getWeight($ufGroupId = NULL) {
1629 //calculate the weight
1632 $queryString = "SELECT ( MAX(civicrm_uf_join.weight)+1) as new_weight
1633 FROM civicrm_uf_join
1634 WHERE module = 'User Registration' OR module = 'User Account' OR module = 'Profile'";
1637 $queryString = "SELECT MAX(civicrm_uf_join.weight) as new_weight
1638 FROM civicrm_uf_join
1639 WHERE civicrm_uf_join.uf_group_id = %1
1640 AND ( entity_id IS NULL OR entity_id <= 0 )";
1641 $p[1] = [$ufGroupId, 'Integer'];
1644 $dao = CRM_Core_DAO
::executeQuery($queryString, $p);
1646 return ($dao->new_weight
) ?
$dao->new_weight
: 1;
1650 * Get the uf group for a module.
1652 * @param string $moduleName
1655 * No to increment the weight.
1656 * @param bool $skipPermission
1658 * Which operation (view, edit, create, etc) to check permission for.
1659 * @param array|NULL $returnFields list of UFGroup fields to return; NULL for default
1662 * array of ufgroups for a module
1664 public static function getModuleUFGroup($moduleName = NULL, $count = 0, $skipPermission = TRUE, $op = CRM_Core_Permission
::VIEW
, $returnFields = NULL) {
1665 $selectFields = ['id', 'title', 'created_id', 'is_active', 'is_reserved', 'group_type'];
1667 if (CRM_Core_BAO_SchemaHandler
::checkIfFieldExists('civicrm_uf_group', 'description')) {
1668 // CRM-13555, since description field was added later (4.4), and to avoid any problems with upgrade
1669 $selectFields[] = 'description';
1672 if (CRM_Core_BAO_SchemaHandler
::checkIfFieldExists('civicrm_uf_group', 'frontend_title')) {
1673 $selectFields[] = 'frontend_title';
1676 if (!empty($returnFields)) {
1677 $selectFields = array_merge($returnFields, array_diff($selectFields, $returnFields));
1680 $queryString = 'SELECT civicrm_uf_group.' . implode(', civicrm_uf_group.', $selectFields) . '
1681 FROM civicrm_uf_group
1682 LEFT JOIN civicrm_uf_join ON (civicrm_uf_group.id = uf_group_id)';
1685 $queryString .= ' AND civicrm_uf_group.is_active = 1
1686 WHERE civicrm_uf_join.module = %2';
1687 $p[2] = [$moduleName, 'String'];
1690 // add permissioning for profiles only if not registration
1691 if (!$skipPermission) {
1692 $permissionClause = CRM_Core_Permission
::ufGroupClause($op, 'civicrm_uf_group.');
1693 if (strpos($queryString, 'WHERE') !== FALSE) {
1694 $queryString .= " AND $permissionClause ";
1697 $queryString .= " $permissionClause ";
1701 $queryString .= ' ORDER BY civicrm_uf_join.weight, civicrm_uf_group.title';
1702 $dao = CRM_Core_DAO
::executeQuery($queryString, $p);
1705 while ($dao->fetch()) {
1706 //skip mix profiles in user Registration / User Account
1707 if (($moduleName === 'User Registration' ||
$moduleName === 'User Account') &&
1708 CRM_Core_BAO_UFField
::checkProfileType($dao->id
)
1712 foreach ($selectFields as $key => $field) {
1713 if ($field === 'id') {
1716 $ufGroups[$dao->id
][$field] = $dao->$field;
1720 // Allow other modules to alter/override the UFGroups.
1721 CRM_Utils_Hook
::buildUFGroupsForModule($moduleName, $ufGroups);
1727 * Filter ufgroups based on logged in user contact type.
1729 * @param int $ufGroupId
1730 * Uf group id (profile id).
1731 * @param int $contactID
1736 public static function filterUFGroups($ufGroupId, $contactID = NULL) {
1738 $session = CRM_Core_Session
::singleton();
1739 $contactID = $session->get('userID');
1743 //get the contact type
1744 $contactType = CRM_Contact_BAO_Contact
::getContactType($contactID);
1746 //match if exixting contact type is same as profile contact type
1747 $profileType = CRM_Core_BAO_UFField
::getProfileType($ufGroupId);
1749 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
1750 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
1752 //in some cases getBasicType() returns a cached array instead of string. Example: array ('sponsor' => 'organization')
1753 if (is_array($profileType)) {
1754 $profileType = array_shift($profileType);
1758 //allow special mix profiles for Contribution and Participant
1759 $specialProfiles = ['Contribution', 'Participant', 'Membership'];
1761 if (in_array($profileType, $specialProfiles)) {
1765 if (($contactType == $profileType) ||
$profileType == 'Contact') {
1774 * Add profile field to a form.
1776 * @param CRM_Core_Form $form
1777 * @param array $field
1781 * @param int $contactId
1782 * @param bool $online
1783 * @param string $usedFor
1784 * For building up prefixed fieldname for special cases (e.g. onBehalf, Honor).
1785 * @param int $rowNumber
1786 * @param string $prefix
1790 public static function buildProfile(
1800 $defaultValues = [];
1801 $fieldName = $field['name'];
1802 $title = $field['title'];
1803 $attributes = $field['attributes'];
1804 $rule = $field['rule'];
1805 $view = $field['is_view'];
1806 $required = ($mode == CRM_Profile_Form
::MODE_SEARCH
) ?
FALSE : $field['is_required'];
1807 $search = $mode == CRM_Profile_Form
::MODE_SEARCH
;
1808 $isShared = CRM_Utils_Array
::value('is_shared', $field, 0);
1810 // do not display view fields in drupal registration form
1812 if ($view && $mode == CRM_Profile_Form
::MODE_REGISTER
) {
1816 if ($usedFor == 'onbehalf') {
1817 $name = "onbehalf[$fieldName]";
1819 elseif ($usedFor == 'honor') {
1820 $name = "honor[$fieldName]";
1822 elseif ($contactId && !$online) {
1823 $name = "field[$contactId][$fieldName]";
1825 elseif ($rowNumber) {
1826 $name = "field[$rowNumber][$fieldName]";
1828 elseif (!empty($prefix)) {
1829 $name = $prefix . "[$fieldName]";
1835 $selectAttributes = ['class' => 'crm-select2', 'placeholder' => TRUE];
1837 if ($fieldName == 'image_URL' && $mode == CRM_Profile_Form
::MODE_EDIT
) {
1838 $deleteExtra = json_encode(ts('Are you sure you want to delete contact image.'));
1840 CRM_Core_Action
::DELETE
=> [
1841 'name' => ts('Delete Contact Image'),
1842 'url' => 'civicrm/contact/image',
1843 'qs' => 'reset=1&id=%%id%%&gid=%%gid%%&action=delete',
1844 'extra' => 'onclick = "' . htmlspecialchars("if (confirm($deleteExtra)) this.href+='&confirmed=1'; else return false;") . '"',
1847 $deleteURL = CRM_Core_Action
::formLink($deleteURL,
1848 CRM_Core_Action
::DELETE
,
1850 'id' => $form->get('id'),
1851 'gid' => $form->get('gid'),
1855 'contact.profileimage.delete',
1859 $form->assign('deleteURL', $deleteURL);
1861 $addressOptions = CRM_Core_BAO_Setting
::valueOptions(CRM_Core_BAO_Setting
::SYSTEM_PREFERENCES_NAME
,
1862 'address_options', TRUE, NULL, TRUE
1865 if (substr($fieldName, 0, 14) === 'state_province') {
1866 $form->addChainSelect($name, ['label' => $title, 'required' => $required]);
1867 $config = CRM_Core_Config
::singleton();
1868 if (!in_array($mode, [CRM_Profile_Form
::MODE_EDIT
, CRM_Profile_Form
::MODE_SEARCH
]) &&
1869 $config->defaultContactStateProvince
1871 $defaultValues[$name] = $config->defaultContactStateProvince
;
1872 $form->setDefaults($defaultValues);
1875 elseif (substr($fieldName, 0, 7) === 'country') {
1876 $form->add('select', $name, $title, ['' => ts('- select -')] + CRM_Core_PseudoConstant
::country(), $required, $selectAttributes);
1877 $config = CRM_Core_Config
::singleton();
1878 if (!in_array($mode, [CRM_Profile_Form
::MODE_EDIT
, CRM_Profile_Form
::MODE_SEARCH
]) &&
1879 $config->defaultContactCountry
1881 $defaultValues[$name] = $config->defaultContactCountry
;
1882 $form->setDefaults($defaultValues);
1885 elseif (substr($fieldName, 0, 6) === 'county') {
1886 if ($addressOptions['county']) {
1887 $form->addChainSelect($name, ['label' => $title, 'required' => $required]);
1890 elseif (substr($fieldName, 0, 9) === 'image_URL') {
1891 $form->add('file', $name, $title, $attributes, $required);
1892 $form->addUploadElement($name);
1894 elseif (substr($fieldName, 0, 2) === 'im') {
1895 $form->add('text', $name, $title, $attributes, $required);
1898 if (substr($name, -1) === ']') {
1899 $providerName = substr($name, 0, -1) . '-provider_id]';
1901 $form->add('select', $providerName, NULL,
1903 '' => ts('- select -'),
1904 ] + CRM_Core_PseudoConstant
::get('CRM_Core_DAO_IM', 'provider_id'), $required
1908 $form->add('select', $name . '-provider_id', $title,
1910 '' => ts('- select -'),
1911 ] + CRM_Core_PseudoConstant
::get('CRM_Core_DAO_IM', 'provider_id'), $required
1915 if ($view && $mode != CRM_Profile_Form
::MODE_SEARCH
) {
1916 $form->freeze($name . '-provider_id');
1920 elseif (CRM_Utils_Array
::value('name', $field) == 'membership_type') {
1921 list($orgInfo, $types) = CRM_Member_BAO_MembershipType
::getMembershipTypeInfo();
1922 $sel = &$form->addElement('hierselect', $name, $title);
1923 $select = ['' => ts('- select -')];
1924 if (count($orgInfo) == 1 && $field['is_required']) {
1925 // we only have one org - so we should default to it. Not sure about defaulting to first type
1926 // as it could be missed - so adding a select
1927 // however, possibly that is more similar to the membership form
1928 if (count($types[1]) > 1) {
1929 $types[1] = $select +
$types[1];
1933 $orgInfo = $select +
$orgInfo;
1935 $sel->setOptions([$orgInfo, $types]);
1937 elseif (CRM_Utils_Array
::value('name', $field) == 'membership_status') {
1938 $form->add('select', $name, $title,
1940 '' => ts('- select -'),
1941 ] + CRM_Member_PseudoConstant
::membershipStatus(NULL, NULL, 'label'), $required
1944 elseif (in_array($fieldName, ['gender_id', 'communication_style_id'])) {
1946 $pseudoValues = CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', $fieldName);
1947 foreach ($pseudoValues as $key => $var) {
1948 $options[$key] = $form->createElement('radio', NULL, ts($title), $var, $key);
1950 $group = $form->addGroup($options, $name, $title);
1952 $form->addRule($name, ts('%1 is a required field.', [1 => $title]), 'required');
1955 $group->setAttribute('allowClear', TRUE);
1958 elseif ($fieldName === 'prefix_id' ||
$fieldName === 'suffix_id') {
1959 $form->addSelect($name, [
1961 'entity' => 'contact',
1962 'field' => $fieldName,
1964 'placeholder' => '',
1967 elseif ($fieldName === 'contact_sub_type') {
1968 $gId = $form->get('gid') ?
$form->get('gid') : CRM_Utils_Array
::value('group_id', $field);
1969 if ($usedFor == 'onbehalf') {
1970 $profileType = 'Organization';
1972 elseif ($usedFor == 'honor') {
1973 $profileType = CRM_Core_BAO_UFField
::getProfileType($form->_params
['honoree_profile_id']);
1976 $profileType = $gId ? CRM_Core_BAO_UFField
::getProfileType($gId) : NULL;
1977 if ($profileType == 'Contact') {
1978 $profileType = 'Individual';
1982 $setSubtype = FALSE;
1983 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
1984 $setSubtype = $profileType;
1985 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
1988 $subtypes = $profileType ? CRM_Contact_BAO_ContactType
::subTypePairs($profileType) : [];
1992 $subtypeList[$setSubtype] = $subtypes[$setSubtype];
1995 $subtypeList = $subtypes;
1998 $form->add('select', $name, $title, $subtypeList, $required, ['class' => 'crm-select2', 'multiple' => TRUE]);
2000 elseif (in_array($fieldName, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
2001 // Get contact type for greeting selector
2002 $gId = $form->get('gid') ?
: CRM_Utils_Array
::value('group_id', $field);
2003 $profileType = CRM_Core_BAO_UFField
::getProfileType($gId, TRUE, FALSE, TRUE);
2005 if (!$profileType ||
in_array($profileType, ['Contact', 'Contribution', 'Participant', 'Membership'])) {
2006 $profileType = ($profileType == 'Contact' && $form->get('id')) ? CRM_Contact_BAO_Contact
::getContactType($form->get('id')) : 'Individual';
2008 if (CRM_Contact_BAO_ContactType
::isaSubType($profileType)) {
2009 $profileType = CRM_Contact_BAO_ContactType
::getBasicType($profileType);
2012 'contact_type' => $profileType,
2013 'greeting_type' => $fieldName,
2015 $form->add('select', $name, $title, ['' => ts('- select -')] + CRM_Core_PseudoConstant
::greeting($greeting), $required);
2016 // add custom greeting element
2017 $form->add('text', $fieldName . '_custom', ts('Custom %1', [1 => ucwords(str_replace('_', ' ', $fieldName))]),
2021 elseif ($fieldName === 'preferred_communication_method') {
2022 $communicationFields = CRM_Core_PseudoConstant
::get('CRM_Contact_DAO_Contact', 'preferred_communication_method');
2023 foreach ($communicationFields as $key => $var) {
2027 $communicationOptions[] = $form->createElement('checkbox', $key, NULL, $var);
2029 $form->addGroup($communicationOptions, $name, $title, '<br/>');
2031 elseif ($fieldName === 'preferred_mail_format') {
2032 $form->add('select', $name, $title, CRM_Core_SelectValues
::pmf());
2034 elseif ($fieldName === 'preferred_language') {
2035 $form->add('select', $name, $title, ['' => ts('- select -')] + CRM_Contact_BAO_Contact
::buildOptions('preferred_language'));
2037 elseif ($fieldName == 'external_identifier') {
2038 $form->add('text', $name, $title, $attributes, $required);
2039 $contID = $contactId;
2041 $contID = $form->get('id');
2043 $form->addRule($name,
2044 ts('External ID already exists in Database.'),
2046 ['CRM_Contact_DAO_Contact', $contID, 'external_identifier']
2049 elseif ($fieldName === 'group') {
2050 CRM_Contact_Form_Edit_TagsAndGroups
::buildQuickForm($form, $contactId,
2051 CRM_Contact_Form_Edit_TagsAndGroups
::GROUP
,
2056 elseif ($fieldName === 'tag') {
2057 CRM_Contact_Form_Edit_TagsAndGroups
::buildQuickForm($form, $contactId,
2058 CRM_Contact_Form_Edit_TagsAndGroups
::TAG
,
2063 elseif (substr($fieldName, 0, 4) === 'url-') {
2064 $form->add('text', $name, $title, CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Website', 'url'), $required);
2065 $form->addRule($name, ts('Enter a valid web address beginning with \'http://\' or \'https://\'.'), 'url');
2067 // Note should be rendered as textarea
2068 elseif (substr($fieldName, -4) == 'note') {
2069 $form->add('textarea', $name, $title, $attributes, $required);
2071 elseif (substr($fieldName, 0, 6) === 'custom') {
2072 $customFieldID = CRM_Core_BAO_CustomField
::getKeyID($fieldName);
2073 if ($customFieldID) {
2074 CRM_Core_BAO_CustomField
::addQuickFormElement($form, $name, $customFieldID, $required, $search, $title);
2077 elseif (substr($fieldName, 0, 14) === 'address_custom') {
2078 list($fName, $locTypeId) = CRM_Utils_System
::explode('-', $fieldName, 2);
2079 $customFieldID = CRM_Core_BAO_CustomField
::getKeyID(substr($fName, 8));
2080 if ($customFieldID) {
2081 CRM_Core_BAO_CustomField
::addQuickFormElement($form, $name, $customFieldID, $required, $search, $title);
2084 elseif ($fieldName == 'send_receipt') {
2085 $form->addElement('checkbox', $name, $title);
2087 elseif ($fieldName == 'soft_credit') {
2088 $form->addEntityRef("soft_credit_contact_id[$rowNumber]", ts('Soft Credit To'), ['create' => TRUE]);
2089 $form->addMoney("soft_credit_amount[{$rowNumber}]", ts('Amount'), FALSE, NULL, FALSE);
2091 elseif ($fieldName === 'product_name') {
2092 list($products, $options) = CRM_Contribute_BAO_Premium
::getPremiumProductInfo();
2093 $sel = &$form->addElement('hierselect', $name, $title);
2094 $products = ['0' => ts('- select -')] +
$products;
2095 $sel->setOptions([$products, $options]);
2097 elseif ($fieldName === 'payment_instrument') {
2098 $form->add('select', $name, $title,
2099 ['' => ts('- select -')] + CRM_Contribute_PseudoConstant
::paymentInstrument(), $required);
2101 elseif ($fieldName === 'financial_type') {
2102 $form->add('select', $name, $title,
2104 '' => ts('- select -'),
2105 ] + CRM_Contribute_PseudoConstant
::financialType(), $required
2108 elseif ($fieldName === 'contribution_status_id') {
2109 $contributionStatuses = CRM_Contribute_BAO_Contribution_Utils
::getContributionStatuses();
2111 $form->add('select', $name, $title,
2113 '' => ts('- select -'),
2114 ] +
$contributionStatuses, $required
2117 elseif ($fieldName === 'soft_credit_type') {
2118 $name = "soft_credit_type[$rowNumber]";
2119 $form->add('select', $name, $title,
2121 '' => ts('- select -'),
2122 ] + CRM_Core_OptionGroup
::values("soft_credit_type")
2124 //CRM-15350: choose SCT field default value as 'Gift' for membership use
2125 //else (for contribution), use configured SCT default value
2126 $SCTDefaultValue = CRM_Core_OptionGroup
::getDefaultValue("soft_credit_type");
2127 if ($field['field_type'] == 'Membership') {
2128 $SCTDefaultValue = CRM_Core_PseudoConstant
::getKey('CRM_Contribute_BAO_ContributionSoft', 'soft_credit_type_id', 'gift');
2130 $form->addElement('hidden', 'sct_default_id', $SCTDefaultValue, ['id' => 'sct_default_id']);
2132 elseif ($fieldName == 'contribution_soft_credit_pcp_id') {
2133 CRM_Contribute_Form_SoftCredit
::addPCPFields($form, "[$rowNumber]");
2135 elseif ($fieldName == 'currency') {
2136 $form->addCurrency($name, $title, $required, NULL, FALSE, FALSE);
2138 elseif ($fieldName == 'contribution_page_id') {
2139 $form->add('select', $name, $title,
2141 '' => ts('- select -'),
2142 ] + CRM_Contribute_PseudoConstant
::contributionPage(), $required, 'class="big"'
2145 elseif ($fieldName == 'activity_status_id') {
2146 $form->add('select', $name, $title,
2148 '' => ts('- select -'),
2149 ] + CRM_Core_PseudoConstant
::activityStatus(), $required
2152 elseif ($fieldName == 'activity_engagement_level') {
2153 $form->add('select', $name, $title,
2155 '' => ts('- select -'),
2156 ] + CRM_Campaign_PseudoConstant
::engagementLevel(), $required
2159 elseif ($fieldName == 'participant_status') {
2161 if ($online == TRUE) {
2162 $cond = 'visibility_id = 1';
2164 $form->add('select', $name, $title,
2166 '' => ts('- select -'),
2167 ] + CRM_Event_PseudoConstant
::participantStatus(NULL, $cond, 'label'), $required
2170 elseif ($fieldName == 'participant_role') {
2171 if (!empty($field['is_multiple'])) {
2172 $form->addCheckBox($name, $title, CRM_Event_PseudoConstant
::participantRole(), NULL, NULL, NULL, NULL, ' ', TRUE);
2175 $form->add('select', $name, $title,
2177 '' => ts('- select -'),
2178 ] + CRM_Event_PseudoConstant
::participantRole(), $required
2182 elseif ($fieldName == 'world_region') {
2183 $form->add('select', $name, $title, CRM_Core_PseudoConstant
::worldRegion(), $required, $selectAttributes);
2185 elseif ($fieldName == 'signature_html') {
2186 $form->add('wysiwyg', $name, $title, CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Email', $fieldName));
2188 elseif ($fieldName == 'signature_text') {
2189 $form->add('textarea', $name, $title, CRM_Core_DAO
::getAttribute('CRM_Core_DAO_Email', $fieldName));
2191 elseif (substr($fieldName, -11) == 'campaign_id') {
2192 if (CRM_Campaign_BAO_Campaign
::isCampaignEnable()) {
2193 $campaigns = CRM_Campaign_BAO_Campaign
::getCampaigns(CRM_Utils_Array
::value($contactId,
2194 $form->_componentCampaigns
2196 $form->add('select', $name, $title,
2198 '' => ts('- select -'),
2199 ] +
$campaigns, $required, 'class="crm-select2 big"'
2203 elseif ($fieldName == 'activity_details') {
2204 $form->add('wysiwyg', $fieldName, $title, ['rows' => 4, 'cols' => 60], $required);
2206 elseif ($fieldName == 'activity_duration') {
2207 $form->add('text', $name, $title, $attributes, $required);
2208 $form->addRule($name, ts('Please enter the duration as number of minutes (integers only).'), 'positiveInteger');
2210 elseif ($fieldName == 'case_status') {
2211 $form->add('select', $name, $title,
2213 '' => ts('- select -'),
2214 ] + CRM_Case_BAO_Case
::buildOptions('case_status_id', 'create'),
2219 if (substr($fieldName, 0, 3) === 'is_' or substr($fieldName, 0, 7) === 'do_not_') {
2220 $form->add('advcheckbox', $name, $title, $attributes, $required);
2222 elseif (CRM_Utils_Array
::value('html_type', $field) === 'Select Date') {
2223 $extra = isset($field['datepicker']) ?
$field['datepicker']['extra'] : CRM_Utils_Date
::getDatePickerExtra($field);
2224 $attributes = isset($field['datepicker']) ?
$field['datepicker']['attributes'] : CRM_Utils_Date
::getDatePickerAttributes($field);
2225 $form->add('datepicker', $name, $title, $attributes, $required, $extra);
2228 $form->add('text', $name, $title, $attributes, $required);
2232 static $hiddenSubtype = FALSE;
2233 if (!$hiddenSubtype && CRM_Contact_BAO_ContactType
::isaSubType($field['field_type'])) {
2234 // In registration mode params are submitted via POST and we don't have any clue
2235 // about profile-id or the profile-type (which could be a subtype)
2236 // To generalize the behavior and simplify the process,
2237 // lets always add the hidden
2238 //subtype value if there is any, and we won't have to
2239 // compute it while processing.
2241 $form->addElement('hidden', $usedFor . '[contact_sub_type]', $field['field_type']);
2244 $form->addElement('hidden', 'contact_sub_type_hidden', $field['field_type']);
2246 $hiddenSubtype = TRUE;
2249 if (($view && $mode != CRM_Profile_Form
::MODE_SEARCH
) ||
$isShared) {
2250 $form->freeze($name);
2254 if (in_array($fieldName, [
2255 'non_deductible_amount',
2260 $form->addRule($name, ts('Please enter a valid amount.'), 'money');
2263 if (!($rule == 'email' && $mode == CRM_Profile_Form
::MODE_SEARCH
)) {
2264 $form->addRule($name, ts('Please enter a valid %1', [1 => $title]), $rule);
2270 * Set profile defaults.
2272 * @param int $contactId
2274 * @param array $fields
2275 * Associative array of fields.
2276 * @param array $defaults
2278 * @param bool $singleProfile
2279 * True for single profile else false(Update multiple items).
2280 * @param int $componentId
2281 * Id for specific components like contribute, event etc.
2282 * @param null $component
2284 public static function setProfileDefaults(
2285 $contactId, &$fields, &$defaults,
2286 $singleProfile = TRUE, $componentId = NULL, $component = NULL
2288 if (!$componentId) {
2289 //get the contact details
2290 $contactDetails = CRM_Contact_BAO_Contact
::getHierContactDetails($contactId, $fields);
2291 $details = $contactDetails[$contactId] ??
NULL;
2292 $multipleFields = ['website' => 'url'];
2294 //start of code to set the default values
2295 foreach ($fields as $name => $field) {
2296 // skip pseudo fields
2297 if (substr($name, 0, 9) == 'phone_ext') {
2301 //set the field name depending upon the profile mode(single/multiple)
2302 if ($singleProfile) {
2306 $fldName = "field[$contactId][$name]";
2309 if ($name == 'group') {
2310 CRM_Contact_Form_Edit_TagsAndGroups
::setDefaults($contactId, $defaults, CRM_Contact_Form_Edit_TagsAndGroups
::GROUP
, $fldName);
2312 if ($name == 'tag') {
2313 CRM_Contact_Form_Edit_TagsAndGroups
::setDefaults($contactId, $defaults, CRM_Contact_Form_Edit_TagsAndGroups
::TAG
, $fldName);
2316 if (!empty($details[$name]) ||
isset($details[$name])) {
2317 //to handle custom data (checkbox) to be written
2318 // to handle birth/deceased date, greeting_type and few other fields
2319 if (in_array($name, CRM_Contact_BAO_Contact
::$_greetingTypes)) {
2320 $defaults[$fldName] = $details[$name . '_id'];
2321 $defaults[$name . '_custom'] = $details[$name . '_custom'];
2323 elseif ($name == 'preferred_communication_method') {
2324 $v = $details[$name];
2325 if (!is_array($details[$name])) {
2326 $v = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $v);
2328 foreach ($v as $item) {
2330 $defaults[$fldName . "[$item]"] = 1;
2334 elseif ($name == 'contact_sub_type') {
2335 $defaults[$fldName] = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, trim($details[$name], CRM_Core_DAO
::VALUE_SEPARATOR
));
2337 elseif ($name == 'world_region') {
2338 $defaults[$fldName] = $details['worldregion_id'];
2340 elseif ($customFieldId = CRM_Core_BAO_CustomField
::getKeyID($name)) {
2341 $defaults[$fldName] = self
::reformatProfileDefaults($field, $details[$name]);
2344 $defaults[$fldName] = $details[$name];
2348 $blocks = ['email', 'phone', 'im', 'openid'];
2349 list($fieldName, $locTypeId, $phoneTypeId) = CRM_Utils_System
::explode('-', $name, 3);
2350 if (!in_array($fieldName, $multipleFields)) {
2351 if (is_array($details)) {
2352 foreach ($details as $key => $value) {
2353 // when we fixed CRM-5319 - get primary loc
2354 // type as per loc field and removed below code.
2355 $primaryLocationType = FALSE;
2356 if ($locTypeId == 'Primary') {
2357 if (is_array($value) && array_key_exists($fieldName, $value)) {
2358 $primaryLocationType = TRUE;
2359 if (in_array($fieldName, $blocks)) {
2360 $locTypeId = CRM_Contact_BAO_Contact
::getPrimaryLocationType($contactId, FALSE, $fieldName);
2363 $locTypeId = CRM_Contact_BAO_Contact
::getPrimaryLocationType($contactId, FALSE, 'address');
2368 // fixed for CRM-665
2369 if (is_numeric($locTypeId)) {
2370 if ($primaryLocationType ||
$locTypeId == CRM_Utils_Array
::value('location_type_id', $value)) {
2371 if (!empty($value[$fieldName])) {
2372 //to handle stateprovince and country
2373 if ($fieldName == 'state_province') {
2374 $defaults[$fldName] = $value['state_province_id'];
2376 elseif ($fieldName == 'county') {
2377 $defaults[$fldName] = $value['county_id'];
2379 elseif ($fieldName == 'country') {
2380 if (!isset($value['country_id']) ||
!$value['country_id']) {
2381 $config = CRM_Core_Config
::singleton();
2382 if ($config->defaultContactCountry
) {
2383 $defaults[$fldName] = $config->defaultContactCountry
;
2387 $defaults[$fldName] = $value['country_id'];
2390 elseif ($fieldName == 'phone') {
2392 if (isset($value['phone'][$phoneTypeId])) {
2393 $defaults[$fldName] = $value['phone'][$phoneTypeId];
2395 if (isset($value['phone_ext'][$phoneTypeId])) {
2396 $defaults[str_replace('phone', 'phone_ext', $fldName)] = $value['phone_ext'][$phoneTypeId];
2400 $phoneDefault = $value['phone'] ??
NULL;
2402 if (!is_array($phoneDefault)) {
2403 $defaults[$fldName] = $phoneDefault;
2407 elseif ($fieldName == 'email') {
2408 //adding the first email (currently we don't support multiple emails of same location type)
2409 $defaults[$fldName] = $value['email'];
2411 elseif ($fieldName == 'im') {
2412 //adding the first im (currently we don't support multiple ims of same location type)
2413 $defaults[$fldName] = $value['im'];
2414 $defaults[$fldName . '-provider_id'] = $value['im_provider_id'];
2417 $defaults[$fldName] = $value[$fieldName];
2420 elseif (substr($fieldName, 0, 14) === 'address_custom' &&
2421 CRM_Utils_Array
::value(substr($fieldName, 8), $value)
2423 $defaults[$fldName] = self
::reformatProfileDefaults($field, $value[substr($fieldName, 8)]);
2428 if (substr($fieldName, 0, 14) === 'address_custom' &&
2429 CRM_Utils_Array
::value(substr($fieldName, 8), $value)
2431 $defaults[$fldName] = self
::reformatProfileDefaults($field, $value[substr($fieldName, 8)]);
2438 if (is_array($details)) {
2439 if ($fieldName === 'url'
2440 && !empty($details['website'])
2441 && !empty($details['website'][$locTypeId])
2443 $defaults[$fldName] = $details['website'][$locTypeId]['url'] ??
NULL;
2451 // Handling Contribution Part of the batch profile
2452 if (CRM_Core_Permission
::access('CiviContribute') && $component == 'Contribute') {
2453 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2456 // Handling Event Participation Part of the batch profile
2457 if (CRM_Core_Permission
::access('CiviEvent') && $component == 'Event') {
2458 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2461 // Handling membership Part of the batch profile
2462 if (CRM_Core_Permission
::access('CiviMember') && $component == 'Membership') {
2463 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2466 // Handling Activity Part of the batch profile
2467 if ($component == 'Activity') {
2468 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2471 // Handling Case Part of the batch profile
2472 if (CRM_Core_Permission
::access('CiviCase') && $component == 'Case') {
2473 self
::setComponentDefaults($fields, $componentId, $component, $defaults);
2478 * Get profiles by type eg: pure Individual etc
2480 * @param array $types
2481 * Associative array of types eg: types('Individual').
2482 * @param bool $onlyPure
2483 * True if only pure profiles are required.
2486 * associative array of profiles
2488 public static function getProfiles($types, $onlyPure = FALSE) {
2490 $ufGroups = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_UFField', 'uf_group_id');
2492 CRM_Utils_Hook
::aclGroup(CRM_Core_Permission
::ADMIN
, NULL, 'civicrm_uf_group', $ufGroups, $ufGroups);
2494 // Exclude Batch Data Entry profiles - CRM-10901
2495 $batchProfiles = CRM_Core_BAO_UFGroup
::getBatchProfiles();
2497 foreach ($ufGroups as $id => $title) {
2498 $ptype = CRM_Core_BAO_UFField
::getProfileType($id, FALSE, $onlyPure);
2499 if (in_array($ptype, $types) && !array_key_exists($id, $batchProfiles)) {
2500 $profiles[$id] = $title;
2507 * Check whether a profile is valid combination of
2508 * required and/or optional profile types
2510 * @param array $required
2511 * Array of types those are required.
2512 * @param array $optional
2513 * Array of types those are optional.
2516 * associative array of profiles
2518 public static function getValidProfiles($required, $optional = NULL) {
2519 if (!is_array($required) ||
empty($required)) {
2524 $ufGroups = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_UFField', 'uf_group_id');
2526 CRM_Utils_Hook
::aclGroup(CRM_Core_Permission
::ADMIN
, NULL, 'civicrm_uf_group', $ufGroups, $ufGroups);
2528 foreach ($ufGroups as $id => $title) {
2529 $type = CRM_Core_BAO_UFField
::checkValidProfileType($id, $required, $optional);
2531 $profiles[$id] = $title;
2539 * Check whether a profile is valid combination of
2540 * required profile fields
2542 * @param array $ufId
2543 * Integer id of the profile.
2544 * @param array $required
2545 * Array of fields those are required in the profile.
2548 * associative array of profiles
2550 public static function checkValidProfile($ufId, $required = NULL) {
2551 $validProfile = FALSE;
2553 return $validProfile;
2556 if (!CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $ufId, 'is_active')) {
2557 return $validProfile;
2560 $profileFields = self
::getFields($ufId, FALSE, CRM_Core_Action
::VIEW
, NULL,
2561 NULL, FALSE, NULL, FALSE, NULL,
2562 CRM_Core_Permission
::CREATE
, NULL
2566 if (!empty($profileFields)) {
2567 $fields = array_keys($profileFields);
2568 foreach ($fields as $val) {
2569 foreach ($required as $key => $field) {
2570 if (strpos($val, $field) === 0) {
2571 unset($required[$key]);
2576 $validProfile = (empty($required));
2579 return $validProfile;
2583 * Get default value for Register.
2585 * @param array $fields
2586 * @param array $defaults
2590 public static function setRegisterDefaults(&$fields, &$defaults) {
2591 $config = CRM_Core_Config
::singleton();
2592 foreach ($fields as $name => $field) {
2593 if (substr($name, 0, 8) == 'country-') {
2594 if (!empty($config->defaultContactCountry
)) {
2595 $defaults[$name] = $config->defaultContactCountry
;
2598 elseif (substr($name, 0, 15) == 'state_province-') {
2599 if (!empty($config->defaultContactStateProvince
)) {
2600 $defaults[$name] = $config->defaultContactStateProvince
;
2608 * make a copy of a profile, including
2609 * all the fields in the profile
2612 * The profile id to copy.
2614 * @return \CRM_Core_DAO
2616 public static function copy($id) {
2617 $maxId = CRM_Core_DAO
::singleValueQuery("SELECT max(id) FROM civicrm_uf_group");
2619 $title = ts('[Copy id %1]', [1 => $maxId +
1]);
2622 'title' => ' ' . $title,
2623 'name' => '__Copy_id_' . ($maxId +
1) . '_',
2627 $copy = CRM_Core_DAO
::copyGeneric('CRM_Core_DAO_UFGroup',
2633 if ($pos = strrpos($copy->name
, "_{$id}")) {
2634 $copy->name
= substr_replace($copy->name
, '', $pos);
2636 $copy->name
= CRM_Utils_String
::munge($copy->name
, '_', 56) . "_{$copy->id}";
2639 $copyUFJoin = CRM_Core_DAO
::copyGeneric('CRM_Core_DAO_UFJoin',
2640 ['uf_group_id' => $id],
2641 ['uf_group_id' => $copy->id
],
2646 $copyUFField = CRM_Core_DAO
::copyGeneric('CRM_Core_BAO_UFField',
2647 ['uf_group_id' => $id],
2648 ['uf_group_id' => $copy->id
]
2651 $maxWeight = CRM_Utils_Weight
::getMax('CRM_Core_DAO_UFJoin', NULL, 'weight');
2655 UPDATE civicrm_uf_join
2657 WHERE uf_group_id = %2
2658 AND ( entity_id IS NULL OR entity_id <= 0 )
2661 1 => [$maxWeight +
1, 'Integer'],
2662 2 => [$copy->id
, 'Integer'],
2664 CRM_Core_DAO
::executeQuery($query, $p);
2665 if ($copy->is_reserved
) {
2666 $query = "UPDATE civicrm_uf_group SET is_reserved = 0 WHERE id = %1";
2667 $params = [1 => [$copy->id
, 'Integer']];
2668 CRM_Core_DAO
::executeQuery($query, $params);
2670 CRM_Utils_Hook
::copy('UFGroup', $copy);
2676 * Process that send notification e-mails
2678 * @param int $contactID
2680 * @param array $values
2681 * Associative array of name/value pair.
2683 public static function commonSendMail($contactID, &$values) {
2684 if (!$contactID ||
!$values) {
2688 $template = CRM_Core_Smarty
::singleton();
2690 $displayName = CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Contact',
2695 self
::profileDisplay($values['id'], $values['values'], $template);
2696 $emailList = explode(',', $values['email']);
2698 $contactLink = CRM_Utils_System
::url('civicrm/contact/view',
2699 "reset=1&cid=$contactID",
2700 TRUE, NULL, FALSE, FALSE, TRUE
2703 //get the default domain email address.
2704 list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain
::getNameAndEmail();
2706 if (!$domainEmailAddress ||
$domainEmailAddress == 'info@EXAMPLE.ORG') {
2707 $fixUrl = CRM_Utils_System
::url('civicrm/admin/domain', 'action=update&reset=1');
2708 CRM_Core_Error
::fatal(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]));
2711 foreach ($emailList as $emailTo) {
2712 // FIXME: take the below out of the foreach loop
2713 CRM_Core_BAO_MessageTemplate
::sendTemplate(
2715 'groupName' => 'msg_tpl_workflow_uf',
2716 'valueName' => 'uf_notify',
2717 'contactId' => $contactID,
2719 'displayName' => $displayName,
2720 'currentDate' => date('r'),
2721 'contactLink' => $contactLink,
2723 'from' => "$domainEmailName <$domainEmailAddress>",
2724 'toEmail' => $emailTo,
2731 * Given a contact id and a group id, returns the field values from the db
2732 * for this group and notify email only if group's notify field is
2733 * set and field values are not empty
2739 * @param array $params
2740 * @param bool $skipCheck
2744 public function checkFieldsEmptyValues($gid, $cid, $params, $skipCheck = FALSE) {
2746 if (CRM_Core_BAO_UFGroup
::filterUFGroups($gid, $cid) ||
$skipCheck) {
2748 $fields = CRM_Core_BAO_UFGroup
::getFields($gid, FALSE, CRM_Core_Action
::VIEW
);
2749 CRM_Core_BAO_UFGroup
::getValues($cid, $fields, $values, FALSE, $params, TRUE);
2751 $email = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'notify');
2753 if (!empty($values) &&
2758 'values' => $values,
2769 * Assign uf fields to template.
2773 * @param array $values
2774 * @param CRM_Core_Smarty $template
2776 public static function profileDisplay($gid, $values, $template) {
2777 $groupTitle = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'title');
2778 $template->assign('grouptitle', $groupTitle);
2779 if (count($values)) {
2780 $template->assign('values', $values);
2785 * Format fields for dupe Contact Matching.
2787 * @param array $params
2789 * @param int $contactId
2792 * associated formatted array
2794 public static function formatFields($params, $contactId = NULL) {
2796 // get the primary location type id and email
2797 list($name, $primaryEmail, $primaryLocationType) = CRM_Contact_BAO_Contact_Location
::getEmailDetails($contactId);
2800 $defaultLocationType = CRM_Core_BAO_LocationType
::getDefault();
2801 $primaryLocationType = $defaultLocationType->id
;
2807 $primaryLocation = 0;
2808 foreach ($params as $key => $value) {
2809 list($fieldName, $locTypeId, $phoneTypeId) = explode('-', $key);
2811 if ($locTypeId == 'Primary') {
2812 $locTypeId = $primaryLocationType;
2815 if (is_numeric($locTypeId)) {
2816 if (!in_array($locTypeId, $locationType)) {
2817 $locationType[$count] = $locTypeId;
2820 $loc = CRM_Utils_Array
::key($locTypeId, $locationType);
2822 $data['location'][$loc]['location_type_id'] = $locTypeId;
2824 // if we are getting in a new primary email, dont overwrite the new one
2825 if ($locTypeId == $primaryLocationType) {
2826 if (!empty($params['email-' . $primaryLocationType])) {
2827 $data['location'][$loc]['email'][$loc]['email'] = $fields['email-' . $primaryLocationType];
2829 elseif (isset($primaryEmail)) {
2830 $data['location'][$loc]['email'][$loc]['email'] = $primaryEmail;
2836 $data['location'][$loc]['is_primary'] = 1;
2838 if ($fieldName == 'phone') {
2840 $data['location'][$loc]['phone'][$loc]['phone_type_id'] = $phoneTypeId;
2843 $data['location'][$loc]['phone'][$loc]['phone_type_id'] = '';
2845 $data['location'][$loc]['phone'][$loc]['phone'] = $value;
2847 elseif ($fieldName == 'email') {
2848 $data['location'][$loc]['email'][$loc]['email'] = $value;
2850 elseif ($fieldName == 'im') {
2851 $data['location'][$loc]['im'][$loc]['name'] = $value;
2854 if ($fieldName === 'state_province') {
2855 $data['location'][$loc]['address']['state_province_id'] = $value;
2857 elseif ($fieldName === 'country') {
2858 $data['location'][$loc]['address']['country_id'] = $value;
2861 $data['location'][$loc]['address'][$fieldName] = $value;
2866 // TODO: prefix, suffix and gender translation may no longer be necessary - check inputs
2867 if ($key === 'individual_suffix') {
2868 $data['suffix_id'] = $value;
2870 elseif ($key === 'individual_prefix') {
2871 $data['prefix_id'] = $value;
2873 elseif ($key === 'gender') {
2874 $data['gender_id'] = $value;
2876 elseif (substr($key, 0, 6) === 'custom') {
2877 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) {
2879 if ($customFields[$customFieldID]['html_type'] == 'CheckBox') {
2880 $value = implode(CRM_Core_DAO
::VALUE_SEPARATOR
, array_keys($value));
2882 // fix the date field
2883 if ($customFields[$customFieldID]['data_type'] == 'Date') {
2884 $date = CRM_Utils_Date
::format($value);
2891 $data['custom'][$customFieldID] = [
2894 'extends' => $customFields[$customFieldID]['extends'],
2895 'type' => $customFields[$customFieldID]['data_type'],
2896 'custom_field_id' => $customFieldID,
2900 elseif ($key == 'edit') {
2904 $data[$key] = $value;
2909 if (!$primaryLocation) {
2911 $data['location'][$loc]['email'][$loc]['email'] = $primaryEmail;
2918 * Calculate the profile type 'group_type' as per profile fields.
2922 * @param bool $includeTypeValues
2923 * @param int $ignoreFieldId
2924 * Ignore particular profile field.
2927 * list of calculated group type
2929 public static function calculateGroupType($gId, $includeTypeValues = FALSE, $ignoreFieldId = NULL) {
2930 //get the profile fields.
2931 $ufFields = self
::getFields($gId, FALSE, NULL, NULL, NULL, TRUE, NULL, TRUE);
2932 return self
::_calculateGroupType($ufFields, $includeTypeValues, $ignoreFieldId);
2936 * Calculate the profile type 'group_type' as per profile fields.
2939 * @param bool $includeTypeValues
2940 * @param int $ignoreFieldId
2941 * Ignore perticular profile field.
2944 * list of calculated group type
2946 public static function _calculateGroupType($ufFields, $includeTypeValues = FALSE, $ignoreFieldId = NULL) {
2947 $groupType = $groupTypeValues = $customFieldIds = [];
2948 if (!empty($ufFields)) {
2949 foreach ($ufFields as $fieldName => $fieldValue) {
2950 //ignore field from group type when provided.
2951 //in case of update profile field.
2952 if ($ignoreFieldId && ($ignoreFieldId == $fieldValue['field_id'])) {
2955 if (!in_array($fieldValue['field_type'], $groupType)) {
2956 $groupType[$fieldValue['field_type']] = $fieldValue['field_type'];
2959 if ($includeTypeValues && ($fldId = CRM_Core_BAO_CustomField
::getKeyID($fieldName))) {
2960 $customFieldIds[$fldId] = $fldId;
2965 if (!empty($customFieldIds)) {
2966 $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) . ')';
2968 $customGroups = CRM_Core_DAO
::executeQuery($query);
2969 while ($customGroups->fetch()) {
2970 if (!$customGroups->extends_entity_column_value
) {
2974 $groupTypeName = "{$customGroups->extends}Type";
2975 if ($customGroups->extends == 'Participant' && $customGroups->extends_entity_column_id
) {
2976 $groupTypeName = CRM_Core_PseudoConstant
::getName('CRM_Core_DAO_CustomGroup', 'extends_entity_column_id', $customGroups->extends_entity_column_id
);
2979 foreach (explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $customGroups->extends_entity_column_value
) as $val) {
2981 $groupTypeValues[$groupTypeName][$val] = $val;
2986 if (!empty($groupTypeValues)) {
2987 $groupType = array_merge($groupType, $groupTypeValues);
2995 * Update the profile type 'group_type' as per profile fields including group types and group subtype values.
2996 * Build and store string like: group_type1,group_type2[VALUE_SEPERATOR]group_type1Type:1:2:3,group_type2Type:1:2
2999 * BirthDate + Email Individual,Contact
3000 * BirthDate + Subject Individual,Activity
3001 * BirthDate + Subject + SurveyOnlyField Individual,Activity\0ActivityType:28
3002 * BirthDate + Subject + SurveyOnlyField + PhoneOnlyField (Not allowed)
3003 * BirthDate + SurveyOnlyField Individual,Activity\0ActivityType:28
3004 * BirthDate + Subject + SurveyOrPhoneField Individual,Activity\0ActivityType:2:28
3005 * BirthDate + SurveyOrPhoneField Individual,Activity\0ActivityType:2:28
3006 * BirthDate + SurveyOrPhoneField + SurveyOnlyField Individual,Activity\0ActivityType:2:28
3007 * BirthDate + StudentField + Subject + SurveyOnlyField Individual,Activity,Student\0ActivityType:28
3010 * @param array $groupTypes
3011 * With key having group type names.
3015 public static function updateGroupTypes($gId, $groupTypes = []) {
3016 if (!is_array($groupTypes) ||
!$gId) {
3020 // If empty group types set group_type as 'null'
3021 if (empty($groupTypes)) {
3022 return CRM_Core_DAO
::setFieldValue('CRM_Core_DAO_UFGroup', $gId, 'group_type', 'null');
3025 $componentGroupTypes = ['Contribution', 'Participant', 'Membership', 'Activity', 'Case'];
3026 $validGroupTypes = array_merge([
3031 ], $componentGroupTypes, CRM_Contact_BAO_ContactType
::subTypes());
3033 $gTypes = $gTypeValues = [];
3035 $participantExtends = ['ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
3036 // Get valid group type and group subtypes
3037 foreach ($groupTypes as $groupType => $value) {
3038 if (in_array($groupType, $validGroupTypes) && !in_array($groupType, $gTypes)) {
3039 $gTypes[] = $groupType;
3044 if (in_array($groupType, $participantExtends)) {
3045 $subTypesOf = $groupType;
3047 elseif (strpos($groupType, 'Type') > 0) {
3048 $subTypesOf = substr($groupType, 0, strpos($groupType, 'Type'));
3054 if (!empty($value) &&
3055 (in_array($subTypesOf, $componentGroupTypes) ||
3056 in_array($subTypesOf, $participantExtends)
3059 $gTypeValues[$subTypesOf] = $groupType . ":" . implode(':', $value);
3063 if (empty($gTypes)) {
3067 // Build String to store group types and group subtypes
3068 $groupTypeString = implode(',', $gTypes);
3069 if (!empty($gTypeValues)) {
3070 $groupTypeString .= CRM_Core_DAO
::VALUE_SEPARATOR
. implode(',', $gTypeValues);
3073 return CRM_Core_DAO
::setFieldValue('CRM_Core_DAO_UFGroup', $gId, 'group_type', $groupTypeString);
3077 * Create a "group_type" string.
3079 * @param array $coreTypes
3080 * E.g. array('Individual','Contact','Student').
3081 * @param array $subTypes
3082 * E.g. array('ActivityType' => array(7, 11)).
3083 * @param string $delim
3086 * @throws CRM_Core_Exception
3088 public static function encodeGroupType($coreTypes, $subTypes, $delim = CRM_Core_DAO
::VALUE_SEPARATOR
) {
3089 $groupTypeExpr = '';
3091 $groupTypeExpr .= implode(',', $coreTypes);
3094 //CRM-15427 Allow Multiple subtype filtering
3095 //if (count($subTypes) > 1) {
3096 //throw new CRM_Core_Exception("Multiple subtype filtering is not currently supported by widget.");
3098 foreach ($subTypes as $subType => $subTypeIds) {
3099 $groupTypeExpr .= $delim . $subType . ':' . implode(':', $subTypeIds);
3102 return $groupTypeExpr;
3106 * setDefault componet specific profile fields.
3108 * @param array $fields
3110 * @param int $componentId
3112 * @param string $component
3114 * @param array $defaults
3115 * An array of default values.
3117 * @param bool $isStandalone
3119 public static function setComponentDefaults(&$fields, $componentId, $component, &$defaults, $isStandalone = FALSE) {
3120 if (!$componentId ||
3121 !in_array($component, ['Contribute', 'Membership', 'Event', 'Activity', 'Case'])
3126 $componentBAO = $componentSubType = NULL;
3127 switch ($component) {
3129 $componentBAO = 'CRM_Member_BAO_Membership';
3130 $componentBAOName = 'Membership';
3131 $componentSubType = ['membership_type_id'];
3135 $componentBAO = 'CRM_Contribute_BAO_Contribution';
3136 $componentBAOName = 'Contribution';
3137 $componentSubType = ['financial_type_id'];
3141 $componentBAO = 'CRM_Event_BAO_Participant';
3142 $componentBAOName = 'Participant';
3143 $componentSubType = ['role_id', 'event_id', 'event_type_id'];
3147 $componentBAO = 'CRM_Activity_BAO_Activity';
3148 $componentBAOName = 'Activity';
3149 $componentSubType = ['activity_type_id'];
3153 $componentBAO = 'CRM_Case_BAO_Case';
3154 $componentBAOName = 'Case';
3155 $componentSubType = ['case_type_id'];
3160 $params = ['id' => $componentId];
3162 //get the component values.
3163 CRM_Core_DAO
::commonRetrieve($componentBAO, $params, $values);
3164 if ($componentBAOName == 'Participant') {
3165 $values +
= ['event_type_id' => CRM_Core_DAO
::getFieldValue('CRM_Event_DAO_Event', $values['event_id'], 'event_type_id')];
3168 $formattedGroupTree = [];
3170 foreach ($fields as $name => $field) {
3171 $fldName = $isStandalone ?
$name : "field[$componentId][$name]";
3172 if (array_key_exists($name, $values)) {
3173 $defaults[$fldName] = $values[$name];
3175 elseif ($name == 'participant_note') {
3176 $noteDetails = CRM_Core_BAO_Note
::getNote($componentId, 'civicrm_participant');
3177 $defaults[$fldName] = array_pop($noteDetails);
3179 elseif (in_array($name, [
3181 'payment_instrument',
3182 'participant_status',
3185 $defaults[$fldName] = $values["{$name}_id"];
3187 elseif ($name == 'membership_type') {
3188 // since membership_type field is a hierselect -
3189 $defaults[$fldName][0]
3190 = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_MembershipType', $values['membership_type_id'], 'member_of_contact_id', 'id');
3191 $defaults[$fldName][1] = $values['membership_type_id'];
3193 elseif ($name == 'membership_status') {
3194 $defaults[$fldName] = $values['status_id'];
3196 elseif ($name == 'case_status') {
3197 $defaults[$fldName] = $values['case_status_id'];
3199 elseif (CRM_Core_BAO_CustomField
::getKeyID($name, TRUE) !== [NULL, NULL]) {
3200 if (empty($formattedGroupTree)) {
3201 //get the groupTree as per subTypes.
3203 foreach ($componentSubType as $subType) {
3204 $subTree = CRM_Core_BAO_CustomGroup
::getTree($componentBAOName, NULL,
3205 $componentId, 0, $values[$subType]
3207 $groupTree = CRM_Utils_Array
::crmArrayMerge($groupTree, $subTree);
3209 $formattedGroupTree = CRM_Core_BAO_CustomGroup
::formatGroupTree($groupTree, 1);
3210 CRM_Core_BAO_CustomGroup
::setDefaults($formattedGroupTree, $defaults);
3213 //FIX ME: We need to loop defaults, but once we move to custom_1_x convention this code can be simplified.
3214 foreach ($defaults as $customKey => $customValue) {
3215 if ($customFieldDetails = CRM_Core_BAO_CustomField
::getKeyID($customKey, TRUE)) {
3216 if ($name == 'custom_' . $customFieldDetails[0]) {
3218 //hack to set default for checkbox
3219 //basically this is for weired field name like field[33][custom_19]
3220 //we are converting this field name to array structure and assign value.
3223 foreach ($formattedGroupTree as $tree) {
3224 if (!empty($tree['fields'][$customFieldDetails[0]])) {
3225 if ('CheckBox' == CRM_Utils_Array
::value('html_type', $tree['fields'][$customFieldDetails[0]])) {
3227 $defaults['field'][$componentId][$name] = $customValue;
3230 elseif (CRM_Utils_Array
::value('data_type', $tree['fields'][$customFieldDetails[0]]) == 'Date') {
3233 // CRM-6681, $default contains formatted date, time values.
3234 $defaults[$fldName] = $customValue;
3235 if (!empty($defaults[$customKey . '_time'])) {
3236 $defaults['field'][$componentId][$name . '_time'] = $defaults[$customKey . '_time'];
3242 if (!$skipValue ||
$isStandalone) {
3243 $defaults[$fldName] = $customValue;
3245 unset($defaults[$customKey]);
3251 elseif (isset($values[$fldName])) {
3252 $defaults[$fldName] = $values[$fldName];
3258 * Retrieve groups of profiles.
3260 * @param int $profileID
3261 * Id of the profile.
3266 public static function profileGroups($profileID) {
3268 $profileTypes = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $profileID, 'group_type');
3269 if ($profileTypes) {
3270 $groupTypeParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $profileTypes);
3271 $groupTypes = explode(',', $groupTypeParts[0]);
3277 * Alter contact params by filtering existing subscribed groups and returns
3278 * unsubscribed groups array for subscription.
3280 * @param array $params
3282 * @param int $contactId
3286 * This contains array of groups for subscription
3288 public static function getDoubleOptInGroupIds(&$params, $contactId = NULL) {
3289 $config = CRM_Core_Config
::singleton();
3290 $subscribeGroupIds = [];
3292 // process further only if profileDoubleOptIn enabled and if groups exist
3293 if (!array_key_exists('group', $params) ||
3294 !self
::isProfileDoubleOptin() ||
3295 CRM_Utils_System
::isNull($params['group'])
3297 return $subscribeGroupIds;
3300 //check if contact email exist.
3302 foreach ($params as $name => $value) {
3303 if (strpos($name, 'email-') !== FALSE) {
3309 //Proceed furthur only if email present
3311 return $subscribeGroupIds;
3314 //do check for already subscriptions.
3315 $contactGroups = [];
3319 FROM civicrm_group_contact
3320 WHERE status = 'Added'
3321 AND contact_id = %1";
3323 $dao = CRM_Core_DAO
::executeQuery($query, [1 => [$contactId, 'Integer']]);
3324 while ($dao->fetch()) {
3325 $contactGroups[$dao->group_id
] = $dao->group_id
;
3329 //since we don't have names, compare w/ label.
3330 $mailingListGroupType = array_search('Mailing List', CRM_Core_OptionGroup
::values('group_type'));
3332 //actual processing start.
3333 foreach ($params['group'] as $groupId => $isSelected) {
3334 //unset group those are not selected.
3336 unset($params['group'][$groupId]);
3340 $groupTypes = explode(CRM_Core_DAO
::VALUE_SEPARATOR
,
3341 CRM_Core_DAO
::getFieldValue('CRM_Contact_DAO_Group', $groupId, 'group_type', 'id')
3343 //get only mailing type group and unset it from params
3344 if (in_array($mailingListGroupType, $groupTypes) && !in_array($groupId, $contactGroups)) {
3345 $subscribeGroupIds[$groupId] = $groupId;
3346 unset($params['group'][$groupId]);
3350 return $subscribeGroupIds;
3354 * Check if we are rendering mixed profiles.
3356 * @param array $profileIds
3357 * Associated array of profile ids.
3360 * true if profile is mixed
3362 public static function checkForMixProfiles($profileIds) {
3363 $mixProfile = FALSE;
3365 $contactTypes = ['Individual', 'Household', 'Organization'];
3366 $subTypes = CRM_Contact_BAO_ContactType
::subTypes();
3368 $components = ['Contribution', 'Participant', 'Membership', 'Activity'];
3370 $typeCount = ['ctype' => [], 'subtype' => []];
3371 foreach ($profileIds as $gid) {
3372 $profileType = CRM_Core_BAO_UFField
::getProfileType($gid);
3373 // ignore profile of type Contact
3374 if ($profileType == 'Contact') {
3377 if (in_array($profileType, $contactTypes)) {
3378 if (!isset($typeCount['ctype'][$profileType])) {
3379 $typeCount['ctype'][$profileType] = 1;
3382 // check if we are rendering profile of different contact types
3383 if (count($typeCount['ctype']) == 2) {
3388 elseif (in_array($profileType, $components)) {
3393 if (!isset($typeCount['subtype'][$profileType])) {
3394 $typeCount['subtype'][$profileType] = 1;
3396 // check if we are rendering profile of different contact sub types
3397 if (count($typeCount['subtype']) == 2) {
3407 * Determine of we show overlay profile or not.
3410 * true if profile should be shown else false
3412 public static function showOverlayProfile() {
3413 $showOverlay = TRUE;
3415 // get the id of overlay profile
3416 $overlayProfileId = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', 'summary_overlay', 'id', 'name');
3417 $query = "SELECT count(id) FROM civicrm_uf_field WHERE uf_group_id = {$overlayProfileId} AND visibility IN ('Public Pages', 'Public Pages and Listings') ";
3419 $count = CRM_Core_DAO
::singleValueQuery($query);
3421 //check if there are no public fields and use is anonymous
3422 $session = CRM_Core_Session
::singleton();
3423 if (!$count && !$session->get('userID')) {
3424 $showOverlay = FALSE;
3427 return $showOverlay;
3431 * Get group type values of the profile.
3433 * @param int $profileId
3434 * @param string $groupType
3439 public static function groupTypeValues($profileId, $groupType = NULL) {
3440 $groupTypeValue = [];
3441 $groupTypes = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_UFGroup', $profileId, 'group_type');
3443 $groupTypeParts = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $groupTypes);
3444 if (empty($groupTypeParts[1])) {
3445 return $groupTypeValue;
3447 $participantExtends = ['ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
3449 foreach (explode(',', $groupTypeParts[1]) as $groupTypeValues) {
3451 $valueParts = explode(':', $groupTypeValues);
3453 ($valueParts[0] != "{$groupType}Type" ||
3454 ($groupType == 'Participant' &&
3455 !in_array($valueParts[0], $participantExtends)
3461 foreach ($valueParts as $val) {
3462 if (CRM_Utils_Rule
::integer($val)) {
3463 $values[$val] = $val;
3466 if (!empty($values)) {
3467 $typeName = substr($valueParts[0], 0, -4);
3468 if (in_array($valueParts[0], $participantExtends)) {
3469 $typeName = $valueParts[0];
3471 $groupTypeValue[$typeName] = $values;
3475 return $groupTypeValue;
3479 * @return bool|object
3481 public static function isProfileDoubleOptin() {
3482 // check for double optin
3483 $config = CRM_Core_Config
::singleton();
3484 if (in_array('CiviMail', $config->enableComponents
)) {
3485 return Civi
::settings()->get('profile_double_optin');
3491 * @return bool|object
3493 public static function isProfileAddToGroupDoubleOptin() {
3494 // check for add to group double optin
3495 $config = CRM_Core_Config
::singleton();
3496 if (in_array('CiviMail', $config->enableComponents
)) {
3497 return Civi
::settings()->get('profile_add_to_group_double_optin');
3503 * Get profiles used for batch entry.
3506 * profileIds profile ids
3508 public static function getBatchProfiles() {
3510 FROM civicrm_uf_group
3511 WHERE name IN ('contribution_batch_entry', 'membership_batch_entry')";
3512 $dao = CRM_Core_DAO
::executeQuery($query);
3514 while ($dao->fetch()) {
3515 $profileIds[$dao->id
] = $dao->id
;
3522 * @param $destination
3523 * @param bool $returnMultiSummaryFields
3525 * @return array|null
3526 * @todo what do I do?
3528 public static function shiftMultiRecordFields(&$source, &$destination, $returnMultiSummaryFields = FALSE) {
3529 $multiSummaryFields = $returnMultiSummaryFields ?
[] : NULL;
3530 foreach ($source as $field => $properties) {
3531 if (!CRM_Core_BAO_CustomField
::getKeyID($field)) {
3534 if (CRM_Core_BAO_CustomField
::isMultiRecordField($field)) {
3535 $destination[$field] = $properties;
3536 if ($returnMultiSummaryFields) {
3537 if ($properties['is_multi_summary']) {
3538 $multiSummaryFields[$field] = $properties;
3541 unset($source[$field]);
3544 return $multiSummaryFields;
3548 * This is function is used to format pseudo fields.
3550 * @param array $fields
3551 * Associated array of profile fields.
3554 public static function reformatProfileFields(&$fields) {
3555 //reformat fields array
3556 foreach ($fields as $name => $field) {
3557 //reformat phone and extension field
3558 if (substr($field['name'], 0, 13) == 'phone_and_ext') {
3559 $fieldSuffix = str_replace('phone_and_ext-', '', $field['name']);
3561 // retain existing element properties and just update and replace key
3562 CRM_Utils_Array
::crmReplaceKey($fields, $name, "phone-{$fieldSuffix}");
3563 $fields["phone-{$fieldSuffix}"]['name'] = "phone-{$fieldSuffix}";
3564 $fields["phone-{$fieldSuffix}"]['where'] = 'civicrm_phone.phone';
3566 // add additional phone extension field
3567 $fields["phone_ext-{$fieldSuffix}"] = $field;
3568 $fields["phone_ext-{$fieldSuffix}"]['title'] = $field['title'] . ' - ' . ts('Ext.');
3569 $fields["phone_ext-{$fieldSuffix}"]['name'] = "phone_ext-{$fieldSuffix}";
3570 $fields["phone_ext-{$fieldSuffix}"]['where'] = 'civicrm_phone.phone_ext';
3571 $fields["phone_ext-{$fieldSuffix}"]['skipDisplay'] = 1;
3572 //ignore required for extension field
3573 $fields["phone_ext-{$fieldSuffix}"]['is_required'] = 0;
3579 * Get the frontend_title for the profile, falling back on 'title' if none.
3581 * @param int $profileID
3585 * @throws \CiviCRM_API3_Exception
3587 public static function getFrontEndTitle(int $profileID) {
3588 $profile = civicrm_api3('UFGroup', 'getsingle', ['id' => $profileID, 'return' => ['title', 'frontend_title']]);
3589 return $profile['frontend_title'] ??
$profile['title'];
3593 * This function is used to format the profile default values.
3595 * @param array $field
3596 * Associated array of profile fields to render.
3597 * @param string $value
3601 * String or array, depending on the html type
3603 public static function reformatProfileDefaults($field, $value) {
3606 switch ($field['html_type']) {
3607 case 'Multi-Select State/Province':
3608 case 'Multi-Select Country':
3609 case 'Multi-Select':
3610 $v = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, trim($value));
3611 foreach ($v as $item) {
3613 $defaults[$item] = $item;
3619 $v = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, $value);
3620 foreach ($v as $item) {
3622 $defaults[$item] = 1;
3623 // seems like we need this for QF style checkboxes in profile where its multiindexed
3625 $defaults["[{$item}]"] = 1;