Merge pull request #19087 from civicrm/5.32
[civicrm-core.git] / CRM / Core / BAO / UFGroup.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 /**
19 * UF group BAO class.
20 */
21 class CRM_Core_BAO_UFGroup extends CRM_Core_DAO_UFGroup {
22
23 const PUBLIC_VISIBILITY = 1,
24 ADMIN_VISIBILITY = 2,
25 LISTINGS_VISIBILITY = 4;
26
27 /**
28 * Cache the match clause used in this transaction.
29 *
30 * @var string
31 */
32 public static $_matchFields = NULL;
33
34 /**
35 * Fetch object based on array of properties.
36 *
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.
41 *
42 * @return object
43 * CRM_Core_DAO_UFGroup object
44 */
45 public static function retrieve(&$params, &$defaults) {
46 return CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_UFGroup', $params, $defaults);
47 }
48
49 /**
50 * Retrieve the first non-generic contact type
51 *
52 * @param int $id
53 * Id of uf_group.
54 *
55 * @return string
56 * contact type
57 */
58 public static function getContactType($id) {
59
60 $validTypes = array_filter(array_keys(CRM_Core_SelectValues::contactType()));
61 $validSubTypes = CRM_Contact_BAO_ContactType::subTypeInfo();
62
63 $typesParts = explode(CRM_Core_DAO::VALUE_SEPARATOR, CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $id, 'group_type'));
64 $types = explode(',', $typesParts[0]);
65
66 $cType = NULL;
67 foreach ($types as $type) {
68 if (in_array($type, $validTypes)) {
69 $cType = $type;
70 }
71 elseif (array_key_exists($type, $validSubTypes)) {
72 $cType = $validSubTypes[$type]['parent'] ?? NULL;
73 }
74 if ($cType) {
75 break;
76 }
77 }
78
79 return $cType;
80 }
81
82 /**
83 * Get the form title.
84 *
85 * @param int $id
86 * Id of uf_form.
87 *
88 * @return string
89 * title
90 *
91 */
92 public static function getTitle($id) {
93 return CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $id, 'title');
94 }
95
96 /**
97 * Update the is_active flag in the db.
98 *
99 * @param int $id
100 * Id of the database record.
101 * @param bool $is_active
102 * Value we want to set the is_active field.
103 *
104 * @return bool
105 * true if we found and updated the object, else false
106 */
107 public static function setIsActive($id, $is_active) {
108 return CRM_Core_DAO::setFieldValue('CRM_Core_DAO_UFGroup', $id, 'is_active', $is_active);
109 }
110
111 /**
112 * Get all the registration fields.
113 *
114 * @param int $action
115 * What action are we doing.
116 * @param int $mode
117 * Mode.
118 *
119 * @param string $ctype
120 *
121 * @return array
122 * the fields that are needed for registration
123 *
124 * @throws \Exception
125 */
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');
129 }
130 else {
131 $ufGroups = CRM_Core_BAO_UFGroup::getModuleUFGroup('Profile');
132 }
133
134 if (!is_array($ufGroups)) {
135 return FALSE;
136 }
137
138 $fields = [];
139
140 foreach ($ufGroups as $id => $title) {
141 if ($ctype) {
142 $fieldType = CRM_Core_BAO_UFField::getProfileType($id);
143 if (($fieldType != 'Contact') &&
144 ($fieldType != $ctype) &&
145 !CRM_Contact_BAO_ContactType::isExtendsContactType($fieldType, $ctype)
146 ) {
147 continue;
148 }
149 if (CRM_Contact_BAO_ContactType::isaSubType($fieldType)) {
150 $profileSubType = $fieldType;
151 }
152 }
153
154 $subset = self::getFields($id, TRUE, $action,
155 NULL, NULL, FALSE, NULL, TRUE, $ctype
156 );
157
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;
162 }
163 }
164 }
165
166 return $fields;
167 }
168
169 /**
170 * Get all the listing fields.
171 *
172 * @param int $action
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
180 *
181 * @param null $restrict
182 * @param bool $skipPermission
183 * @param int $permissionType
184 *
185 * @return array
186 * the fields that are listings related
187 *
188 * @throws \Exception
189 */
190 public static function getListingFields(
191 $action,
192 $visibility,
193 $considerSelector = FALSE,
194 $ufGroupIds = NULL,
195 $searchable = NULL,
196 $restrict = NULL,
197 $skipPermission = FALSE,
198 $permissionType = CRM_Core_Permission::SEARCH
199 ) {
200 if ($ufGroupIds) {
201 $subset = self::getFields($ufGroupIds, FALSE, $action,
202 $visibility, $searchable,
203 FALSE, $restrict,
204 $skipPermission,
205 NULL,
206 $permissionType
207 );
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]);
213 }
214 }
215 }
216 $fields = $subset;
217 }
218 else {
219 $ufGroups = CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id');
220
221 $fields = [];
222 foreach ($ufGroups as $id => $title) {
223 $subset = self::getFields($id, FALSE, $action,
224 $visibility, $searchable,
225 FALSE, $restrict,
226 $skipPermission,
227 NULL,
228 $permissionType
229 );
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]);
235 }
236 }
237 }
238 $fields = array_merge($fields, $subset);
239 }
240 }
241 return $fields;
242 }
243
244 /**
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
247 * formatUFFields().
248 *
249 * @param int $id
250 * The id of the UF group or ids of ufgroup.
251 * @param bool|int $register are we interested in registration fields
252 * @param int $action
253 * What action are we doing.
254 * @param int $visibility
255 * Visibility of fields we are interested in.
256 * @param $searchable
257 * @param bool $showAll
258 * @param string $restrict
259 * Should we restrict based on a specified profile type.
260 * @param bool $skipPermission
261 * @param null $ctype
262 * @param int $permissionType
263 * @param string $orderBy
264 * @param null $orderProfiles
265 *
266 * @param bool $eventProfile
267 *
268 * @return array
269 * The fields that belong to this ufgroup(s)
270 *
271 * @throws \CRM_Core_Exception
272 */
273 public static function getFields(
274 $id,
275 $register = FALSE,
276 $action = NULL,
277 $visibility = NULL,
278 $searchable = NULL,
279 $showAll = FALSE,
280 $restrict = NULL,
281 $skipPermission = FALSE,
282 $ctype = NULL,
283 $permissionType = CRM_Core_Permission::CREATE,
284 $orderBy = 'field_name',
285 $orderProfiles = NULL,
286 $eventProfile = FALSE
287 ) {
288 if (!is_array($id)) {
289 $id = CRM_Utils_Type::escape($id, 'Positive');
290 $profileIds = [$id];
291 }
292 else {
293 $profileIds = $id;
294 }
295
296 $gids = implode(',', $profileIds);
297 $params = [];
298 if ($restrict) {
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 )
303 ";
304 $params = [1 => [$restrict, 'String']];
305 }
306 else {
307 $query = "SELECT g.* from civicrm_uf_group g WHERE g.id IN ( {$gids} ) ";
308 }
309
310 if (!$showAll) {
311 $query .= " AND g.is_active = 1";
312 }
313
314 $checkPermission = [
315 [
316 'administer CiviCRM',
317 'manage event profiles',
318 ],
319 ];
320 if ($eventProfile && CRM_Core_Permission::check($checkPermission)) {
321 $skipPermission = TRUE;
322 }
323
324 // add permissioning for profiles only if not registration
325 if (!$skipPermission) {
326 $permissionClause = CRM_Core_Permission::ufGroupClause($permissionType, 'g.');
327 $query .= " AND $permissionClause ";
328 }
329
330 if ($orderProfiles and count($profileIds) > 1) {
331 $query .= " ORDER BY FIELD( g.id, {$gids} )";
332 }
333 $group = CRM_Core_DAO::executeQuery($query, $params);
334 $fields = [];
335 $validGroup = FALSE;
336
337 while ($group->fetch()) {
338 $validGroup = TRUE;
339 $query = self::createUFFieldQuery($group->id, $searchable, $showAll, $visibility, $orderBy);
340 $field = CRM_Core_DAO::executeQuery($query);
341
342 $importableFields = self::getProfileFieldMetadata($showAll);
343 list($customFields, $addressCustomFields) = self::getCustomFields($ctype);
344
345 while ($field->fetch()) {
346 list($name, $formattedField) = self::formatUFField($group, $field, $customFields, $addressCustomFields, $importableFields, $permissionType);
347 if ($formattedField !== NULL) {
348 $fields[$name] = $formattedField;
349 }
350 }
351 }
352
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)]
356 ));
357 }
358 else {
359 self::reformatProfileFields($fields);
360 }
361
362 return $fields;
363 }
364
365 /**
366 * Format a list of UFFields for use with buildProfile. This is the in-memory analog
367 * of getFields().
368 *
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
377 * @param null $ctype
378 * @param int $permissionType
379 *
380 * @return array
381 * @see self::getFields
382 */
383 public static function formatUFFields(
384 $groupArr,
385 $fieldArrs,
386 $visibility = NULL,
387 $searchable = NULL,
388 $showAll = FALSE,
389 $ctype = NULL,
390 $permissionType = CRM_Core_Permission::CREATE
391 ) {
392 // $group = new CRM_Core_DAO_UFGroup();
393 // $group->copyValues($groupArr); // no... converts string('') to string('null')
394 $group = (object) $groupArr;
395
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);
404
405 $formattedFields = [];
406 foreach ($fieldArrs as $fieldArr) {
407 $field = (object) $fieldArr;
408 if (!self::filterUFField($field, $searchable, $showAll, $visibility)) {
409 continue;
410 }
411
412 list($name, $formattedField) = self::formatUFField($group, $field, $customFields, $addressCustomFields, $importableFields, $permissionType);
413 if ($formattedField !== NULL) {
414 $formattedFields[$name] = $formattedField;
415 }
416 }
417 return $formattedFields;
418 }
419
420 /**
421 * Prepare a field for rendering with CRM_Core_BAO_UFGroup::buildProfile.
422 *
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.
430 *
431 * @return array
432 */
433 protected static function formatUFField(
434 $group,
435 $field,
436 $customFields,
437 $addressCustomFields,
438 $importableFields,
439 $permissionType = CRM_Core_Permission::CREATE
440 ) {
441 $name = $field->field_name;
442 $title = $field->label;
443
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))
447 ) {
448 $addressCustom = TRUE;
449 $name = "address_{$name}";
450 }
451 if ($field->field_name == 'url') {
452 $name .= "-{$field->website_type_id}";
453 }
454 elseif (!empty($field->location_type_id)) {
455 $name .= "-{$field->location_type_id}";
456 }
457 else {
458 $locationFields = self::getLocationFields();
459 if (in_array($field->field_name, $locationFields) || $addressCustom) {
460 $name .= '-Primary';
461 }
462 }
463
464 if (isset($field->phone_type_id)) {
465 $name .= "-{$field->phone_type_id}";
466 }
467 $fieldMetaData = CRM_Utils_Array::value($name, $importableFields, ($importableFields[$field->field_name] ?? []));
468
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
471 $formattedField = [
472 'name' => $name,
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,
478 'title' => $title,
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(
497 'pseudoconstant',
498 CRM_Utils_Array::value($field->field_name, $importableFields)
499 ),
500 // obsolete this when we remove the name / dbName discrepancy with gender/suffix/prefix
501 'dbName' => CRM_Utils_Array::value(
502 'dbName',
503 CRM_Utils_Array::value($field->field_name, $importableFields)
504 ),
505 'skipDisplay' => 0,
506 'data_type' => CRM_Utils_Type::getDataTypeFromFieldMetadata($fieldMetaData),
507 'bao' => $fieldMetaData['bao'] ?? NULL,
508 ];
509
510 $formattedField = CRM_Utils_Date::addDateMetadataToField($fieldMetaData, $formattedField);
511
512 //adding custom field property
513 if (substr($field->field_name, 0, 6) == 'custom' ||
514 substr($field->field_name, 0, 14) === 'address_custom'
515 ) {
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['serialize'] = !empty($customFields[$field->field_name]['serialize']);
520 $formattedField['is_search_range'] = $customFields[$field->field_name]['is_search_range'];
521 // fix for CRM-1994
522 $formattedField['options_per_line'] = $customFields[$field->field_name]['options_per_line'];
523 $formattedField['html_type'] = $customFields[$field->field_name]['html_type'];
524
525 if (CRM_Utils_Array::value('html_type', $formattedField) == 'Select Date') {
526 $formattedField['date_format'] = $customFields[$field->field_name]['date_format'];
527 $formattedField['time_format'] = $customFields[$field->field_name]['time_format'];
528 $formattedField['is_datetime_field'] = TRUE;
529 $formattedField['smarty_view_format'] = CRM_Utils_Date::getDateFieldViewFormat($formattedField['date_format']);
530 }
531
532 $formattedField['is_multi_summary'] = $field->is_multi_summary;
533 return [$name, $formattedField];
534 }
535 else {
536 $formattedField = NULL;
537 return [$name, $formattedField];
538 }
539 }
540 return [$name, $formattedField];
541 }
542
543 /**
544 * Create a query to find all visible UFFields in a UFGroup.
545 *
546 * This is the SQL-variant of checkUFFieldDisplayable().
547 *
548 * @param int $groupId
549 * @param bool $searchable
550 * @param bool $showAll
551 * @param int $visibility
552 * @param string $orderBy
553 * Comma-delimited list of SQL columns.
554 *
555 * @return string
556 */
557 protected static function createUFFieldQuery($groupId, $searchable, $showAll, $visibility, $orderBy) {
558 $where = " WHERE uf_group_id = {$groupId}";
559
560 if ($searchable) {
561 $where .= " AND is_searchable = 1";
562 }
563
564 if (!$showAll) {
565 $where .= " AND is_active = 1";
566 }
567
568 if ($visibility) {
569 $clause = [];
570 if ($visibility & self::PUBLIC_VISIBILITY) {
571 $clause[] = 'visibility = "Public Pages"';
572 }
573 if ($visibility & self::ADMIN_VISIBILITY) {
574 $clause[] = 'visibility = "User and User Admin Only"';
575 }
576 if ($visibility & self::LISTINGS_VISIBILITY) {
577 $clause[] = 'visibility = "Public Pages and Listings"';
578 }
579 if (!empty($clause)) {
580 $where .= ' AND ( ' . implode(' OR ', $clause) . ' ) ';
581 }
582 }
583
584 $query = "SELECT * FROM civicrm_uf_field $where ORDER BY weight";
585 if ($orderBy) {
586 $query .= ", " . $orderBy;
587 return $query;
588 }
589 return $query;
590 }
591
592 /**
593 * Create a query to find all visible UFFields in a UFGroup.
594 *
595 * This is the PHP in-memory variant of createUFFieldQuery().
596 *
597 * @param CRM_Core_DAO_UFField|CRM_Core_DAO $field
598 * @param bool $searchable
599 * @param bool $showAll
600 * @param int $visibility
601 *
602 * @return bool
603 * TRUE if field is displayable
604 */
605 protected static function filterUFField($field, $searchable, $showAll, $visibility) {
606 if ($searchable && $field->is_searchable != 1) {
607 return FALSE;
608 }
609
610 if (!$showAll && $field->is_active != 1) {
611 return FALSE;
612 }
613
614 if ($visibility) {
615 $allowedVisibilities = [];
616 if ($visibility & self::PUBLIC_VISIBILITY) {
617 $allowedVisibilities[] = 'Public Pages';
618 }
619 if ($visibility & self::ADMIN_VISIBILITY) {
620 $allowedVisibilities[] = 'User and User Admin Only';
621 }
622 if ($visibility & self::LISTINGS_VISIBILITY) {
623 $allowedVisibilities[] = 'Public Pages and Listings';
624 }
625 // !empty($allowedVisibilities) seems silly to me, but it is equivalent to the pre-existing SQL
626 if (!empty($allowedVisibilities) && !in_array($field->visibility, $allowedVisibilities)) {
627 return FALSE;
628 }
629 }
630
631 return TRUE;
632 }
633
634 /**
635 * Get a list of filtered field metadata.
636 *
637 * @param $showAll
638 * @param $profileType
639 * @param $contactActivityProfile
640 * @param bool $filterMode
641 * Filter mode means you are using importable fields for filtering rather than just getting metadata.
642 * With filter mode = FALSE BOTH activity fields and component fields are returned.
643 * I can't see why you would ever want to use this function in filter mode as the component fields are
644 * still unfiltered. However, I feel scared enough to leave it as it is. I have marked this function as
645 * deprecated and am recommending the wrapper 'getProfileFieldMetadata' in order to try to
646 * send this confusion to history.
647 *
648 * @return array
649 * @deprecated use getProfileFieldMetadata
650 *
651 */
652 protected static function getImportableFields($showAll, $profileType, $contactActivityProfile, $filterMode = TRUE) {
653 if (!$showAll) {
654 $importableFields = CRM_Contact_BAO_Contact::importableFields('All', FALSE, FALSE, FALSE, TRUE, TRUE);
655 }
656 else {
657 $importableFields = CRM_Contact_BAO_Contact::importableFields('All', FALSE, TRUE, FALSE, TRUE, TRUE);
658 }
659
660 $activityFields = CRM_Activity_BAO_Activity::getProfileFields();
661 $componentFields = CRM_Core_Component::getQueryFields();
662 if ($filterMode == TRUE) {
663 if ($profileType == 'Activity' || $contactActivityProfile) {
664 $importableFields = array_merge($importableFields, $activityFields);
665 }
666 else {
667 $importableFields = array_merge($importableFields, $componentFields);
668 }
669 }
670 else {
671 $importableFields = array_merge($importableFields, $activityFields, $componentFields);
672 }
673
674 $importableFields['group']['title'] = ts('Group(s)');
675 $importableFields['group']['where'] = NULL;
676 $importableFields['tag']['title'] = ts('Tag(s)');
677 $importableFields['tag']['where'] = NULL;
678 return $importableFields;
679 }
680
681 /**
682 * Get the metadata for all potential profile fields.
683 *
684 * @param bool $isIncludeInactive
685 * Should disabled fields be included.
686 *
687 * @return array
688 * Field metadata for all fields that might potentially be in a profile.
689 */
690 protected static function getProfileFieldMetadata($isIncludeInactive) {
691 return self::getImportableFields($isIncludeInactive, NULL, NULL, NULL, TRUE);
692 }
693
694 /**
695 * Get the fields relating to locations.
696 *
697 * @return array
698 */
699 public static function getLocationFields() {
700 static $locationFields = [
701 'street_address',
702 'supplemental_address_1',
703 'supplemental_address_2',
704 'supplemental_address_3',
705 'city',
706 'postal_code',
707 'postal_code_suffix',
708 'geo_code_1',
709 'geo_code_2',
710 'state_province',
711 'country',
712 'county',
713 'phone',
714 'phone_and_ext',
715 'email',
716 'im',
717 'address_name',
718 'phone_ext',
719 ];
720 return $locationFields;
721 }
722
723 /**
724 * @param $ctype
725 *
726 * @return mixed
727 */
728 protected static function getCustomFields($ctype) {
729 $cacheKey = 'uf_group_custom_fields_' . $ctype;
730 if (!Civi::cache('metadata')->has($cacheKey)) {
731 $customFields = CRM_Core_BAO_CustomField::getFieldsForImport($ctype, FALSE, FALSE, FALSE, TRUE, TRUE);
732
733 // hack to add custom data for components
734 $components = ['Contribution', 'Participant', 'Membership', 'Activity', 'Case'];
735 foreach ($components as $value) {
736 $customFields = array_merge($customFields, CRM_Core_BAO_CustomField::getFieldsForImport($value));
737 }
738 $addressCustomFields = CRM_Core_BAO_CustomField::getFieldsForImport('Address');
739 $customFields = array_merge($customFields, $addressCustomFields);
740 Civi::cache('metadata')->set($cacheKey, [$customFields, $addressCustomFields]);
741 }
742 return Civi::cache('metadata')->get($cacheKey);
743 }
744
745 /**
746 * Check the data validity.
747 *
748 * @param int $userID
749 * The user id that we are actually editing.
750 * @param string $name
751 * The machine-name of the group we are interested in.
752 * @param bool $register
753 * @param int $action
754 * The action of the form.
755 *
756 * @pram boolean $register is this the registrtion form
757 * @return bool
758 * true if form is valid
759 */
760 public static function isValid($userID, $name, $register = FALSE, $action = NULL) {
761 if ($register) {
762 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
763 ts('Dynamic Form Creator'),
764 $action
765 );
766 $controller->set('id', $userID);
767 $controller->set('register', 1);
768 $controller->process();
769 return $controller->validate();
770 }
771 else {
772 // make sure we have a valid group
773 $group = new CRM_Core_DAO_UFGroup();
774
775 $group->name = $name;
776
777 if ($group->find(TRUE) && $userID) {
778 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic', ts('Dynamic Form Creator'), $action);
779 $controller->set('gid', $group->id);
780 $controller->set('id', $userID);
781 $controller->set('register', 0);
782 $controller->process();
783 return $controller->validate();
784 }
785 return TRUE;
786 }
787 }
788
789 /**
790 * Get the html for the form that represents this particular group.
791 *
792 * @param int $userID
793 * The user id that we are actually editing.
794 * @param string $title
795 * The title of the group we are interested in.
796 * @param int $action
797 * The action of the form.
798 * @param bool $register
799 * Is this the registration form.
800 * @param bool $reset
801 * Should we reset the form?.
802 * @param int $profileID
803 * Do we have the profile ID?.
804 *
805 * @param bool $doNotProcess
806 * @param null $ctype
807 *
808 * @return string
809 * the html for the form on success, otherwise empty string
810 */
811 public static function getEditHTML(
812 $userID,
813 $title,
814 $action = NULL,
815 $register = FALSE,
816 $reset = FALSE,
817 $profileID = NULL,
818 $doNotProcess = FALSE,
819 $ctype = NULL
820 ) {
821
822 if ($register) {
823 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
824 ts('Dynamic Form Creator'),
825 $action
826 );
827 if ($reset || $doNotProcess) {
828 // hack to make sure we do not process this form
829 $oldQFDefault = CRM_Utils_Array::value('_qf_default',
830 $_POST
831 );
832 unset($_POST['_qf_default']);
833 unset($_REQUEST['_qf_default']);
834 if ($reset) {
835 $controller->reset();
836 }
837 }
838
839 $controller->set('id', $userID);
840 $controller->set('register', 1);
841 $controller->set('skipPermission', 1);
842 $controller->set('ctype', $ctype);
843 $controller->process();
844 if ($doNotProcess || !empty($_POST)) {
845 $controller->validate();
846 }
847 $controller->setEmbedded(TRUE);
848
849 //CRM-5839 - though we want to process form, get the control back.
850 $controller->setSkipRedirection(!$doNotProcess);
851
852 $controller->run();
853
854 // we are done processing so restore the POST/REQUEST vars
855 if (($reset || $doNotProcess) && $oldQFDefault) {
856 $_POST['_qf_default'] = $_REQUEST['_qf_default'] = $oldQFDefault;
857 }
858
859 $template = CRM_Core_Smarty::singleton();
860
861 // Hide CRM error messages if they are displayed using drupal form_set_error.
862 if (!empty($_POST)) {
863 $template->assign('suppressForm', TRUE);
864 }
865
866 return trim($template->fetch('CRM/Profile/Form/Dynamic.tpl'));
867 }
868 else {
869 if (!$profileID) {
870 // make sure we have a valid group
871 $group = new CRM_Core_DAO_UFGroup();
872
873 $group->title = $title;
874
875 if ($group->find(TRUE)) {
876 $profileID = $group->id;
877 }
878 }
879
880 if ($profileID) {
881 // make sure profileID and ctype match if ctype exists
882 if ($ctype) {
883 $profileType = CRM_Core_BAO_UFField::getProfileType($profileID);
884 if (CRM_Contact_BAO_ContactType::isaSubType($profileType)) {
885 $profileType = CRM_Contact_BAO_ContactType::getBasicType($profileType);
886 }
887
888 if (($profileType != 'Contact') && ($profileType != $ctype)) {
889 return NULL;
890 }
891 }
892
893 $controller = new CRM_Core_Controller_Simple('CRM_Profile_Form_Dynamic',
894 ts('Dynamic Form Creator'),
895 $action
896 );
897 if ($reset) {
898 $controller->reset();
899 }
900 $controller->set('gid', $profileID);
901 $controller->set('id', $userID);
902 $controller->set('register', 0);
903 $controller->set('skipPermission', 1);
904 if ($ctype) {
905 $controller->set('ctype', $ctype);
906 }
907 $controller->process();
908 $controller->setEmbedded(TRUE);
909
910 //CRM-5846 - give the control back to drupal.
911 $controller->setSkipRedirection(!$doNotProcess);
912 $controller->run();
913
914 $template = CRM_Core_Smarty::singleton();
915
916 // Hide CRM error messages if they are displayed using drupal form_set_error.
917 if (!empty($_POST) && CRM_Core_Config::singleton()->userFramework == 'Drupal') {
918 if (arg(0) == 'user' || (arg(0) == 'admin' && arg(1) == 'people')) {
919 $template->assign('suppressForm', TRUE);
920 }
921 }
922
923 $templateFile = "CRM/Profile/Form/{$profileID}/Dynamic.tpl";
924 if (!$template->template_exists($templateFile)) {
925 $templateFile = 'CRM/Profile/Form/Dynamic.tpl';
926 }
927 return trim($template->fetch($templateFile));
928 }
929 else {
930 $userEmail = CRM_Contact_BAO_Contact_Location::getEmailDetails($userID);
931
932 // if post not empty then only proceed
933 if (!empty($_POST)) {
934 // get the new email
935 $config = CRM_Core_Config::singleton();
936 $email = $_POST['mail'] ?? NULL;
937
938 if (CRM_Utils_Rule::email($email) && ($email != $userEmail[1])) {
939 CRM_Core_BAO_UFMatch::updateContactEmail($userID, $email);
940 }
941 }
942 }
943 }
944 return '';
945 }
946
947 /**
948 * Given a contact id and a field set, return the values from the db.
949 *
950 * @param int $cid
951 * @param array $fields
952 * The profile fields of interest.
953 * @param array $values
954 * The values for the above fields.
955 * @param bool $searchable
956 * Searchable or not.
957 * @param array $componentWhere
958 * Component condition.
959 * @param bool $absolute
960 * Return urls in absolute form (useful when sending an email).
961 * @param null $additionalWhereClause
962 *
963 * @return null|array
964 */
965 public static function getValues(
966 $cid, &$fields, &$values,
967 $searchable = TRUE, $componentWhere = NULL,
968 $absolute = FALSE, $additionalWhereClause = NULL
969 ) {
970 if (empty($cid) && empty($componentWhere)) {
971 return NULL;
972 }
973
974 // get the contact details (hier)
975 $returnProperties = CRM_Contact_BAO_Contact::makeHierReturnProperties($fields);
976 $params = $cid ? [['contact_id', '=', $cid, 0, 0]] : [];
977
978 // add conditions specified by components. eg partcipant_id etc
979 if (!empty($componentWhere)) {
980 $params = array_merge($params, $componentWhere);
981 }
982
983 $query = new CRM_Contact_BAO_Query($params, $returnProperties, $fields);
984
985 $details = $query->searchQuery(0, 0, NULL, FALSE, FALSE,
986 FALSE, FALSE, FALSE, $additionalWhereClause);
987 while ($details->fetch()) {
988 if (!$details) {
989 return;
990 }
991 }
992 $query->convertToPseudoNames($details);
993
994 $locationTypes = CRM_Core_BAO_Address::buildOptions('location_type_id', 'validate');
995 $imProviders = CRM_Core_DAO_IM::buildOptions('provider_id');
996 $websiteTypes = CRM_Core_DAO_Website::buildOptions('website_type_id');
997
998 $multipleFields = ['url'];
999
1000 //start of code to set the default values
1001 foreach ($fields as $name => $field) {
1002 // fix for CRM-3962
1003 if ($name == 'id') {
1004 $name = 'contact_id';
1005 }
1006
1007 // skip fields that should not be displayed separately
1008 if (!empty($field['skipDisplay'])) {
1009 continue;
1010 }
1011
1012 // Create a unique, non-empty index for each field.
1013 $index = $field['title'];
1014 if ($index === '') {
1015 $index = ' ';
1016 }
1017 while (array_key_exists($index, $values)) {
1018 $index .= ' ';
1019 }
1020
1021 $params[$index] = $values[$index] = '';
1022 $customFieldName = NULL;
1023 // hack for CRM-665
1024 if (isset($details->$name) || $name == 'group' || $name == 'tag') {
1025 // to handle gender / suffix / prefix
1026 if (in_array(substr($name, 0, -3), ['gender', 'prefix', 'suffix'])) {
1027 $params[$index] = $details->$name;
1028 $values[$index] = $details->$name;
1029 }
1030 elseif (in_array($name, CRM_Contact_BAO_Contact::$_greetingTypes)) {
1031 $dname = $name . '_display';
1032 $values[$index] = $details->$dname;
1033 $name = $name . '_id';
1034 $params[$index] = $details->$name;
1035 }
1036 elseif (in_array($name, [
1037 'state_province',
1038 'country',
1039 'county',
1040 ])) {
1041 $values[$index] = $details->$name;
1042 $idx = $name . '_id';
1043 $params[$index] = $details->$idx;
1044 }
1045 elseif ($name === 'preferred_language') {
1046 $params[$index] = $details->$name;
1047 $values[$index] = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', 'preferred_language', $details->$name);
1048 }
1049 elseif ($name == 'group') {
1050 $groups = CRM_Contact_BAO_GroupContact::getContactGroup($cid, 'Added', NULL, FALSE, TRUE);
1051 $title = $ids = [];
1052
1053 foreach ($groups as $g) {
1054 // CRM-8362: User and User Admin visibility groups should be included in display if user has
1055 // VIEW permission on that group
1056 $groupPerm = CRM_Contact_BAO_Group::checkPermission($g['group_id'], TRUE);
1057
1058 if ($g['visibility'] != 'User and User Admin Only' ||
1059 CRM_Utils_Array::key(CRM_Core_Permission::VIEW, $groupPerm)
1060 ) {
1061 $title[] = $g['title'];
1062 if ($g['visibility'] == 'Public Pages') {
1063 $ids[] = $g['group_id'];
1064 }
1065 }
1066 }
1067 $values[$index] = implode(', ', $title);
1068 $params[$index] = implode(',', $ids);
1069 }
1070 elseif ($name == 'tag') {
1071 $entityTags = CRM_Core_BAO_EntityTag::getTag($cid);
1072 $allTags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]);
1073 $title = [];
1074 foreach ($entityTags as $tagId) {
1075 $title[] = $allTags[$tagId];
1076 }
1077 $values[$index] = implode(', ', $title);
1078 $params[$index] = implode(',', $entityTags);
1079 }
1080 elseif ($name == 'activity_status_id') {
1081 $activityStatus = CRM_Core_PseudoConstant::activityStatus();
1082 $values[$index] = $activityStatus[$details->$name];
1083 $params[$index] = $details->$name;
1084 }
1085 elseif ($name == 'activity_date_time') {
1086 $values[$index] = CRM_Utils_Date::customFormat($details->$name);
1087 $params[$index] = $details->$name;
1088 }
1089 elseif ($name == 'contact_sub_type') {
1090 $contactSubTypeNames = explode(CRM_Core_DAO::VALUE_SEPARATOR, $details->$name);
1091 if (!empty($contactSubTypeNames)) {
1092 $contactSubTypeLabels = [];
1093 // get all contact subtypes
1094 $allContactSubTypes = CRM_Contact_BAO_ContactType::subTypeInfo();
1095 // build contact subtype labels array
1096 foreach ($contactSubTypeNames as $cstName) {
1097 if ($cstName) {
1098 $contactSubTypeLabels[] = $allContactSubTypes[$cstName]['label'];
1099 }
1100 }
1101 $values[$index] = implode(',', $contactSubTypeLabels);
1102 }
1103
1104 $params[$index] = $details->$name;
1105 }
1106 else {
1107 if (substr($name, 0, 7) === 'do_not_' || substr($name, 0, 3) === 'is_') {
1108 if ($details->$name) {
1109 $values[$index] = '[ x ]';
1110 }
1111 }
1112 else {
1113 if ($cfID = CRM_Core_BAO_CustomField::getKeyID($name)) {
1114 $htmlType = $field['html_type'];
1115
1116 // field_type is only set when we are retrieving profile values
1117 // when sending email, we call the same function to get custom field
1118 // values etc, i.e. emulating a profile
1119 $fieldType = $field['field_type'] ?? NULL;
1120
1121 if ($htmlType == 'File') {
1122 $entityId = $cid;
1123 if (!$cid &&
1124 $fieldType == 'Activity' && !empty($componentWhere[0][2])
1125 ) {
1126 $entityId = $componentWhere[0][2];
1127 }
1128
1129 $fileURL = CRM_Core_BAO_CustomField::getFileURL($entityId,
1130 $cfID,
1131 NULL,
1132 $absolute,
1133 $additionalWhereClause
1134 );
1135 $params[$index] = $values[$index] = $fileURL['file_url'];
1136 }
1137 else {
1138 $customVal = NULL;
1139 if (isset($dao) && property_exists($dao, 'data_type') &&
1140 ($dao->data_type == 'Int' ||
1141 $dao->data_type == 'Boolean'
1142 )
1143 ) {
1144 $customVal = (int ) ($details->{$name});
1145 }
1146 elseif (isset($dao) && property_exists($dao, 'data_type')
1147 && $dao->data_type == 'Float'
1148 ) {
1149 $customVal = (float ) ($details->{$name});
1150 }
1151 elseif (!CRM_Utils_System::isNull(explode(CRM_Core_DAO::VALUE_SEPARATOR,
1152 $details->{$name}
1153 ))
1154 ) {
1155 $customVal = $details->{$name};
1156 }
1157
1158 //CRM-4582
1159 if (CRM_Utils_System::isNull($customVal)) {
1160 continue;
1161 }
1162
1163 $params[$index] = $customVal;
1164 $values[$index] = CRM_Core_BAO_CustomField::displayValue($customVal, $cfID);
1165 if ($field['data_type'] == 'ContactReference') {
1166 $params[$index] = $values[$index];
1167 }
1168 if (CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField',
1169 $cfID, 'is_search_range'
1170 )
1171 ) {
1172 $customFieldName = "{$name}_from";
1173 }
1174 }
1175 }
1176 elseif ($name == 'image_URL') {
1177 list($width, $height) = getimagesize(CRM_Utils_String::unstupifyUrl($details->$name));
1178 list($thumbWidth, $thumbHeight) = CRM_Contact_BAO_Contact::getThumbSize($width, $height);
1179
1180 $image_URL = '<img src="' . $details->$name . '" height= ' . $thumbHeight . ' width= ' . $thumbWidth . ' />';
1181 $values[$index] = "<a href='#' onclick='contactImagePopUp(\"{$details->$name}\", {$width}, {$height});'>{$image_URL}</a>";
1182 }
1183 elseif (in_array($name, [
1184 'birth_date',
1185 'deceased_date',
1186 ])) {
1187 // @todo this set should be determined from metadata, not hard-coded.
1188 $values[$index] = CRM_Utils_Date::customFormat($details->$name);
1189 $params[$index] = CRM_Utils_Date::isoToMysql($details->$name);
1190 }
1191 else {
1192 $dao = '';
1193 if ($index == 'Campaign') {
1194 $dao = 'CRM_Campaign_DAO_Campaign';
1195 }
1196 elseif ($index == 'Contribution Page') {
1197 $dao = 'CRM_Contribute_DAO_ContributionPage';
1198 }
1199 if ($dao) {
1200 $value = CRM_Core_DAO::getFieldValue($dao, $details->$name, 'title');
1201 }
1202 else {
1203 $value = $details->$name;
1204 }
1205 $values[$index] = $value;
1206 }
1207 }
1208 }
1209 }
1210 elseif (strpos($name, '-') !== FALSE) {
1211 list($fieldName, $id, $type) = CRM_Utils_System::explode('-', $name, 3);
1212
1213 if (!in_array($fieldName, $multipleFields)) {
1214 if ($id == 'Primary') {
1215 // fix for CRM-1543
1216 // not sure why we'd every use Primary location type id
1217 // we need to fix the source if we are using it
1218 // $locationTypeName = CRM_Contact_BAO_Contact::getPrimaryLocationType( $cid );
1219 $locationTypeName = 1;
1220 }
1221 else {
1222 $locationTypeName = $locationTypes[$id] ?? NULL;
1223 }
1224
1225 if (!$locationTypeName) {
1226 continue;
1227 }
1228
1229 $detailName = "{$locationTypeName}-{$fieldName}";
1230 $detailName = str_replace(' ', '_', $detailName);
1231
1232 if (in_array($fieldName, [
1233 'phone',
1234 'im',
1235 'email',
1236 'openid',
1237 ])) {
1238 if ($type) {
1239 $detailName .= "-{$type}";
1240 }
1241 }
1242
1243 if (in_array($fieldName, [
1244 'state_province',
1245 'country',
1246 'county',
1247 ])) {
1248 $values[$index] = $details->$detailName;
1249 $idx = $detailName . '_id';
1250 $params[$index] = $details->$idx;
1251 }
1252 elseif ($fieldName == 'im') {
1253 $providerId = $detailName . '-provider_id';
1254 if (isset($imProviders[$details->$providerId])) {
1255 $values[$index] = $details->$detailName . " (" . $imProviders[$details->$providerId] . ")";
1256 }
1257 else {
1258 $values[$index] = $details->$detailName;
1259 }
1260 $params[$index] = $details->$detailName;
1261 }
1262 elseif ($fieldName == 'phone') {
1263 $phoneExtField = str_replace('phone', 'phone_ext', $detailName);
1264 if (isset($details->$phoneExtField)) {
1265 $values[$index] = $details->$detailName . " (" . $details->$phoneExtField . ")";
1266 }
1267 else {
1268 $values[$index] = $details->$detailName;
1269 }
1270 $params[$index] = $details->$detailName;
1271 }
1272 else {
1273 $values[$index] = $params[$index] = $details->$detailName;
1274 }
1275 }
1276 else {
1277 $detailName = "website-{$id}-{$fieldName}";
1278 $url = CRM_Utils_System::fixURL($details->$detailName);
1279 if ($details->$detailName) {
1280 $websiteTypeId = "website-{$id}-website_type_id";
1281 $websiteType = $websiteTypes[$details->$websiteTypeId];
1282 $values[$index] = "<a href=\"$url\">{$details->$detailName} ( {$websiteType} )</a>";
1283 }
1284 else {
1285 $values[$index] = '';
1286 }
1287 }
1288 }
1289
1290 if ((CRM_Utils_Array::value('visibility', $field) == 'Public Pages and Listings') &&
1291 CRM_Core_Permission::check('profile listings and forms')
1292 ) {
1293
1294 if (CRM_Utils_System::isNull($params[$index])) {
1295 $params[$index] = $values[$index];
1296 }
1297 if (!isset($params[$index])) {
1298 continue;
1299 }
1300 if (!$customFieldName) {
1301 $fieldName = $field['name'];
1302 }
1303 else {
1304 $fieldName = $customFieldName;
1305 }
1306
1307 $url = NULL;
1308 if (CRM_Core_BAO_CustomField::getKeyID($field['name'])) {
1309 $htmlType = $field['html_type'];
1310 if ($htmlType == 'Link') {
1311 $url = $params[$index];
1312 }
1313 elseif (!empty($field['serialize'])) {
1314 $valSeparator = CRM_Core_DAO::VALUE_SEPARATOR;
1315 $selectedOptions = explode($valSeparator, $params[$index]);
1316
1317 foreach ($selectedOptions as $key => $multiOption) {
1318 if ($multiOption) {
1319 $url[] = CRM_Utils_System::url('civicrm/profile',
1320 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1321 urlencode($fieldName) .
1322 '=' .
1323 urlencode($multiOption)
1324 );
1325 }
1326 }
1327 }
1328 else {
1329 $url = CRM_Utils_System::url('civicrm/profile',
1330 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1331 urlencode($fieldName) .
1332 '=' .
1333 urlencode($params[$index])
1334 );
1335 }
1336 }
1337 else {
1338 $url = CRM_Utils_System::url('civicrm/profile',
1339 'reset=1&force=1&gid=' . $field['group_id'] . '&' .
1340 urlencode($fieldName) .
1341 '=' .
1342 urlencode($params[$index])
1343 );
1344 }
1345
1346 if ($url &&
1347 !empty($values[$index]) &&
1348 $searchable
1349 ) {
1350
1351 if (is_array($url) && !empty($url)) {
1352 $links = [];
1353 $eachMultiValue = explode(', ', $values[$index]);
1354 foreach ($eachMultiValue as $key => $valueLabel) {
1355 $links[] = '<a href="' . $url[$key] . '">' . $valueLabel . '</a>';
1356 }
1357 $values[$index] = implode(', ', $links);
1358 }
1359 else {
1360 $values[$index] = '<a href="' . $url . '">' . $values[$index] . '</a>';
1361 }
1362 }
1363 }
1364 }
1365 }
1366
1367 /**
1368 * Check if profile Group used by any module.
1369 *
1370 * @param int $id
1371 * Profile Id.
1372 *
1373 * @return bool
1374 *
1375 */
1376 public static function usedByModule($id) {
1377 //check whether this group is used by any module(check uf join records)
1378 $sql = "SELECT id
1379 FROM civicrm_uf_join
1380 WHERE civicrm_uf_join.uf_group_id=$id";
1381
1382 $dao = new CRM_Core_DAO();
1383 $dao->query($sql);
1384 if ($dao->fetch()) {
1385 return TRUE;
1386 }
1387 else {
1388 return FALSE;
1389 }
1390 }
1391
1392 /**
1393 * Delete the profile Group.
1394 *
1395 * @param int $id
1396 * Profile Id.
1397 *
1398 * @return bool
1399 *
1400 */
1401 public static function del($id) {
1402 CRM_Utils_Hook::pre('delete', 'UFGroup', $id);
1403
1404 //check whether this group contains any profile fields
1405 $profileField = new CRM_Core_DAO_UFField();
1406 $profileField->uf_group_id = $id;
1407 $profileField->find();
1408 while ($profileField->fetch()) {
1409 CRM_Core_BAO_UFField::del($profileField->id);
1410 }
1411
1412 //delete records from uf join table
1413 $ufJoin = new CRM_Core_DAO_UFJoin();
1414 $ufJoin->uf_group_id = $id;
1415 $ufJoin->delete();
1416
1417 //delete profile group
1418 $group = new CRM_Core_DAO_UFGroup();
1419 $group->id = $id;
1420 $group->delete();
1421
1422 CRM_Utils_Hook::post('delete', 'UFGroup', $id, $group);
1423 return 1;
1424 }
1425
1426 /**
1427 * Add the UF Group.
1428 *
1429 * @param array $params
1430 * Reference array contains the values submitted by the form.
1431 * @param array $ids
1432 * Deprecated array.
1433 *
1434 *
1435 * @return object
1436 */
1437 public static function add(&$params, $ids = []) {
1438 if (empty($params['id']) && !empty($ids['ufgroup'])) {
1439 $params['id'] = $ids['ufgroup'];
1440 Civi::log()->warning('ids parameter is deprecated', ['civi.tag' => 'deprecated']);
1441 }
1442 $fields = [
1443 'is_active',
1444 'add_captcha',
1445 'is_map',
1446 'is_update_dupe',
1447 'is_edit_link',
1448 'is_uf_link',
1449 'is_cms_user',
1450 ];
1451 foreach ($fields as $field) {
1452 $params[$field] = CRM_Utils_Array::value($field, $params, FALSE);
1453 }
1454
1455 $params['limit_listings_group_id'] = $params['group'] ?? NULL;
1456 $params['add_to_group_id'] = $params['add_contact_to_group'] ?? NULL;
1457
1458 //CRM-15427
1459 if (!empty($params['group_type']) && is_array($params['group_type'])) {
1460 $params['group_type'] = implode(',', $params['group_type']);
1461 }
1462 $ufGroup = new CRM_Core_DAO_UFGroup();
1463 $ufGroup->copyValues($params);
1464
1465 $ufGroupID = CRM_Utils_Array::value('ufgroup', $ids, CRM_Utils_Array::value('id', $params));
1466 if (!$ufGroupID && empty($params['name'])) {
1467 $ufGroup->name = CRM_Utils_String::munge($ufGroup->title, '_', 56);
1468 }
1469 $ufGroup->id = $ufGroupID;
1470
1471 $ufGroup->save();
1472
1473 if (!$ufGroupID && empty($params['name'])) {
1474 $ufGroup->name = $ufGroup->name . "_{$ufGroup->id}";
1475 $ufGroup->save();
1476 }
1477
1478 return $ufGroup;
1479 }
1480
1481 /**
1482 * Make uf join entries for an uf group.
1483 *
1484 * @param int $weight
1485 * @param array $groupTypes
1486 * An assoc array of name/value pairs.
1487 * @param int $ufGroupId
1488 * Ufgroup id.
1489 */
1490 public static function createUFJoin($weight, $groupTypes, $ufGroupId) {
1491
1492 // get ufjoin records for uf group
1493 $ufGroupRecord = CRM_Core_BAO_UFGroup::getUFJoinRecord($ufGroupId);
1494
1495 // get the list of all ufgroup types
1496 $allUFGroupType = CRM_Core_SelectValues::ufGroupTypes();
1497
1498 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
1499 if (!is_array($groupTypes)) {
1500 $groupTypes = [];
1501 }
1502
1503 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
1504 if (!is_array($ufGroupRecord)) {
1505 $ufGroupRecord = [];
1506 }
1507
1508 // check which values has to be inserted/deleted for contact
1509 $menuRebuild = FALSE;
1510 foreach ($allUFGroupType as $key => $value) {
1511 $joinParams = [];
1512 $joinParams['uf_group_id'] = $ufGroupId;
1513 $joinParams['module'] = $key;
1514 if ($key === 'User Account') {
1515 $menuRebuild = TRUE;
1516 }
1517 if (array_key_exists($key, $groupTypes) && !in_array($key, $ufGroupRecord)) {
1518 // insert a new record
1519 CRM_Core_BAO_UFGroup::addUFJoin($joinParams);
1520 }
1521 elseif (!array_key_exists($key, $groupTypes) && in_array($key, $ufGroupRecord)) {
1522 // delete a record for existing ufgroup
1523 CRM_Core_BAO_UFGroup::delUFJoin($joinParams);
1524 }
1525 }
1526
1527 //update the weight
1528 $query = '
1529 UPDATE civicrm_uf_join
1530 SET weight = %1
1531 WHERE uf_group_id = %2
1532 AND ( entity_id IS NULL OR entity_id <= 0 )
1533 ';
1534 $p = [
1535 1 => [$weight, 'Integer'],
1536 2 => [$ufGroupId, 'Integer'],
1537 ];
1538 CRM_Core_DAO::executeQuery($query, $p);
1539
1540 // Do a menu rebuild, so it gets all the new menu entries for user account
1541 if ($menuRebuild) {
1542 $config = CRM_Core_Config::singleton();
1543 $config->userSystem->updateCategories();
1544 }
1545 }
1546
1547 /**
1548 * Get the UF Join records for an ufgroup id.
1549 *
1550 * @param int $ufGroupId
1551 * Uf group id.
1552 * @param int $displayName
1553 * If set return display name in array.
1554 * @param int $status
1555 * If set return module other than default modules (User Account/User registration/Profile).
1556 *
1557 * @return array
1558 *
1559 */
1560 public static function getUFJoinRecord($ufGroupId = NULL, $displayName = NULL, $status = NULL) {
1561 if ($displayName) {
1562 $UFGroupType = [];
1563 $UFGroupType = CRM_Core_SelectValues::ufGroupTypes();
1564 }
1565
1566 $ufJoin = [];
1567 $dao = new CRM_Core_DAO_UFJoin();
1568
1569 if ($ufGroupId) {
1570 $dao->uf_group_id = $ufGroupId;
1571 }
1572
1573 $dao->find();
1574 $ufJoin = [];
1575
1576 while ($dao->fetch()) {
1577 if (!$displayName) {
1578 $ufJoin[$dao->id] = $dao->module;
1579 }
1580 else {
1581 if (isset($UFGroupType[$dao->module])) {
1582 // skip the default modules
1583 if (!$status) {
1584 $ufJoin[$dao->id] = $UFGroupType[$dao->module];
1585 }
1586 // added for CRM-1475
1587 }
1588 elseif (!CRM_Utils_Array::key($dao->module, $ufJoin)) {
1589 $ufJoin[$dao->id] = $dao->module;
1590 }
1591 }
1592 }
1593 return $ufJoin;
1594 }
1595
1596 /**
1597 * Function takes an associative array and creates a ufjoin record for ufgroup.
1598 *
1599 * @param array $params
1600 * (reference) an assoc array of name/value pairs.
1601 *
1602 * @return CRM_Core_BAO_UFJoin
1603 */
1604 public static function addUFJoin(&$params) {
1605 $ufJoin = new CRM_Core_DAO_UFJoin();
1606 $ufJoin->copyValues($params);
1607 $ufJoin->save();
1608 return $ufJoin;
1609 }
1610
1611 /**
1612 * Delete the uf join record for an uf group.
1613 *
1614 * @param array $params
1615 * (reference) an assoc array of name/value pairs.
1616 */
1617 public static function delUFJoin(&$params) {
1618 $ufJoin = new CRM_Core_DAO_UFJoin();
1619 $ufJoin->copyValues($params);
1620 $ufJoin->delete();
1621 }
1622
1623 /**
1624 * Get the weight for ufjoin record.
1625 *
1626 * @param int $ufGroupId
1627 * If $ufGroupId get update weight or add weight.
1628 *
1629 * @return int
1630 * weight of the UFGroup
1631 */
1632 public static function getWeight($ufGroupId = NULL) {
1633 //calculate the weight
1634 $p = [];
1635 if (!$ufGroupId) {
1636 $queryString = "SELECT ( MAX(civicrm_uf_join.weight)+1) as new_weight
1637 FROM civicrm_uf_join
1638 WHERE module = 'User Registration' OR module = 'User Account' OR module = 'Profile'";
1639 }
1640 else {
1641 $queryString = "SELECT MAX(civicrm_uf_join.weight) as new_weight
1642 FROM civicrm_uf_join
1643 WHERE civicrm_uf_join.uf_group_id = %1
1644 AND ( entity_id IS NULL OR entity_id <= 0 )";
1645 $p[1] = [$ufGroupId, 'Integer'];
1646 }
1647
1648 $dao = CRM_Core_DAO::executeQuery($queryString, $p);
1649 $dao->fetch();
1650 return ($dao->new_weight) ? $dao->new_weight : 1;
1651 }
1652
1653 /**
1654 * Get the uf group for a module.
1655 *
1656 * @param string $moduleName
1657 * Module name.
1658 * @param int $count
1659 * No to increment the weight.
1660 * @param bool $skipPermission
1661 * @param int $op
1662 * Which operation (view, edit, create, etc) to check permission for.
1663 * @param array|NULL $returnFields list of UFGroup fields to return; NULL for default
1664 *
1665 * @return array
1666 * array of ufgroups for a module
1667 */
1668 public static function getModuleUFGroup($moduleName = NULL, $count = 0, $skipPermission = TRUE, $op = CRM_Core_Permission::VIEW, $returnFields = NULL) {
1669 $selectFields = ['id', 'title', 'created_id', 'is_active', 'is_reserved', 'group_type', 'description'];
1670
1671 if (CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_uf_group', 'frontend_title')) {
1672 $selectFields[] = 'frontend_title';
1673 }
1674
1675 if (!empty($returnFields)) {
1676 $selectFields = array_merge($returnFields, array_diff($selectFields, $returnFields));
1677 }
1678
1679 $queryString = 'SELECT civicrm_uf_group.' . implode(', civicrm_uf_group.', $selectFields) . '
1680 FROM civicrm_uf_group
1681 LEFT JOIN civicrm_uf_join ON (civicrm_uf_group.id = uf_group_id)';
1682 $p = [];
1683 if ($moduleName) {
1684 $queryString .= ' AND civicrm_uf_group.is_active = 1
1685 WHERE civicrm_uf_join.module = %2';
1686 $p[2] = [$moduleName, 'String'];
1687 }
1688
1689 // add permissioning for profiles only if not registration
1690 if (!$skipPermission) {
1691 $permissionClause = CRM_Core_Permission::ufGroupClause($op, 'civicrm_uf_group.');
1692 if (strpos($queryString, 'WHERE') !== FALSE) {
1693 $queryString .= " AND $permissionClause ";
1694 }
1695 else {
1696 $queryString .= " $permissionClause ";
1697 }
1698 }
1699
1700 $queryString .= ' ORDER BY civicrm_uf_join.weight, civicrm_uf_group.title';
1701 $dao = CRM_Core_DAO::executeQuery($queryString, $p);
1702
1703 $ufGroups = [];
1704 while ($dao->fetch()) {
1705 //skip mix profiles in user Registration / User Account
1706 if (($moduleName === 'User Registration' || $moduleName === 'User Account') &&
1707 CRM_Core_BAO_UFField::checkProfileType($dao->id)
1708 ) {
1709 continue;
1710 }
1711 foreach ($selectFields as $key => $field) {
1712 if ($field === 'id') {
1713 continue;
1714 }
1715 $ufGroups[$dao->id][$field] = $dao->$field;
1716 }
1717 }
1718
1719 // Allow other modules to alter/override the UFGroups.
1720 CRM_Utils_Hook::buildUFGroupsForModule($moduleName, $ufGroups);
1721
1722 return $ufGroups;
1723 }
1724
1725 /**
1726 * Filter ufgroups based on logged in user contact type.
1727 *
1728 * @param int $ufGroupId
1729 * Uf group id (profile id).
1730 * @param int $contactID
1731 *
1732 * @return bool
1733 * true or false
1734 */
1735 public static function filterUFGroups($ufGroupId, $contactID = NULL) {
1736 if (!$contactID) {
1737 $session = CRM_Core_Session::singleton();
1738 $contactID = $session->get('userID');
1739 }
1740
1741 if ($contactID) {
1742 //get the contact type
1743 $contactType = CRM_Contact_BAO_Contact::getContactType($contactID);
1744
1745 //match if exixting contact type is same as profile contact type
1746 $profileType = CRM_Core_BAO_UFField::getProfileType($ufGroupId);
1747
1748 if (CRM_Contact_BAO_ContactType::isaSubType($profileType)) {
1749 $profileType = CRM_Contact_BAO_ContactType::getBasicType($profileType);
1750
1751 //in some cases getBasicType() returns a cached array instead of string. Example: array ('sponsor' => 'organization')
1752 if (is_array($profileType)) {
1753 $profileType = array_shift($profileType);
1754 }
1755 }
1756
1757 //allow special mix profiles for Contribution and Participant
1758 $specialProfiles = ['Contribution', 'Participant', 'Membership'];
1759
1760 if (in_array($profileType, $specialProfiles)) {
1761 return TRUE;
1762 }
1763
1764 if (($contactType == $profileType) || $profileType == 'Contact') {
1765 return TRUE;
1766 }
1767 }
1768
1769 return FALSE;
1770 }
1771
1772 /**
1773 * Add profile field to a form.
1774 *
1775 * @param CRM_Core_Form $form
1776 * @param array $field
1777 * Properties.
1778 * @param int $mode
1779 * Profile mode.
1780 * @param int $contactId
1781 * @param bool $online
1782 * @param string $usedFor
1783 * For building up prefixed fieldname for special cases (e.g. onBehalf, Honor).
1784 * @param int $rowNumber
1785 * @param string $prefix
1786 *
1787 * @return null
1788 */
1789 public static function buildProfile(
1790 &$form,
1791 &$field,
1792 $mode,
1793 $contactId = NULL,
1794 $online = FALSE,
1795 $usedFor = NULL,
1796 $rowNumber = NULL,
1797 $prefix = ''
1798 ) {
1799 $defaultValues = [];
1800 $fieldName = $field['name'];
1801 $title = $field['title'];
1802 $attributes = $field['attributes'];
1803 $rule = $field['rule'];
1804 $view = $field['is_view'];
1805 $required = ($mode == CRM_Profile_Form::MODE_SEARCH) ? FALSE : $field['is_required'];
1806 $search = $mode == CRM_Profile_Form::MODE_SEARCH;
1807 $isShared = CRM_Utils_Array::value('is_shared', $field, 0);
1808
1809 // do not display view fields in drupal registration form
1810 // CRM-4632
1811 if ($view && $mode == CRM_Profile_Form::MODE_REGISTER) {
1812 return NULL;
1813 }
1814
1815 if ($usedFor == 'onbehalf') {
1816 $name = "onbehalf[$fieldName]";
1817 }
1818 elseif ($usedFor == 'honor') {
1819 $name = "honor[$fieldName]";
1820 }
1821 elseif ($contactId && !$online) {
1822 $name = "field[$contactId][$fieldName]";
1823 }
1824 elseif ($rowNumber) {
1825 $name = "field[$rowNumber][$fieldName]";
1826 }
1827 elseif (!empty($prefix)) {
1828 $name = $prefix . "[$fieldName]";
1829 }
1830 else {
1831 $name = $fieldName;
1832 }
1833
1834 $selectAttributes = ['class' => 'crm-select2', 'placeholder' => TRUE];
1835
1836 if ($fieldName == 'image_URL' && $mode == CRM_Profile_Form::MODE_EDIT) {
1837 $deleteExtra = json_encode(ts('Are you sure you want to delete contact image.'));
1838 $deleteURL = [
1839 CRM_Core_Action::DELETE => [
1840 'name' => ts('Delete Contact Image'),
1841 'url' => 'civicrm/contact/image',
1842 'qs' => 'reset=1&id=%%id%%&gid=%%gid%%&action=delete',
1843 'extra' => 'onclick = "' . htmlspecialchars("if (confirm($deleteExtra)) this.href+='&confirmed=1'; else return false;") . '"',
1844 ],
1845 ];
1846 $deleteURL = CRM_Core_Action::formLink($deleteURL,
1847 CRM_Core_Action::DELETE,
1848 [
1849 'id' => $form->get('id'),
1850 'gid' => $form->get('gid'),
1851 ],
1852 ts('more'),
1853 FALSE,
1854 'contact.profileimage.delete',
1855 'Contact',
1856 $form->get('id')
1857 );
1858 $form->assign('deleteURL', $deleteURL);
1859 }
1860 $addressOptions = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
1861 'address_options', TRUE, NULL, TRUE
1862 );
1863
1864 if (substr($fieldName, 0, 14) === 'state_province') {
1865 $form->addChainSelect($name, ['label' => $title, 'required' => $required]);
1866 $config = CRM_Core_Config::singleton();
1867 if (!in_array($mode, [CRM_Profile_Form::MODE_EDIT, CRM_Profile_Form::MODE_SEARCH]) &&
1868 $config->defaultContactStateProvince
1869 ) {
1870 $defaultValues[$name] = $config->defaultContactStateProvince;
1871 $form->setDefaults($defaultValues);
1872 }
1873 }
1874 elseif (substr($fieldName, 0, 7) === 'country') {
1875 $form->add('select', $name, $title, CRM_Core_PseudoConstant::country(), $required, $selectAttributes);
1876 $config = CRM_Core_Config::singleton();
1877 if (!in_array($mode, [CRM_Profile_Form::MODE_EDIT, CRM_Profile_Form::MODE_SEARCH]) &&
1878 $config->defaultContactCountry
1879 ) {
1880 $defaultValues[$name] = $config->defaultContactCountry;
1881 $form->setDefaults($defaultValues);
1882 }
1883 }
1884 elseif (substr($fieldName, 0, 6) === 'county') {
1885 if ($addressOptions['county']) {
1886 $form->addChainSelect($name, ['label' => $title, 'required' => $required]);
1887 }
1888 }
1889 elseif (substr($fieldName, 0, 9) === 'image_URL') {
1890 $form->add('file', $name, $title, $attributes, $required);
1891 $form->addUploadElement($name);
1892 }
1893 elseif (substr($fieldName, 0, 2) === 'im') {
1894 $form->add('text', $name, $title, $attributes, $required);
1895 if (!$contactId) {
1896 if ($usedFor) {
1897 if (substr($name, -1) === ']') {
1898 $providerName = substr($name, 0, -1) . '-provider_id]';
1899 }
1900 $form->add('select', $providerName, NULL,
1901 CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'), $required
1902 );
1903 }
1904 else {
1905 $form->add('select', $name . '-provider_id', $title,
1906 CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'), $required
1907 );
1908 }
1909
1910 if ($view && $mode != CRM_Profile_Form::MODE_SEARCH) {
1911 $form->freeze($name . '-provider_id');
1912 }
1913 }
1914 }
1915 elseif (CRM_Utils_Array::value('name', $field) == 'membership_type') {
1916 list($orgInfo, $types) = CRM_Member_BAO_MembershipType::getMembershipTypeInfo();
1917 $sel = &$form->addElement('hierselect', $name, $title);
1918 $select = ['' => ts('- select membership type -')];
1919 if (count($orgInfo) == 1 && $field['is_required']) {
1920 // we only have one org - so we should default to it. Not sure about defaulting to first type
1921 // as it could be missed - so adding a select
1922 // however, possibly that is more similar to the membership form
1923 if (count($types[1]) > 1) {
1924 $types[1] = $select + $types[1];
1925 }
1926 }
1927 else {
1928 $orgInfo = $select + $orgInfo;
1929 }
1930 $sel->setOptions([$orgInfo, $types]);
1931 }
1932 elseif (CRM_Utils_Array::value('name', $field) == 'membership_status') {
1933 $form->add('select', $name, $title,
1934 CRM_Member_PseudoConstant::membershipStatus(NULL, NULL, 'label'), $required
1935 );
1936 }
1937 elseif (in_array($fieldName, ['gender_id', 'communication_style_id'])) {
1938 $options = [];
1939 $pseudoValues = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', $fieldName);
1940 $form->addRadio($name, ts('%1', [1 => $title]), $pseudoValues, ['allowClear' => !$required], NULL, $required);
1941 }
1942 elseif ($fieldName === 'prefix_id' || $fieldName === 'suffix_id') {
1943 $form->addSelect($name, [
1944 'label' => $title,
1945 'entity' => 'contact',
1946 'field' => $fieldName,
1947 'class' => 'six',
1948 'placeholder' => '',
1949 ], $required);
1950 }
1951 elseif ($fieldName === 'contact_sub_type') {
1952 $gId = $form->get('gid') ? $form->get('gid') : CRM_Utils_Array::value('group_id', $field);
1953 if ($usedFor == 'onbehalf') {
1954 $profileType = 'Organization';
1955 }
1956 elseif ($usedFor == 'honor') {
1957 $profileType = CRM_Core_BAO_UFField::getProfileType($form->_params['honoree_profile_id']);
1958 }
1959 else {
1960 $profileType = $gId ? CRM_Core_BAO_UFField::getProfileType($gId) : NULL;
1961 if ($profileType == 'Contact') {
1962 $profileType = 'Individual';
1963 }
1964 }
1965
1966 $setSubtype = FALSE;
1967 if (CRM_Contact_BAO_ContactType::isaSubType($profileType)) {
1968 $setSubtype = $profileType;
1969 $profileType = CRM_Contact_BAO_ContactType::getBasicType($profileType);
1970 }
1971
1972 $subtypes = $profileType ? CRM_Contact_BAO_ContactType::subTypePairs($profileType) : [];
1973
1974 if ($setSubtype) {
1975 $subtypeList = [];
1976 $subtypeList[$setSubtype] = $subtypes[$setSubtype];
1977 }
1978 else {
1979 $subtypeList = $subtypes;
1980 }
1981
1982 $form->add('select', $name, $title, $subtypeList, $required, ['class' => 'crm-select2', 'multiple' => TRUE]);
1983 }
1984 elseif (in_array($fieldName, CRM_Contact_BAO_Contact::$_greetingTypes)) {
1985 // Get contact type for greeting selector
1986 $gId = $form->get('gid') ?: CRM_Utils_Array::value('group_id', $field);
1987 $profileType = CRM_Core_BAO_UFField::getProfileType($gId, TRUE, FALSE, TRUE);
1988
1989 if (!$profileType || in_array($profileType, ['Contact', 'Contribution', 'Participant', 'Membership'])) {
1990 $profileType = ($profileType == 'Contact' && $form->get('id')) ? CRM_Contact_BAO_Contact::getContactType($form->get('id')) : 'Individual';
1991 }
1992 if (CRM_Contact_BAO_ContactType::isaSubType($profileType)) {
1993 $profileType = CRM_Contact_BAO_ContactType::getBasicType($profileType);
1994 }
1995 $greeting = [
1996 'contact_type' => $profileType,
1997 'greeting_type' => $fieldName,
1998 ];
1999 $form->add('select', $name, $title, CRM_Core_PseudoConstant::greeting($greeting), $required, ['placeholder' => TRUE]);
2000 // add custom greeting element
2001 $form->add('text', $fieldName . '_custom', ts('Custom %1', [1 => ucwords(str_replace('_', ' ', $fieldName))]),
2002 NULL, FALSE
2003 );
2004 }
2005 elseif ($fieldName === 'preferred_communication_method') {
2006 $communicationFields = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method');
2007 foreach ($communicationFields as $key => $var) {
2008 if ($key == '') {
2009 continue;
2010 }
2011 $communicationOptions[] = $form->createElement('checkbox', $key, NULL, $var);
2012 }
2013 $form->addGroup($communicationOptions, $name, $title, '<br/>');
2014 }
2015 elseif ($fieldName === 'preferred_mail_format') {
2016 $form->add('select', $name, $title, CRM_Core_SelectValues::pmf());
2017 }
2018 elseif ($fieldName === 'preferred_language') {
2019 $form->add('select', $name, $title, CRM_Contact_BAO_Contact::buildOptions('preferred_language'), $required, ['placeholder' => TRUE]);
2020 }
2021 elseif ($fieldName == 'external_identifier') {
2022 $form->add('text', $name, $title, $attributes, $required);
2023 $contID = $contactId;
2024 if (!$contID) {
2025 $contID = $form->get('id');
2026 }
2027 $form->addRule($name,
2028 ts('External ID already exists in Database.'),
2029 'objectExists',
2030 ['CRM_Contact_DAO_Contact', $contID, 'external_identifier']
2031 );
2032 }
2033 elseif ($fieldName === 'group') {
2034 CRM_Contact_Form_Edit_TagsAndGroups::buildQuickForm($form, $contactId,
2035 CRM_Contact_Form_Edit_TagsAndGroups::GROUP,
2036 TRUE, $required,
2037 $title, NULL, $name, 'checkbox', TRUE
2038 );
2039 }
2040 elseif ($fieldName === 'tag') {
2041 CRM_Contact_Form_Edit_TagsAndGroups::buildQuickForm($form, $contactId,
2042 CRM_Contact_Form_Edit_TagsAndGroups::TAG,
2043 FALSE, $required,
2044 NULL, $title, $name
2045 );
2046 }
2047 elseif (substr($fieldName, 0, 4) === 'url-') {
2048 $form->add('text', $name, $title, CRM_Core_DAO::getAttribute('CRM_Core_DAO_Website', 'url'), $required);
2049 $form->addRule($name, ts('Enter a valid web address beginning with \'http://\' or \'https://\'.'), 'url');
2050 }
2051 // Note should be rendered as textarea
2052 elseif (substr($fieldName, -4) == 'note') {
2053 $form->add('textarea', $name, $title, $attributes, $required);
2054 }
2055 elseif (substr($fieldName, 0, 6) === 'custom') {
2056 $customFieldID = CRM_Core_BAO_CustomField::getKeyID($fieldName);
2057 if ($customFieldID) {
2058 CRM_Core_BAO_CustomField::addQuickFormElement($form, $name, $customFieldID, $required, $search, $title);
2059 }
2060 }
2061 elseif (substr($fieldName, 0, 14) === 'address_custom') {
2062 list($fName, $locTypeId) = CRM_Utils_System::explode('-', $fieldName, 2);
2063 $customFieldID = CRM_Core_BAO_CustomField::getKeyID(substr($fName, 8));
2064 if ($customFieldID) {
2065 CRM_Core_BAO_CustomField::addQuickFormElement($form, $name, $customFieldID, $required, $search, $title);
2066 }
2067 }
2068 elseif ($fieldName == 'send_receipt') {
2069 $form->addElement('checkbox', $name, $title);
2070 }
2071 elseif ($fieldName == 'soft_credit') {
2072 $form->addEntityRef("soft_credit_contact_id[$rowNumber]", ts('Soft Credit To'), ['create' => TRUE]);
2073 $form->addMoney("soft_credit_amount[{$rowNumber}]", ts('Amount'), FALSE, NULL, FALSE);
2074 }
2075 elseif ($fieldName === 'product_name') {
2076 list($products, $options) = CRM_Contribute_BAO_Premium::getPremiumProductInfo();
2077 $sel = &$form->addElement('hierselect', $name, $title);
2078 $products = ['0' => ts('- select %1 -', [1 => $title])] + $products;
2079 $sel->setOptions([$products, $options]);
2080 }
2081 elseif ($fieldName === 'payment_instrument') {
2082 $form->add('select', $name, $title,
2083 CRM_Contribute_PseudoConstant::paymentInstrument(), $required, ['placeholder' => TRUE]);
2084 }
2085 elseif ($fieldName === 'financial_type') {
2086 $form->add('select', $name, $title,
2087 CRM_Contribute_PseudoConstant::financialType(), $required, ['placeholder' => TRUE]
2088 );
2089 }
2090 elseif ($fieldName === 'contribution_status_id') {
2091 $contributionStatuses = CRM_Contribute_BAO_Contribution_Utils::getContributionStatuses();
2092
2093 $form->add('select', $name, $title,
2094 $contributionStatuses, $required, ['placeholder' => TRUE]
2095 );
2096 }
2097 elseif ($fieldName === 'soft_credit_type') {
2098 $name = "soft_credit_type[$rowNumber]";
2099 $form->add('select', $name, $title,
2100 CRM_Core_OptionGroup::values("soft_credit_type"), ['placeholder' => TRUE]
2101 );
2102 //CRM-15350: choose SCT field default value as 'Gift' for membership use
2103 //else (for contribution), use configured SCT default value
2104 $SCTDefaultValue = CRM_Core_OptionGroup::getDefaultValue("soft_credit_type");
2105 if ($field['field_type'] == 'Membership') {
2106 $SCTDefaultValue = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_ContributionSoft', 'soft_credit_type_id', 'gift');
2107 }
2108 $form->addElement('hidden', 'sct_default_id', $SCTDefaultValue, ['id' => 'sct_default_id']);
2109 }
2110 elseif ($fieldName == 'contribution_soft_credit_pcp_id') {
2111 CRM_Contribute_Form_SoftCredit::addPCPFields($form, "[$rowNumber]");
2112 }
2113 elseif ($fieldName == 'currency') {
2114 $form->addCurrency($name, $title, $required, NULL, FALSE, FALSE);
2115 }
2116 elseif ($fieldName == 'contribution_page_id') {
2117 $form->add('select', $name, $title,
2118 CRM_Contribute_PseudoConstant::contributionPage(), $required, [
2119 'class' => 'big',
2120 'placeholder' => TRUE,
2121 ]
2122 );
2123 }
2124 elseif ($fieldName == 'activity_status_id') {
2125 $form->add('select', $name, $title,
2126 CRM_Core_PseudoConstant::activityStatus(), $required, ['placeholder' => TRUE]
2127 );
2128 }
2129 elseif ($fieldName == 'activity_engagement_level') {
2130 $form->add('select', $name, $title,
2131 CRM_Campaign_PseudoConstant::engagementLevel(), $required, ['placeholder' => TRUE]
2132 );
2133 }
2134 elseif ($fieldName == 'participant_status') {
2135 $cond = NULL;
2136 if ($online == TRUE) {
2137 $cond = 'visibility_id = 1';
2138 }
2139 $form->add('select', $name, $title,
2140 CRM_Event_PseudoConstant::participantStatus(NULL, $cond, 'label'), $required, ['placeholder' => TRUE]
2141 );
2142 }
2143 elseif ($fieldName == 'participant_role') {
2144 if (!empty($field['is_multiple'])) {
2145 $form->addCheckBox($name, $title, CRM_Event_PseudoConstant::participantRole(), NULL, NULL, NULL, NULL, '&nbsp', TRUE);
2146 }
2147 else {
2148 $form->add('select', $name, $title,
2149 CRM_Event_PseudoConstant::participantRole(), $required, ['placeholder' => TRUE]
2150 );
2151 }
2152 }
2153 elseif ($fieldName == 'world_region') {
2154 $form->add('select', $name, $title, CRM_Core_PseudoConstant::worldRegion(), $required, $selectAttributes);
2155 }
2156 elseif ($fieldName == 'signature_html') {
2157 $form->add('wysiwyg', $name, $title, CRM_Core_DAO::getAttribute('CRM_Core_DAO_Email', $fieldName));
2158 }
2159 elseif ($fieldName == 'signature_text') {
2160 $form->add('textarea', $name, $title, CRM_Core_DAO::getAttribute('CRM_Core_DAO_Email', $fieldName));
2161 }
2162 elseif (substr($fieldName, -11) == 'campaign_id') {
2163 if (CRM_Campaign_BAO_Campaign::isCampaignEnable()) {
2164 $campaigns = CRM_Campaign_BAO_Campaign::getCampaigns(CRM_Utils_Array::value($contactId,
2165 $form->_componentCampaigns
2166 ));
2167 $form->add('select', $name, $title,
2168 $campaigns, $required,
2169 [
2170 'class' => 'crm-select2 big',
2171 'placeholder' => TRUE,
2172 ]
2173 );
2174 }
2175 }
2176 elseif ($fieldName == 'activity_details') {
2177 $form->add('wysiwyg', $fieldName, $title, ['rows' => 4, 'cols' => 60], $required);
2178 }
2179 elseif ($fieldName == 'activity_duration') {
2180 $form->add('text', $name, $title, $attributes, $required);
2181 $form->addRule($name, ts('Please enter the duration as number of minutes (integers only).'), 'positiveInteger');
2182 }
2183 elseif ($fieldName == 'case_status') {
2184 $form->add('select', $name, $title,
2185 CRM_Case_BAO_Case::buildOptions('case_status_id', 'create'),
2186 $required, ['placeholder' => TRUE]
2187 );
2188 }
2189 else {
2190 if (substr($fieldName, 0, 3) === 'is_' or substr($fieldName, 0, 7) === 'do_not_') {
2191 $form->add('advcheckbox', $name, $title, $attributes, $required);
2192 }
2193 elseif (CRM_Utils_Array::value('html_type', $field) === 'Select Date') {
2194 $extra = isset($field['datepicker']) ? $field['datepicker']['extra'] : CRM_Utils_Date::getDatePickerExtra($field);
2195 $attributes = isset($field['datepicker']) ? $field['datepicker']['attributes'] : CRM_Utils_Date::getDatePickerAttributes($field);
2196 $form->add('datepicker', $name, $title, $attributes, $required, $extra);
2197 }
2198 else {
2199 $form->add('text', $name, $title, $attributes, $required);
2200 }
2201 }
2202
2203 static $hiddenSubtype = FALSE;
2204 if (!$hiddenSubtype && CRM_Contact_BAO_ContactType::isaSubType($field['field_type'])) {
2205 // In registration mode params are submitted via POST and we don't have any clue
2206 // about profile-id or the profile-type (which could be a subtype)
2207 // To generalize the behavior and simplify the process,
2208 // lets always add the hidden
2209 //subtype value if there is any, and we won't have to
2210 // compute it while processing.
2211 if ($usedFor) {
2212 $form->addElement('hidden', $usedFor . '[contact_sub_type]', $field['field_type']);
2213 }
2214 else {
2215 $form->addElement('hidden', 'contact_sub_type_hidden', $field['field_type']);
2216 }
2217 $hiddenSubtype = TRUE;
2218 }
2219
2220 if (($view && $mode != CRM_Profile_Form::MODE_SEARCH) || $isShared) {
2221 $form->freeze($name);
2222 }
2223
2224 //add the rules
2225 if (in_array($fieldName, [
2226 'non_deductible_amount',
2227 'total_amount',
2228 'fee_amount',
2229 'net_amount',
2230 ])) {
2231 $form->addRule($name, ts('Please enter a valid amount.'), 'money');
2232 }
2233 if ($rule) {
2234 if (!($rule == 'email' && $mode == CRM_Profile_Form::MODE_SEARCH)) {
2235 $form->addRule($name, ts('Please enter a valid %1', [1 => $title]), $rule);
2236 }
2237 }
2238 }
2239
2240 /**
2241 * Set profile defaults.
2242 *
2243 * @param int $contactId
2244 * Contact id.
2245 * @param array $fields
2246 * Associative array of fields.
2247 * @param array $defaults
2248 * Defaults array.
2249 * @param bool $singleProfile
2250 * True for single profile else false(Update multiple items).
2251 * @param int $componentId
2252 * Id for specific components like contribute, event etc.
2253 * @param null $component
2254 */
2255 public static function setProfileDefaults(
2256 $contactId, &$fields, &$defaults,
2257 $singleProfile = TRUE, $componentId = NULL, $component = NULL
2258 ) {
2259 if (!$componentId) {
2260 //get the contact details
2261 $contactDetails = CRM_Contact_BAO_Contact::getHierContactDetails($contactId, $fields);
2262 $details = $contactDetails[$contactId] ?? NULL;
2263 $multipleFields = ['website' => 'url'];
2264
2265 //start of code to set the default values
2266 foreach ($fields as $name => $field) {
2267 // skip pseudo fields
2268 if (substr($name, 0, 9) == 'phone_ext') {
2269 continue;
2270 }
2271
2272 //set the field name depending upon the profile mode(single/multiple)
2273 if ($singleProfile) {
2274 $fldName = $name;
2275 }
2276 else {
2277 $fldName = "field[$contactId][$name]";
2278 }
2279
2280 if ($name == 'group') {
2281 CRM_Contact_Form_Edit_TagsAndGroups::setDefaults($contactId, $defaults, CRM_Contact_Form_Edit_TagsAndGroups::GROUP, $fldName);
2282 }
2283 if ($name == 'tag') {
2284 CRM_Contact_Form_Edit_TagsAndGroups::setDefaults($contactId, $defaults, CRM_Contact_Form_Edit_TagsAndGroups::TAG, $fldName);
2285 }
2286
2287 if (!empty($details[$name]) || isset($details[$name])) {
2288 //to handle custom data (checkbox) to be written
2289 // to handle birth/deceased date, greeting_type and few other fields
2290 if (in_array($name, CRM_Contact_BAO_Contact::$_greetingTypes)) {
2291 $defaults[$fldName] = $details[$name . '_id'];
2292 $defaults[$name . '_custom'] = $details[$name . '_custom'];
2293 }
2294 elseif ($name == 'preferred_communication_method') {
2295 $v = $details[$name];
2296 if (!is_array($details[$name])) {
2297 $v = explode(CRM_Core_DAO::VALUE_SEPARATOR, $v);
2298 }
2299 foreach ($v as $item) {
2300 if ($item) {
2301 $defaults[$fldName . "[$item]"] = 1;
2302 }
2303 }
2304 }
2305 elseif ($name == 'contact_sub_type') {
2306 $defaults[$fldName] = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($details[$name], CRM_Core_DAO::VALUE_SEPARATOR));
2307 }
2308 elseif ($name == 'world_region') {
2309 $defaults[$fldName] = $details['worldregion_id'];
2310 }
2311 elseif (CRM_Core_BAO_CustomField::getKeyID($name)) {
2312 $defaults[$fldName] = self::formatCustomValue($field, $details[$name]);
2313 if (!$singleProfile && $field['html_type'] === 'CheckBox') {
2314 // For batch update profile there needs to be a key lik
2315 // $defaults['field[166]['custom_8'][2]'] => 1 where
2316 // 166 is the conntact id, 8 is the field id and 2 is the checkbox option.
2317 foreach ($defaults[$fldName] as $itemKey => $itemValue) {
2318 $defaults[$fldName . '[' . $itemKey . ']'] = $itemValue;
2319 }
2320 }
2321 }
2322 else {
2323 $defaults[$fldName] = $details[$name];
2324 }
2325 }
2326 else {
2327 $blocks = ['email', 'phone', 'im', 'openid'];
2328 list($fieldName, $locTypeId, $phoneTypeId) = CRM_Utils_System::explode('-', $name, 3);
2329 if (!in_array($fieldName, $multipleFields)) {
2330 if (is_array($details)) {
2331 foreach ($details as $key => $value) {
2332 // when we fixed CRM-5319 - get primary loc
2333 // type as per loc field and removed below code.
2334 $primaryLocationType = FALSE;
2335 if ($locTypeId == 'Primary') {
2336 if (is_array($value) && array_key_exists($fieldName, $value)) {
2337 $primaryLocationType = TRUE;
2338 if (in_array($fieldName, $blocks)) {
2339 $locTypeId = CRM_Contact_BAO_Contact::getPrimaryLocationType($contactId, FALSE, $fieldName);
2340 }
2341 else {
2342 $locTypeId = CRM_Contact_BAO_Contact::getPrimaryLocationType($contactId, FALSE, 'address');
2343 }
2344 }
2345 }
2346
2347 // fixed for CRM-665
2348 if (is_numeric($locTypeId)) {
2349 if ($primaryLocationType || $locTypeId == CRM_Utils_Array::value('location_type_id', $value)) {
2350 if (!empty($value[$fieldName])) {
2351 //to handle stateprovince and country
2352 if ($fieldName == 'state_province') {
2353 $defaults[$fldName] = $value['state_province_id'];
2354 }
2355 elseif ($fieldName == 'county') {
2356 $defaults[$fldName] = $value['county_id'];
2357 }
2358 elseif ($fieldName == 'country') {
2359 if (!isset($value['country_id']) || !$value['country_id']) {
2360 $config = CRM_Core_Config::singleton();
2361 if ($config->defaultContactCountry) {
2362 $defaults[$fldName] = $config->defaultContactCountry;
2363 }
2364 }
2365 else {
2366 $defaults[$fldName] = $value['country_id'];
2367 }
2368 }
2369 elseif ($fieldName == 'phone') {
2370 if ($phoneTypeId) {
2371 if (isset($value['phone'][$phoneTypeId])) {
2372 $defaults[$fldName] = $value['phone'][$phoneTypeId];
2373 }
2374 if (isset($value['phone_ext'][$phoneTypeId])) {
2375 $defaults[str_replace('phone', 'phone_ext', $fldName)] = $value['phone_ext'][$phoneTypeId];
2376 }
2377 }
2378 else {
2379 $phoneDefault = $value['phone'] ?? NULL;
2380 // CRM-9216
2381 if (!is_array($phoneDefault)) {
2382 $defaults[$fldName] = $phoneDefault;
2383 }
2384 }
2385 }
2386 elseif ($fieldName == 'email') {
2387 //adding the first email (currently we don't support multiple emails of same location type)
2388 $defaults[$fldName] = $value['email'];
2389 }
2390 elseif ($fieldName == 'im') {
2391 //adding the first im (currently we don't support multiple ims of same location type)
2392 $defaults[$fldName] = $value['im'];
2393 $defaults[$fldName . '-provider_id'] = $value['im_provider_id'];
2394 }
2395 else {
2396 $defaults[$fldName] = $value[$fieldName];
2397 }
2398 }
2399 elseif (strpos($fieldName, 'address_custom') === 0 && !empty($value[substr($fieldName, 8)])) {
2400 $defaults[$fldName] = self::formatCustomValue($field, $value[substr($fieldName, 8)]);
2401 }
2402 }
2403 }
2404 elseif (strpos($fieldName, 'address_custom') === 0 && !empty($value[substr($fieldName, 8)])) {
2405 $defaults[$fldName] = self::formatCustomValue($field, $value[substr($fieldName, 8)]);
2406 }
2407 }
2408 }
2409 }
2410 else {
2411 if (is_array($details)) {
2412 if ($fieldName === 'url'
2413 && !empty($details['website'])
2414 && !empty($details['website'][$locTypeId])
2415 ) {
2416 $defaults[$fldName] = $details['website'][$locTypeId]['url'] ?? NULL;
2417 }
2418 }
2419 }
2420 }
2421 }
2422 }
2423
2424 // Handling Contribution Part of the batch profile
2425 if (CRM_Core_Permission::access('CiviContribute') && $component == 'Contribute') {
2426 self::setComponentDefaults($fields, $componentId, $component, $defaults);
2427 }
2428
2429 // Handling Event Participation Part of the batch profile
2430 if (CRM_Core_Permission::access('CiviEvent') && $component == 'Event') {
2431 self::setComponentDefaults($fields, $componentId, $component, $defaults);
2432 }
2433
2434 // Handling membership Part of the batch profile
2435 if (CRM_Core_Permission::access('CiviMember') && $component == 'Membership') {
2436 self::setComponentDefaults($fields, $componentId, $component, $defaults);
2437 }
2438
2439 // Handling Activity Part of the batch profile
2440 if ($component == 'Activity') {
2441 self::setComponentDefaults($fields, $componentId, $component, $defaults);
2442 }
2443
2444 // Handling Case Part of the batch profile
2445 if (CRM_Core_Permission::access('CiviCase') && $component == 'Case') {
2446 self::setComponentDefaults($fields, $componentId, $component, $defaults);
2447 }
2448 }
2449
2450 /**
2451 * Get profiles by type eg: pure Individual etc
2452 *
2453 * @param array $types
2454 * Associative array of types eg: types('Individual').
2455 * @param bool $onlyPure
2456 * True if only pure profiles are required.
2457 *
2458 * @return array
2459 * associative array of profiles
2460 */
2461 public static function getProfiles($types, $onlyPure = FALSE) {
2462 $profiles = [];
2463 $ufGroups = CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id');
2464
2465 CRM_Utils_Hook::aclGroup(CRM_Core_Permission::ADMIN, NULL, 'civicrm_uf_group', $ufGroups, $ufGroups);
2466
2467 // Exclude Batch Data Entry profiles - CRM-10901
2468 $batchProfiles = CRM_Core_BAO_UFGroup::getBatchProfiles();
2469
2470 foreach ($ufGroups as $id => $title) {
2471 $ptype = CRM_Core_BAO_UFField::getProfileType($id, FALSE, $onlyPure);
2472 if (in_array($ptype, $types) && !array_key_exists($id, $batchProfiles)) {
2473 $profiles[$id] = $title;
2474 }
2475 }
2476 return $profiles;
2477 }
2478
2479 /**
2480 * Check whether a profile is valid combination of
2481 * required and/or optional profile types
2482 *
2483 * @param array $required
2484 * Array of types those are required.
2485 * @param array $optional
2486 * Array of types those are optional.
2487 *
2488 * @return array
2489 * associative array of profiles
2490 */
2491 public static function getValidProfiles($required, $optional = NULL) {
2492 if (!is_array($required) || empty($required)) {
2493 return NULL;
2494 }
2495
2496 $profiles = [];
2497 $ufGroups = CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id');
2498
2499 CRM_Utils_Hook::aclGroup(CRM_Core_Permission::ADMIN, NULL, 'civicrm_uf_group', $ufGroups, $ufGroups);
2500
2501 foreach ($ufGroups as $id => $title) {
2502 $type = CRM_Core_BAO_UFField::checkValidProfileType($id, $required, $optional);
2503 if ($type) {
2504 $profiles[$id] = $title;
2505 }
2506 }
2507
2508 return $profiles;
2509 }
2510
2511 /**
2512 * Check whether a profile is valid combination of
2513 * required profile fields
2514 *
2515 * @param array $ufId
2516 * Integer id of the profile.
2517 * @param array $required
2518 * Array of fields those are required in the profile.
2519 *
2520 * @return array
2521 * associative array of profiles
2522 */
2523 public static function checkValidProfile($ufId, $required = NULL) {
2524 $validProfile = FALSE;
2525 if (!$ufId) {
2526 return $validProfile;
2527 }
2528
2529 if (!CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $ufId, 'is_active')) {
2530 return $validProfile;
2531 }
2532
2533 $profileFields = self::getFields($ufId, FALSE, CRM_Core_Action::VIEW, NULL,
2534 NULL, FALSE, NULL, FALSE, NULL,
2535 CRM_Core_Permission::CREATE, NULL
2536 );
2537
2538 $validProfile = [];
2539 if (!empty($profileFields)) {
2540 $fields = array_keys($profileFields);
2541 foreach ($fields as $val) {
2542 foreach ($required as $key => $field) {
2543 if (strpos($val, $field) === 0) {
2544 unset($required[$key]);
2545 }
2546 }
2547 }
2548
2549 $validProfile = (empty($required));
2550 }
2551
2552 return $validProfile;
2553 }
2554
2555 /**
2556 * Get default value for Register.
2557 *
2558 * @param array $fields
2559 * @param array $defaults
2560 *
2561 * @return array
2562 */
2563 public static function setRegisterDefaults(&$fields, &$defaults) {
2564 $config = CRM_Core_Config::singleton();
2565 foreach ($fields as $name => $field) {
2566 if (substr($name, 0, 8) == 'country-') {
2567 if (!empty($config->defaultContactCountry)) {
2568 $defaults[$name] = $config->defaultContactCountry;
2569 }
2570 }
2571 elseif (substr($name, 0, 15) == 'state_province-') {
2572 if (!empty($config->defaultContactStateProvince)) {
2573 $defaults[$name] = $config->defaultContactStateProvince;
2574 }
2575 }
2576 }
2577 return $defaults;
2578 }
2579
2580 /**
2581 * make a copy of a profile, including
2582 * all the fields in the profile
2583 *
2584 * @param int $id
2585 * The profile id to copy.
2586 *
2587 * @return \CRM_Core_DAO
2588 */
2589 public static function copy($id) {
2590 $maxId = CRM_Core_DAO::singleValueQuery("SELECT max(id) FROM civicrm_uf_group");
2591
2592 $title = ts('[Copy id %1]', [1 => $maxId + 1]);
2593 $fieldsFix = [
2594 'suffix' => [
2595 'title' => ' ' . $title,
2596 'name' => '__Copy_id_' . ($maxId + 1) . '_',
2597 ],
2598 ];
2599
2600 $copy = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFGroup',
2601 ['id' => $id],
2602 NULL,
2603 $fieldsFix
2604 );
2605
2606 if ($pos = strrpos($copy->name, "_{$id}")) {
2607 $copy->name = substr_replace($copy->name, '', $pos);
2608 }
2609 $copy->name = CRM_Utils_String::munge($copy->name, '_', 56) . "_{$copy->id}";
2610 $copy->save();
2611
2612 $copyUFJoin = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFJoin',
2613 ['uf_group_id' => $id],
2614 ['uf_group_id' => $copy->id],
2615 NULL,
2616 'entity_table'
2617 );
2618
2619 $copyUFField = CRM_Core_DAO::copyGeneric('CRM_Core_BAO_UFField',
2620 ['uf_group_id' => $id],
2621 ['uf_group_id' => $copy->id]
2622 );
2623
2624 $maxWeight = CRM_Utils_Weight::getMax('CRM_Core_DAO_UFJoin', NULL, 'weight');
2625
2626 //update the weight
2627 $query = "
2628 UPDATE civicrm_uf_join
2629 SET weight = %1
2630 WHERE uf_group_id = %2
2631 AND ( entity_id IS NULL OR entity_id <= 0 )
2632 ";
2633 $p = [
2634 1 => [$maxWeight + 1, 'Integer'],
2635 2 => [$copy->id, 'Integer'],
2636 ];
2637 CRM_Core_DAO::executeQuery($query, $p);
2638 if ($copy->is_reserved) {
2639 $query = "UPDATE civicrm_uf_group SET is_reserved = 0 WHERE id = %1";
2640 $params = [1 => [$copy->id, 'Integer']];
2641 CRM_Core_DAO::executeQuery($query, $params);
2642 }
2643 CRM_Utils_Hook::copy('UFGroup', $copy);
2644
2645 return $copy;
2646 }
2647
2648 /**
2649 * Process that send notification e-mails
2650 *
2651 * @param int $contactID
2652 * Contact id.
2653 * @param array $values
2654 * Associative array of name/value pair.
2655 */
2656 public static function commonSendMail($contactID, &$values) {
2657 if (!$contactID || !$values) {
2658 return;
2659
2660 }
2661 $template = CRM_Core_Smarty::singleton();
2662
2663 $displayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
2664 $contactID,
2665 'display_name'
2666 );
2667
2668 self::profileDisplay($values['id'], $values['values'], $template);
2669 $emailList = explode(',', $values['email']);
2670
2671 $contactLink = CRM_Utils_System::url('civicrm/contact/view',
2672 "reset=1&cid=$contactID",
2673 TRUE, NULL, FALSE, FALSE, TRUE
2674 );
2675
2676 //get the default domain email address.
2677 list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain::getNameAndEmail();
2678
2679 if (!$domainEmailAddress || $domainEmailAddress == 'info@EXAMPLE.ORG') {
2680 $fixUrl = CRM_Utils_System::url('civicrm/admin/domain', 'action=update&reset=1');
2681 CRM_Core_Error::statusBounce(ts('The site administrator needs to enter a valid \'FROM Email Address\' in <a href="%1">Administer CiviCRM &raquo; Communications &raquo; FROM Email Addresses</a>. The email address used may need to be a valid mail account with your email service provider.', [1 => $fixUrl]));
2682 }
2683
2684 foreach ($emailList as $emailTo) {
2685 // FIXME: take the below out of the foreach loop
2686 CRM_Core_BAO_MessageTemplate::sendTemplate(
2687 [
2688 'groupName' => 'msg_tpl_workflow_uf',
2689 'valueName' => 'uf_notify',
2690 'contactId' => $contactID,
2691 'tplParams' => [
2692 'displayName' => $displayName,
2693 'currentDate' => date('r'),
2694 'contactLink' => $contactLink,
2695 ],
2696 'from' => "$domainEmailName <$domainEmailAddress>",
2697 'toEmail' => $emailTo,
2698 ]
2699 );
2700 }
2701 }
2702
2703 /**
2704 * Given a contact id and a group id, returns the field values from the db
2705 * for this group and notify email only if group's notify field is
2706 * set and field values are not empty
2707 *
2708 * @param int $gid
2709 * Group id.
2710 * @param int $cid
2711 * Contact id.
2712 * @param array $params
2713 * @param bool $skipCheck
2714 *
2715 * @return array
2716 */
2717 public function checkFieldsEmptyValues($gid, $cid, $params, $skipCheck = FALSE) {
2718 if ($gid) {
2719 if (CRM_Core_BAO_UFGroup::filterUFGroups($gid, $cid) || $skipCheck) {
2720 $values = [];
2721 $fields = CRM_Core_BAO_UFGroup::getFields($gid, FALSE, CRM_Core_Action::VIEW);
2722 CRM_Core_BAO_UFGroup::getValues($cid, $fields, $values, FALSE, $params, TRUE);
2723
2724 $email = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'notify');
2725
2726 if (!empty($values) &&
2727 !empty($email)
2728 ) {
2729 $val = [
2730 'id' => $gid,
2731 'values' => $values,
2732 'email' => $email,
2733 ];
2734 return $val;
2735 }
2736 }
2737 }
2738 return NULL;
2739 }
2740
2741 /**
2742 * Assign uf fields to template.
2743 *
2744 * @param int $gid
2745 * Group id.
2746 * @param array $values
2747 * @param CRM_Core_Smarty $template
2748 */
2749 public static function profileDisplay($gid, $values, $template) {
2750 $groupTitle = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'title');
2751 $template->assign('grouptitle', $groupTitle);
2752 if (count($values)) {
2753 $template->assign('values', $values);
2754 }
2755 }
2756
2757 /**
2758 * Format fields for dupe Contact Matching.
2759 *
2760 * @param array $params
2761 *
2762 * @param int $contactId
2763 *
2764 * @return array
2765 * associated formatted array
2766 */
2767 public static function formatFields($params, $contactId = NULL) {
2768 if ($contactId) {
2769 // get the primary location type id and email
2770 list($name, $primaryEmail, $primaryLocationType) = CRM_Contact_BAO_Contact_Location::getEmailDetails($contactId);
2771 }
2772 else {
2773 $defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
2774 $primaryLocationType = $defaultLocationType->id;
2775 }
2776
2777 $data = [];
2778 $locationType = [];
2779 $count = 1;
2780 $primaryLocation = 0;
2781 foreach ($params as $key => $value) {
2782 list($fieldName, $locTypeId, $phoneTypeId) = explode('-', $key);
2783
2784 if ($locTypeId == 'Primary') {
2785 $locTypeId = $primaryLocationType;
2786 }
2787
2788 if (is_numeric($locTypeId)) {
2789 if (!in_array($locTypeId, $locationType)) {
2790 $locationType[$count] = $locTypeId;
2791 $count++;
2792 }
2793 $loc = CRM_Utils_Array::key($locTypeId, $locationType);
2794
2795 $data['location'][$loc]['location_type_id'] = $locTypeId;
2796
2797 // if we are getting in a new primary email, dont overwrite the new one
2798 if ($locTypeId == $primaryLocationType) {
2799 if (!empty($params['email-' . $primaryLocationType])) {
2800 $data['location'][$loc]['email'][$loc]['email'] = $fields['email-' . $primaryLocationType];
2801 }
2802 elseif (isset($primaryEmail)) {
2803 $data['location'][$loc]['email'][$loc]['email'] = $primaryEmail;
2804 }
2805 $primaryLocation++;
2806 }
2807
2808 if ($loc == 1) {
2809 $data['location'][$loc]['is_primary'] = 1;
2810 }
2811 if ($fieldName == 'phone') {
2812 if ($phoneTypeId) {
2813 $data['location'][$loc]['phone'][$loc]['phone_type_id'] = $phoneTypeId;
2814 }
2815 else {
2816 $data['location'][$loc]['phone'][$loc]['phone_type_id'] = '';
2817 }
2818 $data['location'][$loc]['phone'][$loc]['phone'] = $value;
2819 }
2820 elseif ($fieldName == 'email') {
2821 $data['location'][$loc]['email'][$loc]['email'] = $value;
2822 }
2823 elseif ($fieldName == 'im') {
2824 $data['location'][$loc]['im'][$loc]['name'] = $value;
2825 }
2826 else {
2827 if ($fieldName === 'state_province') {
2828 $data['location'][$loc]['address']['state_province_id'] = $value;
2829 }
2830 elseif ($fieldName === 'country') {
2831 $data['location'][$loc]['address']['country_id'] = $value;
2832 }
2833 else {
2834 $data['location'][$loc]['address'][$fieldName] = $value;
2835 }
2836 }
2837 }
2838 else {
2839 // TODO: prefix, suffix and gender translation may no longer be necessary - check inputs
2840 if ($key === 'individual_suffix') {
2841 $data['suffix_id'] = $value;
2842 }
2843 elseif ($key === 'individual_prefix') {
2844 $data['prefix_id'] = $value;
2845 }
2846 elseif ($key === 'gender') {
2847 $data['gender_id'] = $value;
2848 }
2849 elseif (substr($key, 0, 6) === 'custom') {
2850 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
2851 //fix checkbox
2852 if ($customFields[$customFieldID]['html_type'] == 'CheckBox') {
2853 $value = implode(CRM_Core_DAO::VALUE_SEPARATOR, array_keys($value));
2854 }
2855 // fix the date field
2856 if ($customFields[$customFieldID]['data_type'] == 'Date') {
2857 $date = CRM_Utils_Date::format($value);
2858 if (!$date) {
2859 $date = '';
2860 }
2861 $value = $date;
2862 }
2863
2864 $data['custom'][$customFieldID] = [
2865 'id' => $id,
2866 'value' => $value,
2867 'extends' => $customFields[$customFieldID]['extends'],
2868 'type' => $customFields[$customFieldID]['data_type'],
2869 'custom_field_id' => $customFieldID,
2870 ];
2871 }
2872 }
2873 elseif ($key == 'edit') {
2874 continue;
2875 }
2876 else {
2877 $data[$key] = $value;
2878 }
2879 }
2880 }
2881
2882 if (!$primaryLocation) {
2883 $loc++;
2884 $data['location'][$loc]['email'][$loc]['email'] = $primaryEmail;
2885 }
2886
2887 return $data;
2888 }
2889
2890 /**
2891 * Calculate the profile type 'group_type' as per profile fields.
2892 *
2893 * @param int $gId
2894 * Profile id.
2895 * @param bool $includeTypeValues
2896 * @param int $ignoreFieldId
2897 * Ignore particular profile field.
2898 *
2899 * @return array
2900 * list of calculated group type
2901 */
2902 public static function calculateGroupType($gId, $includeTypeValues = FALSE, $ignoreFieldId = NULL) {
2903 //get the profile fields.
2904 $ufFields = self::getFields($gId, FALSE, NULL, NULL, NULL, TRUE, NULL, TRUE);
2905 return self::_calculateGroupType($ufFields, $includeTypeValues, $ignoreFieldId);
2906 }
2907
2908 /**
2909 * Calculate the profile type 'group_type' as per profile fields.
2910 *
2911 * @param $ufFields
2912 * @param bool $includeTypeValues
2913 * @param int $ignoreFieldId
2914 * Ignore perticular profile field.
2915 *
2916 * @return array
2917 * list of calculated group type
2918 */
2919 public static function _calculateGroupType($ufFields, $includeTypeValues = FALSE, $ignoreFieldId = NULL) {
2920 $groupType = $groupTypeValues = $customFieldIds = [];
2921 if (!empty($ufFields)) {
2922 foreach ($ufFields as $fieldName => $fieldValue) {
2923 //ignore field from group type when provided.
2924 //in case of update profile field.
2925 if ($ignoreFieldId && ($ignoreFieldId == $fieldValue['field_id'])) {
2926 continue;
2927 }
2928 if (!in_array($fieldValue['field_type'], $groupType)) {
2929 $groupType[$fieldValue['field_type']] = $fieldValue['field_type'];
2930 }
2931
2932 if ($includeTypeValues && ($fldId = CRM_Core_BAO_CustomField::getKeyID($fieldName))) {
2933 $customFieldIds[$fldId] = $fldId;
2934 }
2935 }
2936 }
2937
2938 if (!empty($customFieldIds)) {
2939 $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) . ')';
2940
2941 $customGroups = CRM_Core_DAO::executeQuery($query);
2942 while ($customGroups->fetch()) {
2943 if (!$customGroups->extends_entity_column_value) {
2944 continue;
2945 }
2946
2947 $groupTypeName = "{$customGroups->extends}Type";
2948 if ($customGroups->extends == 'Participant' && $customGroups->extends_entity_column_id) {
2949 $groupTypeName = CRM_Core_PseudoConstant::getName('CRM_Core_DAO_CustomGroup', 'extends_entity_column_id', $customGroups->extends_entity_column_id);
2950 }
2951
2952 foreach (explode(CRM_Core_DAO::VALUE_SEPARATOR, $customGroups->extends_entity_column_value) as $val) {
2953 if ($val) {
2954 $groupTypeValues[$groupTypeName][$val] = $val;
2955 }
2956 }
2957 }
2958
2959 if (!empty($groupTypeValues)) {
2960 $groupType = array_merge($groupType, $groupTypeValues);
2961 }
2962 }
2963
2964 return $groupType;
2965 }
2966
2967 /**
2968 * Update the profile type 'group_type' as per profile fields including group types and group subtype values.
2969 * Build and store string like: group_type1,group_type2[VALUE_SEPARATOR]group_type1Type:1:2:3,group_type2Type:1:2
2970 *
2971 * FIELDS GROUP_TYPE
2972 * BirthDate + Email Individual,Contact
2973 * BirthDate + Subject Individual,Activity
2974 * BirthDate + Subject + SurveyOnlyField Individual,Activity\0ActivityType:28
2975 * BirthDate + Subject + SurveyOnlyField + PhoneOnlyField (Not allowed)
2976 * BirthDate + SurveyOnlyField Individual,Activity\0ActivityType:28
2977 * BirthDate + Subject + SurveyOrPhoneField Individual,Activity\0ActivityType:2:28
2978 * BirthDate + SurveyOrPhoneField Individual,Activity\0ActivityType:2:28
2979 * BirthDate + SurveyOrPhoneField + SurveyOnlyField Individual,Activity\0ActivityType:2:28
2980 * BirthDate + StudentField + Subject + SurveyOnlyField Individual,Activity,Student\0ActivityType:28
2981 *
2982 * @param int $gId
2983 * @param array $groupTypes
2984 * With key having group type names.
2985 *
2986 * @return bool
2987 */
2988 public static function updateGroupTypes($gId, $groupTypes = []) {
2989 if (!is_array($groupTypes) || !$gId) {
2990 return FALSE;
2991 }
2992
2993 // If empty group types set group_type as 'null'
2994 if (empty($groupTypes)) {
2995 return CRM_Core_DAO::setFieldValue('CRM_Core_DAO_UFGroup', $gId, 'group_type', 'null');
2996 }
2997
2998 $componentGroupTypes = ['Contribution', 'Participant', 'Membership', 'Activity', 'Case'];
2999 $validGroupTypes = array_merge([
3000 'Contact',
3001 'Individual',
3002 'Organization',
3003 'Household',
3004 ], $componentGroupTypes, CRM_Contact_BAO_ContactType::subTypes());
3005
3006 $gTypes = $gTypeValues = [];
3007
3008 $participantExtends = ['ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
3009 // Get valid group type and group subtypes
3010 foreach ($groupTypes as $groupType => $value) {
3011 if (in_array($groupType, $validGroupTypes) && !in_array($groupType, $gTypes)) {
3012 $gTypes[] = $groupType;
3013 }
3014
3015 $subTypesOf = NULL;
3016
3017 if (in_array($groupType, $participantExtends)) {
3018 $subTypesOf = $groupType;
3019 }
3020 elseif (strpos($groupType, 'Type') > 0) {
3021 $subTypesOf = substr($groupType, 0, strpos($groupType, 'Type'));
3022 }
3023 else {
3024 continue;
3025 }
3026
3027 if (!empty($value) &&
3028 (in_array($subTypesOf, $componentGroupTypes) ||
3029 in_array($subTypesOf, $participantExtends)
3030 )
3031 ) {
3032 $gTypeValues[$subTypesOf] = $groupType . ":" . implode(':', $value);
3033 }
3034 }
3035
3036 if (empty($gTypes)) {
3037 return FALSE;
3038 }
3039
3040 // Build String to store group types and group subtypes
3041 $groupTypeString = implode(',', $gTypes);
3042 if (!empty($gTypeValues)) {
3043 $groupTypeString .= CRM_Core_DAO::VALUE_SEPARATOR . implode(',', $gTypeValues);
3044 }
3045
3046 return CRM_Core_DAO::setFieldValue('CRM_Core_DAO_UFGroup', $gId, 'group_type', $groupTypeString);
3047 }
3048
3049 /**
3050 * Create a "group_type" string.
3051 *
3052 * @param array $coreTypes
3053 * E.g. array('Individual','Contact','Student').
3054 * @param array $subTypes
3055 * E.g. array('ActivityType' => array(7, 11)).
3056 * @param string $delim
3057 *
3058 * @return string
3059 * @throws CRM_Core_Exception
3060 */
3061 public static function encodeGroupType($coreTypes, $subTypes, $delim = CRM_Core_DAO::VALUE_SEPARATOR) {
3062 $groupTypeExpr = '';
3063 if ($coreTypes) {
3064 $groupTypeExpr .= implode(',', $coreTypes);
3065 }
3066 if ($subTypes) {
3067 //CRM-15427 Allow Multiple subtype filtering
3068 //if (count($subTypes) > 1) {
3069 //throw new CRM_Core_Exception("Multiple subtype filtering is not currently supported by widget.");
3070 //}
3071 foreach ($subTypes as $subType => $subTypeIds) {
3072 $groupTypeExpr .= $delim . $subType . ':' . implode(':', $subTypeIds);
3073 }
3074 }
3075 return $groupTypeExpr;
3076 }
3077
3078 /**
3079 * setDefault componet specific profile fields.
3080 *
3081 * @param array $fields
3082 * Profile fields.
3083 * @param int $componentId
3084 * ComponetID.
3085 * @param string $component
3086 * Component name.
3087 * @param array $defaults
3088 * An array of default values.
3089 *
3090 * @param bool $isStandalone
3091 */
3092 public static function setComponentDefaults(&$fields, $componentId, $component, &$defaults, $isStandalone = FALSE) {
3093 if (!$componentId ||
3094 !in_array($component, ['Contribute', 'Membership', 'Event', 'Activity', 'Case'])
3095 ) {
3096 return;
3097 }
3098
3099 $componentBAO = $componentSubType = NULL;
3100 switch ($component) {
3101 case 'Membership':
3102 $componentBAO = 'CRM_Member_BAO_Membership';
3103 $componentBAOName = 'Membership';
3104 $componentSubType = ['membership_type_id'];
3105 break;
3106
3107 case 'Contribute':
3108 $componentBAO = 'CRM_Contribute_BAO_Contribution';
3109 $componentBAOName = 'Contribution';
3110 $componentSubType = ['financial_type_id'];
3111 break;
3112
3113 case 'Event':
3114 $componentBAO = 'CRM_Event_BAO_Participant';
3115 $componentBAOName = 'Participant';
3116 $componentSubType = ['role_id', 'event_id', 'event_type_id'];
3117 break;
3118
3119 case 'Activity':
3120 $componentBAO = 'CRM_Activity_BAO_Activity';
3121 $componentBAOName = 'Activity';
3122 $componentSubType = ['activity_type_id'];
3123 break;
3124
3125 case 'Case':
3126 $componentBAO = 'CRM_Case_BAO_Case';
3127 $componentBAOName = 'Case';
3128 $componentSubType = ['case_type_id'];
3129 break;
3130 }
3131
3132 $values = [];
3133 $params = ['id' => $componentId];
3134
3135 //get the component values.
3136 CRM_Core_DAO::commonRetrieve($componentBAO, $params, $values);
3137 if ($componentBAOName == 'Participant') {
3138 $values += ['event_type_id' => CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $values['event_id'], 'event_type_id')];
3139 }
3140
3141 $formattedGroupTree = [];
3142
3143 foreach ($fields as $name => $field) {
3144 $fldName = $isStandalone ? $name : "field[$componentId][$name]";
3145 if (array_key_exists($name, $values)) {
3146 $defaults[$fldName] = $values[$name];
3147 }
3148 elseif ($name == 'participant_note') {
3149 $noteDetails = CRM_Core_BAO_Note::getNote($componentId, 'civicrm_participant');
3150 $defaults[$fldName] = array_pop($noteDetails);
3151 }
3152 elseif (in_array($name, [
3153 'financial_type',
3154 'payment_instrument',
3155 'participant_status',
3156 'participant_role',
3157 ])) {
3158 $defaults[$fldName] = $values["{$name}_id"];
3159 }
3160 elseif ($name == 'membership_type') {
3161 // since membership_type field is a hierselect -
3162 $defaults[$fldName][0]
3163 = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $values['membership_type_id'], 'member_of_contact_id', 'id');
3164 $defaults[$fldName][1] = $values['membership_type_id'];
3165 }
3166 elseif ($name == 'membership_status') {
3167 $defaults[$fldName] = $values['status_id'];
3168 }
3169 elseif ($name == 'case_status') {
3170 $defaults[$fldName] = $values['case_status_id'];
3171 }
3172 elseif (CRM_Core_BAO_CustomField::getKeyID($name, TRUE) !== [NULL, NULL]) {
3173 if (empty($formattedGroupTree)) {
3174 //get the groupTree as per subTypes.
3175 $groupTree = [];
3176 foreach ($componentSubType as $subType) {
3177 $subTree = CRM_Core_BAO_CustomGroup::getTree($componentBAOName, NULL,
3178 $componentId, 0, $values[$subType]
3179 );
3180 $groupTree = CRM_Utils_Array::crmArrayMerge($groupTree, $subTree);
3181 }
3182 $formattedGroupTree = CRM_Core_BAO_CustomGroup::formatGroupTree($groupTree, 1);
3183 CRM_Core_BAO_CustomGroup::setDefaults($formattedGroupTree, $defaults);
3184 }
3185
3186 //FIX ME: We need to loop defaults, but once we move to custom_1_x convention this code can be simplified.
3187 foreach ($defaults as $customKey => $customValue) {
3188 if ($customFieldDetails = CRM_Core_BAO_CustomField::getKeyID($customKey, TRUE)) {
3189 if ($name == 'custom_' . $customFieldDetails[0]) {
3190
3191 //hack to set default for checkbox
3192 //basically this is for weired field name like field[33][custom_19]
3193 //we are converting this field name to array structure and assign value.
3194 $skipValue = FALSE;
3195
3196 foreach ($formattedGroupTree as $tree) {
3197 if (!empty($tree['fields'][$customFieldDetails[0]])) {
3198 if ('CheckBox' == CRM_Utils_Array::value('html_type', $tree['fields'][$customFieldDetails[0]])) {
3199 $skipValue = TRUE;
3200 $defaults['field'][$componentId][$name] = $customValue;
3201 break;
3202 }
3203 elseif (CRM_Utils_Array::value('data_type', $tree['fields'][$customFieldDetails[0]]) == 'Date') {
3204 $skipValue = TRUE;
3205
3206 // CRM-6681, $default contains formatted date, time values.
3207 $defaults[$fldName] = $customValue;
3208 if (!empty($defaults[$customKey . '_time'])) {
3209 $defaults['field'][$componentId][$name . '_time'] = $defaults[$customKey . '_time'];
3210 }
3211 }
3212 }
3213 }
3214
3215 if (!$skipValue || $isStandalone) {
3216 $defaults[$fldName] = $customValue;
3217 }
3218 unset($defaults[$customKey]);
3219 break;
3220 }
3221 }
3222 }
3223 }
3224 elseif (isset($values[$fldName])) {
3225 $defaults[$fldName] = $values[$fldName];
3226 }
3227 }
3228 }
3229
3230 /**
3231 * Retrieve groups of profiles.
3232 *
3233 * @param int $profileID
3234 * Id of the profile.
3235 *
3236 * @return array
3237 * returns array
3238 */
3239 public static function profileGroups($profileID) {
3240 $groupTypes = [];
3241 $profileTypes = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $profileID, 'group_type');
3242 if ($profileTypes) {
3243 $groupTypeParts = explode(CRM_Core_DAO::VALUE_SEPARATOR, $profileTypes);
3244 $groupTypes = explode(',', $groupTypeParts[0]);
3245 }
3246 return $groupTypes;
3247 }
3248
3249 /**
3250 * Alter contact params by filtering existing subscribed groups and returns
3251 * unsubscribed groups array for subscription.
3252 *
3253 * @param array $params
3254 * Contact params.
3255 * @param int $contactId
3256 * User contact id.
3257 *
3258 * @return array
3259 * This contains array of groups for subscription
3260 */
3261 public static function getDoubleOptInGroupIds(&$params, $contactId = NULL) {
3262 $config = CRM_Core_Config::singleton();
3263 $subscribeGroupIds = [];
3264
3265 // process further only if profileDoubleOptIn enabled and if groups exist
3266 if (!array_key_exists('group', $params) ||
3267 !self::isProfileDoubleOptin() ||
3268 CRM_Utils_System::isNull($params['group'])
3269 ) {
3270 return $subscribeGroupIds;
3271 }
3272
3273 //check if contact email exist.
3274 $hasEmails = FALSE;
3275 foreach ($params as $name => $value) {
3276 if (strpos($name, 'email-') !== FALSE) {
3277 $hasEmails = TRUE;
3278 break;
3279 }
3280 }
3281
3282 //Proceed furthur only if email present
3283 if (!$hasEmails) {
3284 return $subscribeGroupIds;
3285 }
3286
3287 //do check for already subscriptions.
3288 $contactGroups = [];
3289 if ($contactId) {
3290 $query = "
3291 SELECT group_id
3292 FROM civicrm_group_contact
3293 WHERE status = 'Added'
3294 AND contact_id = %1";
3295
3296 $dao = CRM_Core_DAO::executeQuery($query, [1 => [$contactId, 'Integer']]);
3297 while ($dao->fetch()) {
3298 $contactGroups[$dao->group_id] = $dao->group_id;
3299 }
3300 }
3301
3302 //since we don't have names, compare w/ label.
3303 $mailingListGroupType = array_search('Mailing List', CRM_Core_OptionGroup::values('group_type'));
3304
3305 //actual processing start.
3306 foreach ($params['group'] as $groupId => $isSelected) {
3307 //unset group those are not selected.
3308 if (!$isSelected) {
3309 unset($params['group'][$groupId]);
3310 continue;
3311 }
3312
3313 $groupTypes = explode(CRM_Core_DAO::VALUE_SEPARATOR,
3314 CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $groupId, 'group_type', 'id')
3315 );
3316 //get only mailing type group and unset it from params
3317 if (in_array($mailingListGroupType, $groupTypes) && !in_array($groupId, $contactGroups)) {
3318 $subscribeGroupIds[$groupId] = $groupId;
3319 unset($params['group'][$groupId]);
3320 }
3321 }
3322
3323 return $subscribeGroupIds;
3324 }
3325
3326 /**
3327 * Check if we are rendering mixed profiles.
3328 *
3329 * @param array $profileIds
3330 * Associated array of profile ids.
3331 *
3332 * @return bool
3333 * true if profile is mixed
3334 */
3335 public static function checkForMixProfiles($profileIds) {
3336 $mixProfile = FALSE;
3337
3338 $contactTypes = ['Individual', 'Household', 'Organization'];
3339 $subTypes = CRM_Contact_BAO_ContactType::subTypes();
3340
3341 $components = ['Contribution', 'Participant', 'Membership', 'Activity'];
3342
3343 $typeCount = ['ctype' => [], 'subtype' => []];
3344 foreach ($profileIds as $gid) {
3345 $profileType = CRM_Core_BAO_UFField::getProfileType($gid);
3346 // ignore profile of type Contact
3347 if ($profileType == 'Contact') {
3348 continue;
3349 }
3350 if (in_array($profileType, $contactTypes)) {
3351 if (!isset($typeCount['ctype'][$profileType])) {
3352 $typeCount['ctype'][$profileType] = 1;
3353 }
3354
3355 // check if we are rendering profile of different contact types
3356 if (count($typeCount['ctype']) == 2) {
3357 $mixProfile = TRUE;
3358 break;
3359 }
3360 }
3361 elseif (in_array($profileType, $components)) {
3362 $mixProfile = TRUE;
3363 break;
3364 }
3365 else {
3366 if (!isset($typeCount['subtype'][$profileType])) {
3367 $typeCount['subtype'][$profileType] = 1;
3368 }
3369 // check if we are rendering profile of different contact sub types
3370 if (count($typeCount['subtype']) == 2) {
3371 $mixProfile = TRUE;
3372 break;
3373 }
3374 }
3375 }
3376 return $mixProfile;
3377 }
3378
3379 /**
3380 * Determine of we show overlay profile or not.
3381 *
3382 * @return bool
3383 * true if profile should be shown else false
3384 */
3385 public static function showOverlayProfile() {
3386 $showOverlay = TRUE;
3387
3388 // get the id of overlay profile
3389 $overlayProfileId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', 'summary_overlay', 'id', 'name');
3390 $query = "SELECT count(id) FROM civicrm_uf_field WHERE uf_group_id = {$overlayProfileId} AND visibility IN ('Public Pages', 'Public Pages and Listings') ";
3391
3392 $count = CRM_Core_DAO::singleValueQuery($query);
3393
3394 //check if there are no public fields and use is anonymous
3395 $session = CRM_Core_Session::singleton();
3396 if (!$count && !$session->get('userID')) {
3397 $showOverlay = FALSE;
3398 }
3399
3400 return $showOverlay;
3401 }
3402
3403 /**
3404 * Get group type values of the profile.
3405 *
3406 * @param int $profileId
3407 * @param string $groupType
3408 *
3409 * @return array
3410 * group type values
3411 */
3412 public static function groupTypeValues($profileId, $groupType = NULL) {
3413 $groupTypeValue = [];
3414 $groupTypes = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $profileId, 'group_type');
3415
3416 $groupTypeParts = explode(CRM_Core_DAO::VALUE_SEPARATOR, $groupTypes);
3417 if (empty($groupTypeParts[1])) {
3418 return $groupTypeValue;
3419 }
3420 $participantExtends = ['ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
3421
3422 foreach (explode(',', $groupTypeParts[1]) as $groupTypeValues) {
3423 $values = [];
3424 $valueParts = explode(':', $groupTypeValues);
3425 if ($groupType &&
3426 ($valueParts[0] != "{$groupType}Type" ||
3427 ($groupType == 'Participant' &&
3428 !in_array($valueParts[0], $participantExtends)
3429 )
3430 )
3431 ) {
3432 continue;
3433 }
3434 foreach ($valueParts as $val) {
3435 if (CRM_Utils_Rule::integer($val)) {
3436 $values[$val] = $val;
3437 }
3438 }
3439 if (!empty($values)) {
3440 $typeName = substr($valueParts[0], 0, -4);
3441 if (in_array($valueParts[0], $participantExtends)) {
3442 $typeName = $valueParts[0];
3443 }
3444 $groupTypeValue[$typeName] = $values;
3445 }
3446 }
3447
3448 return $groupTypeValue;
3449 }
3450
3451 /**
3452 * @return bool|object
3453 */
3454 public static function isProfileDoubleOptin() {
3455 // check for double optin
3456 $config = CRM_Core_Config::singleton();
3457 if (in_array('CiviMail', $config->enableComponents)) {
3458 return Civi::settings()->get('profile_double_optin');
3459 }
3460 return FALSE;
3461 }
3462
3463 /**
3464 * @return bool|object
3465 */
3466 public static function isProfileAddToGroupDoubleOptin() {
3467 // check for add to group double optin
3468 $config = CRM_Core_Config::singleton();
3469 if (in_array('CiviMail', $config->enableComponents)) {
3470 return Civi::settings()->get('profile_add_to_group_double_optin');
3471 }
3472 return FALSE;
3473 }
3474
3475 /**
3476 * Get profiles used for batch entry.
3477 *
3478 * @return array
3479 * profileIds profile ids
3480 */
3481 public static function getBatchProfiles() {
3482 $query = "SELECT id
3483 FROM civicrm_uf_group
3484 WHERE name IN ('contribution_batch_entry', 'membership_batch_entry')";
3485 $dao = CRM_Core_DAO::executeQuery($query);
3486 $profileIds = [];
3487 while ($dao->fetch()) {
3488 $profileIds[$dao->id] = $dao->id;
3489 }
3490 return $profileIds;
3491 }
3492
3493 /**
3494 * @param $source
3495 * @param $destination
3496 * @param bool $returnMultiSummaryFields
3497 *
3498 * @return array|null
3499 * @todo what do I do?
3500 */
3501 public static function shiftMultiRecordFields(&$source, &$destination, $returnMultiSummaryFields = FALSE) {
3502 $multiSummaryFields = $returnMultiSummaryFields ? [] : NULL;
3503 foreach ($source as $field => $properties) {
3504 if (!CRM_Core_BAO_CustomField::getKeyID($field)) {
3505 continue;
3506 }
3507 if (CRM_Core_BAO_CustomField::isMultiRecordField($field)) {
3508 $destination[$field] = $properties;
3509 if ($returnMultiSummaryFields) {
3510 if ($properties['is_multi_summary']) {
3511 $multiSummaryFields[$field] = $properties;
3512 }
3513 }
3514 unset($source[$field]);
3515 }
3516 }
3517 return $multiSummaryFields;
3518 }
3519
3520 /**
3521 * This is function is used to format pseudo fields.
3522 *
3523 * @param array $fields
3524 * Associated array of profile fields.
3525 *
3526 */
3527 public static function reformatProfileFields(&$fields) {
3528 //reformat fields array
3529 foreach ($fields as $name => $field) {
3530 //reformat phone and extension field
3531 if (substr($field['name'], 0, 13) == 'phone_and_ext') {
3532 $fieldSuffix = str_replace('phone_and_ext-', '', $field['name']);
3533
3534 // retain existing element properties and just update and replace key
3535 CRM_Utils_Array::crmReplaceKey($fields, $name, "phone-{$fieldSuffix}");
3536 $fields["phone-{$fieldSuffix}"]['name'] = "phone-{$fieldSuffix}";
3537 $fields["phone-{$fieldSuffix}"]['where'] = 'civicrm_phone.phone';
3538
3539 // add additional phone extension field
3540 $fields["phone_ext-{$fieldSuffix}"] = $field;
3541 $fields["phone_ext-{$fieldSuffix}"]['title'] = $field['title'] . ' - ' . ts('Ext.');
3542 $fields["phone_ext-{$fieldSuffix}"]['name'] = "phone_ext-{$fieldSuffix}";
3543 $fields["phone_ext-{$fieldSuffix}"]['where'] = 'civicrm_phone.phone_ext';
3544 $fields["phone_ext-{$fieldSuffix}"]['skipDisplay'] = 1;
3545 //ignore required for extension field
3546 $fields["phone_ext-{$fieldSuffix}"]['is_required'] = 0;
3547 }
3548 }
3549 }
3550
3551 /**
3552 * Get the frontend_title for the profile, falling back on 'title' if none.
3553 *
3554 * @param int $profileID
3555 *
3556 * @return string
3557 *
3558 * @throws \CiviCRM_API3_Exception
3559 */
3560 public static function getFrontEndTitle(int $profileID) {
3561 $profile = civicrm_api3('UFGroup', 'getsingle', ['id' => $profileID, 'return' => ['title', 'frontend_title']]);
3562 return $profile['frontend_title'] ?? $profile['title'];
3563 }
3564
3565 /**
3566 * Format custom field value for use in prepopulating a quickform profile field.
3567 *
3568 * @param array $field
3569 * Field metadata.
3570 * @param string $value
3571 * Raw value
3572 *
3573 * @return mixed
3574 * String or array, depending on the html type
3575 */
3576 private static function formatCustomValue($field, $value) {
3577 if (CRM_Core_BAO_CustomField::isSerialized($field)) {
3578 $value = CRM_Utils_Array::explodePadded($value);
3579
3580 // This may not be required now.
3581 if ($field['html_type'] === 'CheckBox') {
3582 $checkboxes = [];
3583 foreach (array_filter($value) as $item) {
3584 $checkboxes[$item] = 1;
3585 // CRM-2969 seems like we need this for QF style checkboxes in profile where its multiindexed
3586 $checkboxes["[{$item}]"] = 1;
3587 }
3588 return $checkboxes;
3589 }
3590 }
3591 return $value;
3592 }
3593
3594 }