Merge pull request #18712 from eileenmcnaughton/locev3
[civicrm-core.git] / CRM / Core / BAO / UFGroup.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18/**
8eedd10a 19 * UF group BAO class.
6a488035
TO
20 */
21class CRM_Core_BAO_UFGroup extends CRM_Core_DAO_UFGroup {
a6a0cdb7 22
7da04cde 23 const PUBLIC_VISIBILITY = 1,
6a488035
TO
24 ADMIN_VISIBILITY = 2,
25 LISTINGS_VISIBILITY = 4;
26
27 /**
fe482240 28 * Cache the match clause used in this transaction.
6a488035
TO
29 *
30 * @var string
31 */
518fa0ee 32 public static $_matchFields = NULL;
6a488035
TO
33
34 /**
fe482240 35 * Fetch object based on array of properties.
6a488035 36 *
6a0b768e
TO
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.
6a488035 41 *
a6c01b45
CW
42 * @return object
43 * CRM_Core_DAO_UFGroup object
6a488035 44 */
00be9182 45 public static function retrieve(&$params, &$defaults) {
6a488035
TO
46 return CRM_Core_DAO::commonRetrieve('CRM_Core_DAO_UFGroup', $params, $defaults);
47 }
48
49 /**
50 * Retrieve the first non-generic contact type
51 *
6a0b768e
TO
52 * @param int $id
53 * Id of uf_group.
6a488035 54 *
a6c01b45
CW
55 * @return string
56 * contact type
6a488035 57 */
00be9182 58 public static function getContactType($id) {
6a488035
TO
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)) {
9c1bc317 72 $cType = $validSubTypes[$type]['parent'] ?? NULL;
6a488035 73 }
2aa397bc 74 if ($cType) {
5d6ac993 75 break;
2aa397bc 76 }
6a488035
TO
77 }
78
79 return $cType;
80 }
81
82 /**
83 * Get the form title.
84 *
6a0b768e
TO
85 * @param int $id
86 * Id of uf_form.
6a488035 87 *
a6c01b45
CW
88 * @return string
89 * title
6a488035 90 *
6a488035
TO
91 */
92 public static function getTitle($id) {
93 return CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $id, 'title');
94 }
95
96 /**
fe482240 97 * Update the is_active flag in the db.
6a488035 98 *
6a0b768e
TO
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.
6a488035 103 *
8a4fede3 104 * @return bool
105 * true if we found and updated the object, else false
6a488035 106 */
00be9182 107 public static function setIsActive($id, $is_active) {
6a488035
TO
108 return CRM_Core_DAO::setFieldValue('CRM_Core_DAO_UFGroup', $id, 'is_active', $is_active);
109 }
110
111 /**
fe482240 112 * Get all the registration fields.
6a488035 113 *
6a0b768e
TO
114 * @param int $action
115 * What action are we doing.
116 * @param int $mode
117 * Mode.
da6b46f4 118 *
a6a0cdb7 119 * @param string $ctype
6a488035 120 *
a6c01b45
CW
121 * @return array
122 * the fields that are needed for registration
a6a0cdb7 123 *
124 * @throws \Exception
6a488035 125 */
00be9182 126 public static function getRegistrationFields($action, $mode, $ctype = NULL) {
6a488035
TO
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
a6a0cdb7 138 $fields = [];
6a488035
TO
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) {
a7488080 160 if (empty($fields[$name])) {
6a488035
TO
161 $fields[$name] = $field;
162 }
163 }
164 }
165
166 return $fields;
167 }
168
169 /**
fe482240 170 * Get all the listing fields.
6a488035 171 *
6a0b768e
TO
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.
da6b46f4 178 * @param array $ufGroupIds
6a0b768e 179 * @param bool $searchable
6a488035 180 *
da6b46f4
EM
181 * @param null $restrict
182 * @param bool $skipPermission
183 * @param int $permissionType
a6a0cdb7 184 *
a6c01b45
CW
185 * @return array
186 * the fields that are listings related
a6a0cdb7 187 *
188 * @throws \Exception
6a488035 189 */
e7483cbe 190 public static function getListingFields(
6a488035
TO
191 $action,
192 $visibility,
193 $considerSelector = FALSE,
5d6ac993
TO
194 $ufGroupIds = NULL,
195 $searchable = NULL,
196 $restrict = NULL,
197 $skipPermission = FALSE,
198 $permissionType = CRM_Core_Permission::SEARCH
6a488035
TO
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 {
ff4f7744 219 $ufGroups = CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id');
6a488035 220
a6a0cdb7 221 $fields = [];
6a488035
TO
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) {
4f99ca55
TO
233 if (!$field['in_selector']) {
234 unset($subset[$name]);
2aa397bc 235 }
6a488035
TO
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 *
b8bb9d06 249 * @param int $id
6a0b768e 250 * The id of the UF group or ids of ufgroup.
fd31fa4c 251 * @param bool|int $register are we interested in registration fields
6a0b768e
TO
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
fd31fa4c 257 * @param bool $showAll
6a0b768e
TO
258 * @param string $restrict
259 * Should we restrict based on a specified profile type.
fd31fa4c
EM
260 * @param bool $skipPermission
261 * @param null $ctype
262 * @param int $permissionType
263 * @param string $orderBy
264 * @param null $orderProfiles
265 *
54957108 266 * @param bool $eventProfile
267 *
a6c01b45 268 * @return array
54957108 269 * The fields that belong to this ufgroup(s)
301906ab 270 *
271 * @throws \CRM_Core_Exception
6a488035 272 */
e7483cbe 273 public static function getFields(
6a488035
TO
274 $id,
275 $register = FALSE,
276 $action = NULL,
5d6ac993 277 $visibility = NULL,
6a488035 278 $searchable = NULL,
5d6ac993 279 $showAll = FALSE,
6a488035
TO
280 $restrict = NULL,
281 $skipPermission = FALSE,
5d6ac993 282 $ctype = NULL,
6a488035 283 $permissionType = CRM_Core_Permission::CREATE,
5d6ac993 284 $orderBy = 'field_name',
44792363 285 $orderProfiles = NULL,
286 $eventProfile = FALSE
6a488035
TO
287 ) {
288 if (!is_array($id)) {
289 $id = CRM_Utils_Type::escape($id, 'Positive');
a6a0cdb7 290 $profileIds = [$id];
6a488035
TO
291 }
292 else {
293 $profileIds = $id;
294 }
295
296 $gids = implode(',', $profileIds);
a6a0cdb7 297 $params = [];
6a488035 298 if ($restrict) {
f9d8a5d1 299 $query = "SELECT g.* from civicrm_uf_group g
300 LEFT JOIN civicrm_uf_join j ON (j.uf_group_id = g.id)
6a488035 301 WHERE g.id IN ( {$gids} )
f9d8a5d1 302 AND ((j.uf_group_id IN ( {$gids} ) AND j.module = %1) OR g.is_reserved = 1 )
6a488035 303 ";
a6a0cdb7 304 $params = [1 => [$restrict, 'String']];
6a488035
TO
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
a6a0cdb7 314 $checkPermission = [
315 [
44792363 316 'administer CiviCRM',
317 'manage event profiles',
a6a0cdb7 318 ],
319 ];
44792363 320 if ($eventProfile && CRM_Core_Permission::check($checkPermission)) {
321 $skipPermission = TRUE;
322 }
323
6a488035
TO
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
518fa0ee 330 if ($orderProfiles and count($profileIds) > 1) {
6a488035
TO
331 $query .= " ORDER BY FIELD( g.id, {$gids} )";
332 }
5d6ac993 333 $group = CRM_Core_DAO::executeQuery($query, $params);
a6a0cdb7 334 $fields = [];
6a488035
TO
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
fc040dd2 342 $importableFields = self::getProfileFieldMetadata($showAll);
6a488035
TO
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 }
6a488035
TO
351 }
352
353 if (empty($fields) && !$validGroup) {
301906ab 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.',
a6a0cdb7 355 [1 => implode(',', $profileIds)]
5d6ac993 356 ));
6a488035
TO
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 *
6a0b768e
TO
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.
6a488035 375 * @param bool $searchable
77b97be7
EM
376 * @param bool $showAll
377 * @param null $ctype
378 * @param int $permissionType
379 *
6a488035 380 * @return array
c490a46a 381 * @see self::getFields
6a488035
TO
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);
5d6ac993
TO
400 $profileType = CRM_Core_BAO_UFField::calculateProfileType(implode(',', $ufGroupType));
401 $contactActivityProfile = CRM_Core_BAO_UFField::checkContactActivityProfileTypeByGroupType(implode(',', $ufGroupType));
6a488035
TO
402 $importableFields = self::getImportableFields($showAll, $profileType, $contactActivityProfile);
403 list($customFields, $addressCustomFields) = self::getCustomFields($ctype);
404
a6a0cdb7 405 $formattedFields = [];
6a488035 406 foreach ($fieldArrs as $fieldArr) {
6a488035
TO
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
e7483cbe 425 * @param array $customFields
6a488035
TO
426 * @param array $addressCustomFields
427 * @param array $importableFields
6a0b768e
TO
428 * @param int $permissionType
429 * Eg CRM_Core_Permission::CREATE.
a6a0cdb7 430 *
6a488035
TO
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;
a6a0cdb7 445 if (in_array($permissionType, [CRM_Core_Permission::CREATE, CRM_Core_Permission::EDIT]) &&
6a488035
TO
446 in_array($field->field_name, array_keys($addressCustomFields))
447 ) {
448 $addressCustom = TRUE;
449 $name = "address_{$name}";
450 }
887e764d
PN
451 if ($field->field_name == 'url') {
452 $name .= "-{$field->website_type_id}";
453 }
454 elseif (!empty($field->location_type_id)) {
6a488035
TO
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 }
2e1f50d6 467 $fieldMetaData = CRM_Utils_Array::value($name, $importableFields, ($importableFields[$field->field_name] ?? []));
6a488035
TO
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
a6a0cdb7 471 $formattedField = [
6a488035
TO
472 'name' => $name,
473 'groupTitle' => $group->title,
5d6ac993 474 'groupName' => $group->name,
ce1a9db4 475 'groupDisplayTitle' => (!empty($group->frontend_title)) ? $group->frontend_title : $group->title,
6a488035
TO
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)),
2e1f50d6
CW
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,
6a488035 491 'group_id' => $group->id,
2e1f50d6
CW
492 'add_to_group_id' => $group->add_to_group_id ?? NULL,
493 'add_captcha' => $group->add_captcha ?? NULL,
6a488035
TO
494 'field_type' => $field->field_type,
495 'field_id' => $field->id,
0c145cc0
DL
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 ),
6a488035 505 'skipDisplay' => 0,
d86e674f 506 'data_type' => CRM_Utils_Type::getDataTypeFromFieldMetadata($fieldMetaData),
6b409353 507 'bao' => $fieldMetaData['bao'] ?? NULL,
a6a0cdb7 508 ];
6a488035 509
d86e674f 510 $formattedField = CRM_Utils_Date::addDateMetadataToField($fieldMetaData, $formattedField);
511
6a488035
TO
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)) {
e1d699d7 519 $formattedField['serialize'] = !empty($customFields[$field->field_name]['serialize']);
6a488035
TO
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'];
6a488035
TO
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'];
d86e674f 528 $formattedField['is_datetime_field'] = TRUE;
2732685b 529 $formattedField['smarty_view_format'] = CRM_Utils_Date::getDateFieldViewFormat($formattedField['date_format']);
6a488035
TO
530 }
531
532 $formattedField['is_multi_summary'] = $field->is_multi_summary;
a6a0cdb7 533 return [$name, $formattedField];
6a488035
TO
534 }
535 else {
536 $formattedField = NULL;
a6a0cdb7 537 return [$name, $formattedField];
6a488035
TO
538 }
539 }
a6a0cdb7 540 return [$name, $formattedField];
6a488035
TO
541 }
542
543 /**
fe482240 544 * Create a query to find all visible UFFields in a UFGroup.
6a488035
TO
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
6a0b768e
TO
552 * @param string $orderBy
553 * Comma-delimited list of SQL columns.
a6a0cdb7 554 *
6a488035
TO
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) {
a6a0cdb7 569 $clause = [];
6a488035
TO
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 /**
fe482240 593 * Create a query to find all visible UFFields in a UFGroup.
6a488035
TO
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
a6a0cdb7 601 *
a6c01b45
CW
602 * @return bool
603 * TRUE if field is displayable
6a488035
TO
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) {
a6a0cdb7 615 $allowedVisibilities = [];
6a488035
TO
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
b5c2afd0 634 /**
fc040dd2 635 * Get a list of filtered field metadata.
636 *
b5c2afd0
EM
637 * @param $showAll
638 * @param $profileType
639 * @param $contactActivityProfile
fc040dd2 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.
b5c2afd0
EM
647 *
648 * @return array
a6a0cdb7 649 * @deprecated use getProfileFieldMetadata
650 *
b5c2afd0 651 */
fc040dd2 652 protected static function getImportableFields($showAll, $profileType, $contactActivityProfile, $filterMode = TRUE) {
6a488035
TO
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
fc040dd2 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 }
6a488035
TO
669 }
670 else {
fc040dd2 671 $importableFields = array_merge($importableFields, $activityFields, $componentFields);
6a488035
TO
672 }
673
6a488035
TO
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
fc040dd2 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
bc854509 694 /**
695 * Get the fields relating to locations.
696 *
697 * @return array
698 */
6a488035 699 public static function getLocationFields() {
a6a0cdb7 700 static $locationFields = [
6a488035
TO
701 'street_address',
702 'supplemental_address_1',
703 'supplemental_address_2',
207f62c6 704 'supplemental_address_3',
6a488035
TO
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',
a6a0cdb7 719 ];
6a488035
TO
720 return $locationFields;
721 }
722
b5c2afd0
EM
723 /**
724 * @param $ctype
725 *
726 * @return mixed
727 */
6a488035 728 protected static function getCustomFields($ctype) {
58159c3b 729 $cacheKey = 'uf_group_custom_fields_' . $ctype;
730 if (!Civi::cache('metadata')->has($cacheKey)) {
6a488035
TO
731 $customFields = CRM_Core_BAO_CustomField::getFieldsForImport($ctype, FALSE, FALSE, FALSE, TRUE, TRUE);
732
733 // hack to add custom data for components
a6a0cdb7 734 $components = ['Contribution', 'Participant', 'Membership', 'Activity', 'Case'];
6a488035
TO
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);
58159c3b 740 Civi::cache('metadata')->set($cacheKey, [$customFields, $addressCustomFields]);
6a488035 741 }
58159c3b 742 return Civi::cache('metadata')->get($cacheKey);
6a488035
TO
743 }
744
745 /**
fe482240 746 * Check the data validity.
6a488035 747 *
6a0b768e
TO
748 * @param int $userID
749 * The user id that we are actually editing.
b247a732 750 * @param string $name
751 * The machine-name of the group we are interested in.
2a6da8d7 752 * @param bool $register
6a0b768e
TO
753 * @param int $action
754 * The action of the form.
6a488035 755 *
2a6da8d7 756 * @pram boolean $register is this the registrtion form
e7483cbe 757 * @return bool
a6c01b45 758 * true if form is valid
6a488035 759 */
b247a732 760 public static function isValid($userID, $name, $register = FALSE, $action = NULL) {
6a488035
TO
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
b247a732 775 $group->name = $name;
6a488035
TO
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 /**
fe482240 790 * Get the html for the form that represents this particular group.
6a488035 791 *
6a0b768e
TO
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?.
2a6da8d7
EM
804 *
805 * @param bool $doNotProcess
806 * @param null $ctype
6a488035 807 *
a6c01b45
CW
808 * @return string
809 * the html for the form on success, otherwise empty string
6a488035 810 */
e7483cbe 811 public static function getEditHTML(
5d6ac993 812 $userID,
6a488035 813 $title,
5d6ac993
TO
814 $action = NULL,
815 $register = FALSE,
816 $reset = FALSE,
817 $profileID = NULL,
6a488035 818 $doNotProcess = FALSE,
5d6ac993 819 $ctype = NULL
6a488035
TO
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.
91768280 850 $controller->setSkipRedirection(!$doNotProcess);
6a488035
TO
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.
91768280 911 $controller->setSkipRedirection(!$doNotProcess);
6a488035
TO
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();
9c1bc317 936 $email = $_POST['mail'] ?? NULL;
6a488035
TO
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
6a488035 947 /**
f3f00653 948 * Given a contact id and a field set, return the values from the db.
6a488035 949 *
100fef9d 950 * @param int $cid
6a0b768e
TO
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).
2a6da8d7 961 * @param null $additionalWhereClause
f3f00653 962 *
963 * @return null|array
6a488035 964 */
5d6ac993
TO
965 public static function getValues(
966 $cid, &$fields, &$values,
6a488035
TO
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);
a6a0cdb7 976 $params = $cid ? [['contact_id', '=', $cid, 0, 0]] : [];
6a488035
TO
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);
6a488035 984
5d6ac993 985 $details = $query->searchQuery(0, 0, NULL, FALSE, FALSE,
6a488035 986 FALSE, FALSE, FALSE, $additionalWhereClause);
6511fc14 987 while ($details->fetch()) {
c4deda45
BS
988 if (!$details) {
989 return;
990 }
6a488035 991 }
d9ab802d 992 $query->convertToPseudoNames($details);
6a488035 993
4961a325
JP
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');
6a488035 997
a6a0cdb7 998 $multipleFields = ['url'];
6a488035
TO
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
a7488080 1008 if (!empty($field['skipDisplay'])) {
6a488035
TO
1009 continue;
1010 }
1011
50bf705c 1012 // Create a unique, non-empty index for each field.
6a488035 1013 $index = $field['title'];
f9f40af3
TO
1014 if ($index === '') {
1015 $index = ' ';
2aa397bc 1016 }
5d6ac993 1017 while (array_key_exists($index, $values)) {
50bf705c 1018 $index .= ' ';
5d6ac993 1019 }
6a488035 1020
6a488035
TO
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
a6a0cdb7 1026 if (in_array(substr($name, 0, -3), ['gender', 'prefix', 'suffix'])) {
04ffef8d 1027 $params[$index] = $details->$name;
46796b19 1028 $values[$index] = $details->$name;
6a488035
TO
1029 }
1030 elseif (in_array($name, CRM_Contact_BAO_Contact::$_greetingTypes)) {
5d6ac993 1031 $dname = $name . '_display';
6a488035 1032 $values[$index] = $details->$dname;
5d6ac993 1033 $name = $name . '_id';
6a488035
TO
1034 $params[$index] = $details->$name;
1035 }
a6a0cdb7 1036 elseif (in_array($name, [
5d6ac993
TO
1037 'state_province',
1038 'country',
21dfd5f5 1039 'county',
a6a0cdb7 1040 ])) {
6a488035 1041 $values[$index] = $details->$name;
5d6ac993 1042 $idx = $name . '_id';
6a488035
TO
1043 $params[$index] = $details->$idx;
1044 }
6a488035 1045 elseif ($name === 'preferred_language') {
6a488035 1046 $params[$index] = $details->$name;
a8c23526 1047 $values[$index] = CRM_Core_PseudoConstant::getLabel('CRM_Contact_DAO_Contact', 'preferred_language', $details->$name);
6a488035
TO
1048 }
1049 elseif ($name == 'group') {
1050 $groups = CRM_Contact_BAO_GroupContact::getContactGroup($cid, 'Added', NULL, FALSE, TRUE);
a6a0cdb7 1051 $title = $ids = [];
6a488035
TO
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
69953588 1056 $groupPerm = CRM_Contact_BAO_Group::checkPermission($g['group_id'], TRUE);
6a488035
TO
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);
a6a0cdb7 1072 $allTags = CRM_Core_PseudoConstant::get('CRM_Core_DAO_EntityTag', 'tag_id', ['onlyActive' => FALSE]);
1073 $title = [];
6a488035
TO
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)) {
a6a0cdb7 1092 $contactSubTypeLabels = [];
6a488035
TO
1093 // get all contact subtypes
1094 $allContactSubTypes = CRM_Contact_BAO_ContactType::subTypeInfo();
1095 // build contact subtype labels array
5d6ac993 1096 foreach ($contactSubTypeNames as $cstName) {
6a488035
TO
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
9c1bc317 1119 $fieldType = $field['field_type'] ?? NULL;
6a488035
TO
1120
1121 if ($htmlType == 'File') {
1122 $entityId = $cid;
1123 if (!$cid &&
5d6ac993
TO
1124 $fieldType == 'Activity' && !empty($componentWhere[0][2])
1125 ) {
6a488035
TO
1126 $entityId = $componentWhere[0][2];
1127 }
1128
1129 $fileURL = CRM_Core_BAO_CustomField::getFileURL($entityId,
1130 $cfID,
1131 NULL,
b42d84aa 1132 $absolute,
1133 $additionalWhereClause
6a488035
TO
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 ) {
5d6ac993 1144 $customVal = (int ) ($details->{$name});
6a488035
TO
1145 }
1146 elseif (isset($dao) && property_exists($dao, 'data_type')
1147 && $dao->data_type == 'Float'
1148 ) {
5d6ac993 1149 $customVal = (float ) ($details->{$name});
6a488035
TO
1150 }
1151 elseif (!CRM_Utils_System::isNull(explode(CRM_Core_DAO::VALUE_SEPARATOR,
5d6ac993
TO
1152 $details->{$name}
1153 ))
1154 ) {
6a488035
TO
1155 $customVal = $details->{$name};
1156 }
1157
1158 //CRM-4582
1159 if (CRM_Utils_System::isNull($customVal)) {
1160 continue;
1161 }
1162
1163 $params[$index] = $customVal;
8cee0c70 1164 $values[$index] = CRM_Core_BAO_CustomField::displayValue($customVal, $cfID);
1b4d9e39 1165 if ($field['data_type'] == 'ContactReference') {
6a488035
TO
1166 $params[$index] = $values[$index];
1167 }
1168 if (CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField',
5d6ac993
TO
1169 $cfID, 'is_search_range'
1170 )
1171 ) {
6a488035
TO
1172 $customFieldName = "{$name}_from";
1173 }
1174 }
1175 }
1176 elseif ($name == 'image_URL') {
77d45291 1177 list($width, $height) = getimagesize(CRM_Utils_String::unstupifyUrl($details->$name));
6a488035
TO
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 }
a6a0cdb7 1183 elseif (in_array($name, [
5d6ac993
TO
1184 'birth_date',
1185 'deceased_date',
a6a0cdb7 1186 ])) {
363544d7 1187 // @todo this set should be determined from metadata, not hard-coded.
6a488035
TO
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 {
9c1bc317 1222 $locationTypeName = $locationTypes[$id] ?? NULL;
6a488035
TO
1223 }
1224
1225 if (!$locationTypeName) {
1226 continue;
1227 }
1228
1229 $detailName = "{$locationTypeName}-{$fieldName}";
1230 $detailName = str_replace(' ', '_', $detailName);
1231
a6a0cdb7 1232 if (in_array($fieldName, [
5d6ac993
TO
1233 'phone',
1234 'im',
1235 'email',
21dfd5f5 1236 'openid',
a6a0cdb7 1237 ])) {
6a488035
TO
1238 if ($type) {
1239 $detailName .= "-{$type}";
1240 }
1241 }
1242
a6a0cdb7 1243 if (in_array($fieldName, [
5d6ac993
TO
1244 'state_province',
1245 'country',
21dfd5f5 1246 'county',
a6a0cdb7 1247 ])) {
6a488035 1248 $values[$index] = $details->$detailName;
5d6ac993 1249 $idx = $detailName . '_id';
6a488035
TO
1250 $params[$index] = $details->$idx;
1251 }
1252 elseif ($fieldName == 'im') {
1253 $providerId = $detailName . '-provider_id';
38913d36
TO
1254 if (isset($imProviders[$details->$providerId])) {
1255 $values[$index] = $details->$detailName . " (" . $imProviders[$details->$providerId] . ")";
6a488035
TO
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) {
5d6ac993
TO
1280 $websiteTypeId = "website-{$id}-website_type_id";
1281 $websiteType = $websiteTypes[$details->$websiteTypeId];
6a488035
TO
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 }
9574b6bc 1313 elseif (!empty($field['serialize'])) {
f0fed404
JF
1314 $valSeparator = CRM_Core_DAO::VALUE_SEPARATOR;
1315 $selectedOptions = explode($valSeparator, $params[$index]);
6a488035
TO
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)) {
a6a0cdb7 1352 $links = [];
6a488035
TO
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 *
6a0b768e
TO
1370 * @param int $id
1371 * Profile Id.
6a488035 1372 *
e7483cbe 1373 * @return bool
6a488035 1374 *
6a488035
TO
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 *
6a0b768e
TO
1395 * @param int $id
1396 * Profile Id.
6a488035 1397 *
e7483cbe 1398 * @return bool
6a488035 1399 *
6a488035
TO
1400 */
1401 public static function del($id) {
1402 //check whether this group contains any profile fields
1403 $profileField = new CRM_Core_DAO_UFField();
1404 $profileField->uf_group_id = $id;
1405 $profileField->find();
1406 while ($profileField->fetch()) {
1407 CRM_Core_BAO_UFField::del($profileField->id);
1408 }
1409
1410 //delete records from uf join table
1411 $ufJoin = new CRM_Core_DAO_UFJoin();
1412 $ufJoin->uf_group_id = $id;
1413 $ufJoin->delete();
1414
1415 //delete profile group
1416 $group = new CRM_Core_DAO_UFGroup();
1417 $group->id = $id;
1418 $group->delete();
1419 return 1;
1420 }
1421
1422 /**
fe482240 1423 * Add the UF Group.
6a488035 1424 *
6a0b768e
TO
1425 * @param array $params
1426 * Reference array contains the values submitted by the form.
1427 * @param array $ids
1428 * Reference array contains the id.
6a488035 1429 *
6a488035
TO
1430 *
1431 * @return object
1432 */
a6a0cdb7 1433 public static function add(&$params, $ids = []) {
1434 $fields = [
5d6ac993
TO
1435 'is_active',
1436 'add_captcha',
1437 'is_map',
1438 'is_update_dupe',
1439 'is_edit_link',
1440 'is_uf_link',
21dfd5f5 1441 'is_cms_user',
a6a0cdb7 1442 ];
6a488035
TO
1443 foreach ($fields as $field) {
1444 $params[$field] = CRM_Utils_Array::value($field, $params, FALSE);
1445 }
1446
9c1bc317
CW
1447 $params['limit_listings_group_id'] = $params['group'] ?? NULL;
1448 $params['add_to_group_id'] = $params['add_contact_to_group'] ?? NULL;
6a488035 1449
f80ef0e2 1450 //CRM-15427
1451 if (!empty($params['group_type']) && is_array($params['group_type'])) {
1452 $params['group_type'] = implode(',', $params['group_type']);
1453 }
6a488035
TO
1454 $ufGroup = new CRM_Core_DAO_UFGroup();
1455 $ufGroup->copyValues($params);
1456
6a73ef3f 1457 $ufGroupID = CRM_Utils_Array::value('ufgroup', $ids, CRM_Utils_Array::value('id', $params));
25973039 1458 if (!$ufGroupID && empty($params['name'])) {
6a488035
TO
1459 $ufGroup->name = CRM_Utils_String::munge($ufGroup->title, '_', 56);
1460 }
1461 $ufGroup->id = $ufGroupID;
1462
1463 $ufGroup->save();
1464
25973039 1465 if (!$ufGroupID && empty($params['name'])) {
6a488035
TO
1466 $ufGroup->name = $ufGroup->name . "_{$ufGroup->id}";
1467 $ufGroup->save();
1468 }
1469
1470 return $ufGroup;
1471 }
1472
1473 /**
fe482240 1474 * Make uf join entries for an uf group.
6a488035 1475 *
259ab031 1476 * @param int $weight
1477 * @param array $groupTypes
1478 * An assoc array of name/value pairs.
6a0b768e
TO
1479 * @param int $ufGroupId
1480 * Ufgroup id.
6a488035 1481 */
259ab031 1482 public static function createUFJoin($weight, $groupTypes, $ufGroupId) {
6a488035
TO
1483
1484 // get ufjoin records for uf group
1485 $ufGroupRecord = CRM_Core_BAO_UFGroup::getUFJoinRecord($ufGroupId);
1486
1487 // get the list of all ufgroup types
1488 $allUFGroupType = CRM_Core_SelectValues::ufGroupTypes();
1489
1490 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
1491 if (!is_array($groupTypes)) {
a6a0cdb7 1492 $groupTypes = [];
6a488035
TO
1493 }
1494
1495 // this fix is done to prevent warning generated by array_key_exits incase of empty array is given as input
1496 if (!is_array($ufGroupRecord)) {
a6a0cdb7 1497 $ufGroupRecord = [];
6a488035
TO
1498 }
1499
1500 // check which values has to be inserted/deleted for contact
1501 $menuRebuild = FALSE;
1502 foreach ($allUFGroupType as $key => $value) {
a6a0cdb7 1503 $joinParams = [];
6a488035
TO
1504 $joinParams['uf_group_id'] = $ufGroupId;
1505 $joinParams['module'] = $key;
259ab031 1506 if ($key === 'User Account') {
6a488035
TO
1507 $menuRebuild = TRUE;
1508 }
1509 if (array_key_exists($key, $groupTypes) && !in_array($key, $ufGroupRecord)) {
1510 // insert a new record
1511 CRM_Core_BAO_UFGroup::addUFJoin($joinParams);
1512 }
1513 elseif (!array_key_exists($key, $groupTypes) && in_array($key, $ufGroupRecord)) {
1514 // delete a record for existing ufgroup
1515 CRM_Core_BAO_UFGroup::delUFJoin($joinParams);
1516 }
1517 }
1518
1519 //update the weight
259ab031 1520 $query = '
6a488035
TO
1521UPDATE civicrm_uf_join
1522SET weight = %1
1523WHERE uf_group_id = %2
1524AND ( entity_id IS NULL OR entity_id <= 0 )
259ab031 1525';
a6a0cdb7 1526 $p = [
259ab031 1527 1 => [$weight, 'Integer'],
a6a0cdb7 1528 2 => [$ufGroupId, 'Integer'],
1529 ];
6a488035
TO
1530 CRM_Core_DAO::executeQuery($query, $p);
1531
4d16a7e1
DS
1532 // Do a menu rebuild, so it gets all the new menu entries for user account
1533 if ($menuRebuild) {
1534 $config = CRM_Core_Config::singleton();
1535 $config->userSystem->updateCategories();
6a488035
TO
1536 }
1537 }
1538
1539 /**
fe482240 1540 * Get the UF Join records for an ufgroup id.
6a488035 1541 *
6a0b768e
TO
1542 * @param int $ufGroupId
1543 * Uf group id.
1544 * @param int $displayName
1545 * If set return display name in array.
1546 * @param int $status
1547 * If set return module other than default modules (User Account/User registration/Profile).
77b97be7 1548 *
a6c01b45 1549 * @return array
6a488035 1550 *
6a488035
TO
1551 */
1552 public static function getUFJoinRecord($ufGroupId = NULL, $displayName = NULL, $status = NULL) {
1553 if ($displayName) {
a6a0cdb7 1554 $UFGroupType = [];
6a488035
TO
1555 $UFGroupType = CRM_Core_SelectValues::ufGroupTypes();
1556 }
1557
a6a0cdb7 1558 $ufJoin = [];
6a488035
TO
1559 $dao = new CRM_Core_DAO_UFJoin();
1560
1561 if ($ufGroupId) {
1562 $dao->uf_group_id = $ufGroupId;
1563 }
1564
1565 $dao->find();
a6a0cdb7 1566 $ufJoin = [];
6a488035
TO
1567
1568 while ($dao->fetch()) {
1569 if (!$displayName) {
1570 $ufJoin[$dao->id] = $dao->module;
1571 }
1572 else {
1573 if (isset($UFGroupType[$dao->module])) {
1574 // skip the default modules
1575 if (!$status) {
1576 $ufJoin[$dao->id] = $UFGroupType[$dao->module];
1577 }
1578 // added for CRM-1475
1579 }
1580 elseif (!CRM_Utils_Array::key($dao->module, $ufJoin)) {
1581 $ufJoin[$dao->id] = $dao->module;
1582 }
1583 }
1584 }
1585 return $ufJoin;
1586 }
1587
1588 /**
fe482240 1589 * Function takes an associative array and creates a ufjoin record for ufgroup.
6a488035 1590 *
6a0b768e
TO
1591 * @param array $params
1592 * (reference) an assoc array of name/value pairs.
6a488035 1593 *
16b10e64 1594 * @return CRM_Core_BAO_UFJoin
6a488035 1595 */
00be9182 1596 public static function addUFJoin(&$params) {
6a488035
TO
1597 $ufJoin = new CRM_Core_DAO_UFJoin();
1598 $ufJoin->copyValues($params);
1599 $ufJoin->save();
1600 return $ufJoin;
1601 }
1602
1603 /**
fe482240 1604 * Delete the uf join record for an uf group.
6a488035 1605 *
6a0b768e
TO
1606 * @param array $params
1607 * (reference) an assoc array of name/value pairs.
6a488035 1608 */
00be9182 1609 public static function delUFJoin(&$params) {
6a488035
TO
1610 $ufJoin = new CRM_Core_DAO_UFJoin();
1611 $ufJoin->copyValues($params);
1612 $ufJoin->delete();
1613 }
1614
1615 /**
fe482240 1616 * Get the weight for ufjoin record.
6a488035 1617 *
6a0b768e
TO
1618 * @param int $ufGroupId
1619 * If $ufGroupId get update weight or add weight.
6a488035 1620 *
a6c01b45
CW
1621 * @return int
1622 * weight of the UFGroup
6a488035 1623 */
00be9182 1624 public static function getWeight($ufGroupId = NULL) {
6a488035 1625 //calculate the weight
a6a0cdb7 1626 $p = [];
6a488035
TO
1627 if (!$ufGroupId) {
1628 $queryString = "SELECT ( MAX(civicrm_uf_join.weight)+1) as new_weight
1629 FROM civicrm_uf_join
1630 WHERE module = 'User Registration' OR module = 'User Account' OR module = 'Profile'";
1631 }
1632 else {
1633 $queryString = "SELECT MAX(civicrm_uf_join.weight) as new_weight
1634 FROM civicrm_uf_join
1635 WHERE civicrm_uf_join.uf_group_id = %1
1636 AND ( entity_id IS NULL OR entity_id <= 0 )";
a6a0cdb7 1637 $p[1] = [$ufGroupId, 'Integer'];
6a488035
TO
1638 }
1639
1640 $dao = CRM_Core_DAO::executeQuery($queryString, $p);
1641 $dao->fetch();
1642 return ($dao->new_weight) ? $dao->new_weight : 1;
1643 }
1644
1645 /**
fe482240 1646 * Get the uf group for a module.
6a488035 1647 *
6a0b768e
TO
1648 * @param string $moduleName
1649 * Module name.
1650 * @param int $count
1651 * No to increment the weight.
77b97be7 1652 * @param bool $skipPermission
6a0b768e
TO
1653 * @param int $op
1654 * Which operation (view, edit, create, etc) to check permission for.
2141efbf 1655 * @param array|NULL $returnFields list of UFGroup fields to return; NULL for default
6a488035 1656 *
a6c01b45
CW
1657 * @return array
1658 * array of ufgroups for a module
6a488035 1659 */
2141efbf 1660 public static function getModuleUFGroup($moduleName = NULL, $count = 0, $skipPermission = TRUE, $op = CRM_Core_Permission::VIEW, $returnFields = NULL) {
c4baa1da 1661 $selectFields = ['id', 'title', 'created_id', 'is_active', 'is_reserved', 'group_type', 'description'];
290d4ccc 1662
eed7e803 1663 if (CRM_Core_BAO_SchemaHandler::checkIfFieldExists('civicrm_uf_group', 'frontend_title')) {
ce1a9db4 1664 $selectFields[] = 'frontend_title';
290d4ccc 1665 }
7ba38389 1666
1667 if (!empty($returnFields)) {
1668 $selectFields = array_merge($returnFields, array_diff($selectFields, $returnFields));
2141efbf 1669 }
7ba38389 1670
1671 $queryString = 'SELECT civicrm_uf_group.' . implode(', civicrm_uf_group.', $selectFields) . '
6a488035
TO
1672 FROM civicrm_uf_group
1673 LEFT JOIN civicrm_uf_join ON (civicrm_uf_group.id = uf_group_id)';
a6a0cdb7 1674 $p = [];
6a488035
TO
1675 if ($moduleName) {
1676 $queryString .= ' AND civicrm_uf_group.is_active = 1
1677 WHERE civicrm_uf_join.module = %2';
a6a0cdb7 1678 $p[2] = [$moduleName, 'String'];
6a488035
TO
1679 }
1680
6a488035
TO
1681 // add permissioning for profiles only if not registration
1682 if (!$skipPermission) {
1683 $permissionClause = CRM_Core_Permission::ufGroupClause($op, 'civicrm_uf_group.');
1684 if (strpos($queryString, 'WHERE') !== FALSE) {
1685 $queryString .= " AND $permissionClause ";
1686 }
1687 else {
1688 $queryString .= " $permissionClause ";
1689 }
1690 }
1691
1692 $queryString .= ' ORDER BY civicrm_uf_join.weight, civicrm_uf_group.title';
1693 $dao = CRM_Core_DAO::executeQuery($queryString, $p);
1694
a6a0cdb7 1695 $ufGroups = [];
6a488035
TO
1696 while ($dao->fetch()) {
1697 //skip mix profiles in user Registration / User Account
a6a0cdb7 1698 if (($moduleName === 'User Registration' || $moduleName === 'User Account') &&
6a488035
TO
1699 CRM_Core_BAO_UFField::checkProfileType($dao->id)
1700 ) {
1701 continue;
1702 }
7ba38389 1703 foreach ($selectFields as $key => $field) {
a6a0cdb7 1704 if ($field === 'id') {
7ba38389 1705 continue;
1706 }
7ba38389 1707 $ufGroups[$dao->id][$field] = $dao->$field;
1708 }
6a488035
TO
1709 }
1710
1711 // Allow other modules to alter/override the UFGroups.
1712 CRM_Utils_Hook::buildUFGroupsForModule($moduleName, $ufGroups);
1713
1714 return $ufGroups;
1715 }
1716
1717 /**
fe482240 1718 * Filter ufgroups based on logged in user contact type.
6a488035 1719 *
6a0b768e
TO
1720 * @param int $ufGroupId
1721 * Uf group id (profile id).
c490a46a 1722 * @param int $contactID
77b97be7 1723 *
e7483cbe 1724 * @return bool
a6c01b45 1725 * true or false
6a488035 1726 */
00be9182 1727 public static function filterUFGroups($ufGroupId, $contactID = NULL) {
6a488035
TO
1728 if (!$contactID) {
1729 $session = CRM_Core_Session::singleton();
1730 $contactID = $session->get('userID');
1731 }
1732
1733 if ($contactID) {
1734 //get the contact type
1735 $contactType = CRM_Contact_BAO_Contact::getContactType($contactID);
1736
1737 //match if exixting contact type is same as profile contact type
1738 $profileType = CRM_Core_BAO_UFField::getProfileType($ufGroupId);
1739
1740 if (CRM_Contact_BAO_ContactType::isaSubType($profileType)) {
1741 $profileType = CRM_Contact_BAO_ContactType::getBasicType($profileType);
34daab76
M
1742
1743 //in some cases getBasicType() returns a cached array instead of string. Example: array ('sponsor' => 'organization')
1744 if (is_array($profileType)) {
1745 $profileType = array_shift($profileType);
1746 }
6a488035
TO
1747 }
1748
1749 //allow special mix profiles for Contribution and Participant
a6a0cdb7 1750 $specialProfiles = ['Contribution', 'Participant', 'Membership'];
6a488035
TO
1751
1752 if (in_array($profileType, $specialProfiles)) {
1753 return TRUE;
1754 }
1755
1756 if (($contactType == $profileType) || $profileType == 'Contact') {
1757 return TRUE;
1758 }
1759 }
1760
1761 return FALSE;
1762 }
1763
1764 /**
fe482240 1765 * Add profile field to a form.
6a488035 1766 *
c927c151 1767 * @param CRM_Core_Form $form
6a0b768e
TO
1768 * @param array $field
1769 * Properties.
1770 * @param int $mode
1771 * Profile mode.
1f177c6b 1772 * @param int $contactId
77b97be7 1773 * @param bool $online
6a0b768e
TO
1774 * @param string $usedFor
1775 * For building up prefixed fieldname for special cases (e.g. onBehalf, Honor).
1f177c6b 1776 * @param int $rowNumber
77b97be7
EM
1777 * @param string $prefix
1778 *
6a488035 1779 * @return null
6a488035 1780 */
e7483cbe 1781 public static function buildProfile(
6a488035
TO
1782 &$form,
1783 &$field,
1784 $mode,
1785 $contactId = NULL,
1786 $online = FALSE,
133e2c99 1787 $usedFor = NULL,
5d6ac993 1788 $rowNumber = NULL,
6a488035
TO
1789 $prefix = ''
1790 ) {
a6a0cdb7 1791 $defaultValues = [];
1f177c6b
CW
1792 $fieldName = $field['name'];
1793 $title = $field['title'];
1794 $attributes = $field['attributes'];
1795 $rule = $field['rule'];
1796 $view = $field['is_view'];
1797 $required = ($mode == CRM_Profile_Form::MODE_SEARCH) ? FALSE : $field['is_required'];
63d76404 1798 $search = $mode == CRM_Profile_Form::MODE_SEARCH;
1f177c6b 1799 $isShared = CRM_Utils_Array::value('is_shared', $field, 0);
6a488035
TO
1800
1801 // do not display view fields in drupal registration form
1802 // CRM-4632
1803 if ($view && $mode == CRM_Profile_Form::MODE_REGISTER) {
e7483cbe 1804 return NULL;
6a488035
TO
1805 }
1806
133e2c99 1807 if ($usedFor == 'onbehalf') {
6a488035
TO
1808 $name = "onbehalf[$fieldName]";
1809 }
133e2c99 1810 elseif ($usedFor == 'honor') {
1811 $name = "honor[$fieldName]";
1812 }
6a488035
TO
1813 elseif ($contactId && !$online) {
1814 $name = "field[$contactId][$fieldName]";
1815 }
1816 elseif ($rowNumber) {
1817 $name = "field[$rowNumber][$fieldName]";
1818 }
1819 elseif (!empty($prefix)) {
5d6ac993 1820 $name = $prefix . "[$fieldName]";
6a488035
TO
1821 }
1822 else {
1823 $name = $fieldName;
1824 }
9e1854a1 1825
a6a0cdb7 1826 $selectAttributes = ['class' => 'crm-select2', 'placeholder' => TRUE];
6a488035
TO
1827
1828 if ($fieldName == 'image_URL' && $mode == CRM_Profile_Form::MODE_EDIT) {
e8fb9449 1829 $deleteExtra = json_encode(ts('Are you sure you want to delete contact image.'));
a6a0cdb7 1830 $deleteURL = [
1831 CRM_Core_Action::DELETE => [
e7483cbe
J
1832 'name' => ts('Delete Contact Image'),
1833 'url' => 'civicrm/contact/image',
1834 'qs' => 'reset=1&id=%%id%%&gid=%%gid%%&action=delete',
e8fb9449 1835 'extra' => 'onclick = "' . htmlspecialchars("if (confirm($deleteExtra)) this.href+='&confirmed=1'; else return false;") . '"',
a6a0cdb7 1836 ],
1837 ];
6a488035
TO
1838 $deleteURL = CRM_Core_Action::formLink($deleteURL,
1839 CRM_Core_Action::DELETE,
a6a0cdb7 1840 [
5d6ac993 1841 'id' => $form->get('id'),
6a488035 1842 'gid' => $form->get('gid'),
a6a0cdb7 1843 ],
87dab4a4
AH
1844 ts('more'),
1845 FALSE,
1846 'contact.profileimage.delete',
1847 'Contact',
1848 $form->get('id')
6a488035
TO
1849 );
1850 $form->assign('deleteURL', $deleteURL);
1851 }
1852 $addressOptions = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
1853 'address_options', TRUE, NULL, TRUE
1854 );
1855
1856 if (substr($fieldName, 0, 14) === 'state_province') {
a6a0cdb7 1857 $form->addChainSelect($name, ['label' => $title, 'required' => $required]);
6a488035 1858 $config = CRM_Core_Config::singleton();
a6a0cdb7 1859 if (!in_array($mode, [CRM_Profile_Form::MODE_EDIT, CRM_Profile_Form::MODE_SEARCH]) &&
6a488035
TO
1860 $config->defaultContactStateProvince
1861 ) {
1862 $defaultValues[$name] = $config->defaultContactStateProvince;
1863 $form->setDefaults($defaultValues);
1864 }
1865 }
1866 elseif (substr($fieldName, 0, 7) === 'country') {
a2eb6152 1867 $form->add('select', $name, $title, CRM_Core_PseudoConstant::country(), $required, $selectAttributes);
6a488035 1868 $config = CRM_Core_Config::singleton();
a6a0cdb7 1869 if (!in_array($mode, [CRM_Profile_Form::MODE_EDIT, CRM_Profile_Form::MODE_SEARCH]) &&
6a488035
TO
1870 $config->defaultContactCountry
1871 ) {
1872 $defaultValues[$name] = $config->defaultContactCountry;
1873 $form->setDefaults($defaultValues);
1874 }
1875 }
1876 elseif (substr($fieldName, 0, 6) === 'county') {
1877 if ($addressOptions['county']) {
a6a0cdb7 1878 $form->addChainSelect($name, ['label' => $title, 'required' => $required]);
6a488035
TO
1879 }
1880 }
1881 elseif (substr($fieldName, 0, 9) === 'image_URL') {
1882 $form->add('file', $name, $title, $attributes, $required);
1883 $form->addUploadElement($name);
1884 }
1885 elseif (substr($fieldName, 0, 2) === 'im') {
1886 $form->add('text', $name, $title, $attributes, $required);
1887 if (!$contactId) {
133e2c99 1888 if ($usedFor) {
a6a0cdb7 1889 if (substr($name, -1) === ']') {
6a488035
TO
1890 $providerName = substr($name, 0, -1) . '-provider_id]';
1891 }
1892 $form->add('select', $providerName, NULL,
a2eb6152 1893 CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'), $required
6a488035
TO
1894 );
1895 }
1896 else {
1897 $form->add('select', $name . '-provider_id', $title,
a2eb6152 1898 CRM_Core_PseudoConstant::get('CRM_Core_DAO_IM', 'provider_id'), $required
6a488035
TO
1899 );
1900 }
1901
1902 if ($view && $mode != CRM_Profile_Form::MODE_SEARCH) {
1903 $form->freeze($name . '-provider_id');
1904 }
1905 }
1906 }
5d6ac993 1907 elseif (CRM_Utils_Array::value('name', $field) == 'membership_type') {
6a488035
TO
1908 list($orgInfo, $types) = CRM_Member_BAO_MembershipType::getMembershipTypeInfo();
1909 $sel = &$form->addElement('hierselect', $name, $title);
a2eb6152 1910 $select = ['' => ts('- select membership type -')];
5d6ac993 1911 if (count($orgInfo) == 1 && $field['is_required']) {
53cfc93c 1912 // we only have one org - so we should default to it. Not sure about defaulting to first type
1913 // as it could be missed - so adding a select
1914 // however, possibly that is more similar to the membership form
5d6ac993 1915 if (count($types[1]) > 1) {
53cfc93c 1916 $types[1] = $select + $types[1];
1917 }
1918 }
1919 else {
1920 $orgInfo = $select + $orgInfo;
1921 }
a6a0cdb7 1922 $sel->setOptions([$orgInfo, $types]);
6a488035 1923 }
5d6ac993 1924 elseif (CRM_Utils_Array::value('name', $field) == 'membership_status') {
6a488035 1925 $form->add('select', $name, $title,
a2eb6152 1926 CRM_Member_PseudoConstant::membershipStatus(NULL, NULL, 'label'), $required
6a488035
TO
1927 );
1928 }
a6a0cdb7 1929 elseif (in_array($fieldName, ['gender_id', 'communication_style_id'])) {
1930 $options = [];
ee015de4 1931 $pseudoValues = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', $fieldName);
39405208 1932 $form->addRadio($name, ts('%1', [1 => $title]), $pseudoValues, ['allowClear' => !$required], NULL, $required);
6a488035 1933 }
f45334bd 1934 elseif ($fieldName === 'prefix_id' || $fieldName === 'suffix_id') {
a6a0cdb7 1935 $form->addSelect($name, [
1f177c6b
CW
1936 'label' => $title,
1937 'entity' => 'contact',
1938 'field' => $fieldName,
1939 'class' => 'six',
1940 'placeholder' => '',
a6a0cdb7 1941 ], $required);
6a488035
TO
1942 }
1943 elseif ($fieldName === 'contact_sub_type') {
1944 $gId = $form->get('gid') ? $form->get('gid') : CRM_Utils_Array::value('group_id', $field);
133e2c99 1945 if ($usedFor == 'onbehalf') {
6a488035
TO
1946 $profileType = 'Organization';
1947 }
133e2c99 1948 elseif ($usedFor == 'honor') {
1949 $profileType = CRM_Core_BAO_UFField::getProfileType($form->_params['honoree_profile_id']);
1950 }
6a488035
TO
1951 else {
1952 $profileType = $gId ? CRM_Core_BAO_UFField::getProfileType($gId) : NULL;
1953 if ($profileType == 'Contact') {
1954 $profileType = 'Individual';
1955 }
1956 }
1957
1958 $setSubtype = FALSE;
1959 if (CRM_Contact_BAO_ContactType::isaSubType($profileType)) {
1960 $setSubtype = $profileType;
1961 $profileType = CRM_Contact_BAO_ContactType::getBasicType($profileType);
1962 }
1963
a6a0cdb7 1964 $subtypes = $profileType ? CRM_Contact_BAO_ContactType::subTypePairs($profileType) : [];
6a488035
TO
1965
1966 if ($setSubtype) {
a6a0cdb7 1967 $subtypeList = [];
6a488035
TO
1968 $subtypeList[$setSubtype] = $subtypes[$setSubtype];
1969 }
1970 else {
1971 $subtypeList = $subtypes;
1972 }
1973
a6a0cdb7 1974 $form->add('select', $name, $title, $subtypeList, $required, ['class' => 'crm-select2', 'multiple' => TRUE]);
6a488035
TO
1975 }
1976 elseif (in_array($fieldName, CRM_Contact_BAO_Contact::$_greetingTypes)) {
d43d0639
CW
1977 // Get contact type for greeting selector
1978 $gId = $form->get('gid') ?: CRM_Utils_Array::value('group_id', $field);
6a488035
TO
1979 $profileType = CRM_Core_BAO_UFField::getProfileType($gId, TRUE, FALSE, TRUE);
1980
d43d0639
CW
1981 if (!$profileType || in_array($profileType, ['Contact', 'Contribution', 'Participant', 'Membership'])) {
1982 $profileType = ($profileType == 'Contact' && $form->get('id')) ? CRM_Contact_BAO_Contact::getContactType($form->get('id')) : 'Individual';
6a488035
TO
1983 }
1984 if (CRM_Contact_BAO_ContactType::isaSubType($profileType)) {
1985 $profileType = CRM_Contact_BAO_ContactType::getBasicType($profileType);
1986 }
d43d0639 1987 $greeting = [
6a488035
TO
1988 'contact_type' => $profileType,
1989 'greeting_type' => $fieldName,
d43d0639 1990 ];
a2eb6152 1991 $form->add('select', $name, $title, CRM_Core_PseudoConstant::greeting($greeting), $required, ['placeholder' => TRUE]);
6a488035 1992 // add custom greeting element
a6a0cdb7 1993 $form->add('text', $fieldName . '_custom', ts('Custom %1', [1 => ucwords(str_replace('_', ' ', $fieldName))]),
6a488035
TO
1994 NULL, FALSE
1995 );
1996 }
1997 elseif ($fieldName === 'preferred_communication_method') {
e7e657f0 1998 $communicationFields = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method');
6a488035
TO
1999 foreach ($communicationFields as $key => $var) {
2000 if ($key == '') {
2001 continue;
2002 }
2003 $communicationOptions[] = $form->createElement('checkbox', $key, NULL, $var);
2004 }
2005 $form->addGroup($communicationOptions, $name, $title, '<br/>');
2006 }
2007 elseif ($fieldName === 'preferred_mail_format') {
2008 $form->add('select', $name, $title, CRM_Core_SelectValues::pmf());
2009 }
2010 elseif ($fieldName === 'preferred_language') {
a2eb6152 2011 $form->add('select', $name, $title, CRM_Contact_BAO_Contact::buildOptions('preferred_language'), $required, ['placeholder' => TRUE]);
6a488035
TO
2012 }
2013 elseif ($fieldName == 'external_identifier') {
2014 $form->add('text', $name, $title, $attributes, $required);
2015 $contID = $contactId;
2016 if (!$contID) {
2017 $contID = $form->get('id');
2018 }
2019 $form->addRule($name,
2020 ts('External ID already exists in Database.'),
2021 'objectExists',
a6a0cdb7 2022 ['CRM_Contact_DAO_Contact', $contID, 'external_identifier']
6a488035
TO
2023 );
2024 }
2025 elseif ($fieldName === 'group') {
2026 CRM_Contact_Form_Edit_TagsAndGroups::buildQuickForm($form, $contactId,
2027 CRM_Contact_Form_Edit_TagsAndGroups::GROUP,
2028 TRUE, $required,
be44418d 2029 $title, NULL, $name, 'checkbox', TRUE
6a488035
TO
2030 );
2031 }
2032 elseif ($fieldName === 'tag') {
2033 CRM_Contact_Form_Edit_TagsAndGroups::buildQuickForm($form, $contactId,
2034 CRM_Contact_Form_Edit_TagsAndGroups::TAG,
2035 FALSE, $required,
2036 NULL, $title, $name
2037 );
2038 }
2039 elseif (substr($fieldName, 0, 4) === 'url-') {
eef9a6da
J
2040 $form->add('text', $name, $title, CRM_Core_DAO::getAttribute('CRM_Core_DAO_Website', 'url'), $required);
2041 $form->addRule($name, ts('Enter a valid web address beginning with \'http://\' or \'https://\'.'), 'url');
6a488035
TO
2042 }
2043 // Note should be rendered as textarea
2044 elseif (substr($fieldName, -4) == 'note') {
2045 $form->add('textarea', $name, $title, $attributes, $required);
2046 }
2047 elseif (substr($fieldName, 0, 6) === 'custom') {
2048 $customFieldID = CRM_Core_BAO_CustomField::getKeyID($fieldName);
2049 if ($customFieldID) {
3a7773be 2050 CRM_Core_BAO_CustomField::addQuickFormElement($form, $name, $customFieldID, $required, $search, $title);
6a488035
TO
2051 }
2052 }
2053 elseif (substr($fieldName, 0, 14) === 'address_custom') {
2054 list($fName, $locTypeId) = CRM_Utils_System::explode('-', $fieldName, 2);
2055 $customFieldID = CRM_Core_BAO_CustomField::getKeyID(substr($fName, 8));
2056 if ($customFieldID) {
3a7773be 2057 CRM_Core_BAO_CustomField::addQuickFormElement($form, $name, $customFieldID, $required, $search, $title);
6a488035
TO
2058 }
2059 }
6a488035
TO
2060 elseif ($fieldName == 'send_receipt') {
2061 $form->addElement('checkbox', $name, $title);
2062 }
2063 elseif ($fieldName == 'soft_credit') {
a6a0cdb7 2064 $form->addEntityRef("soft_credit_contact_id[$rowNumber]", ts('Soft Credit To'), ['create' => TRUE]);
5ee60152 2065 $form->addMoney("soft_credit_amount[{$rowNumber}]", ts('Amount'), FALSE, NULL, FALSE);
6a488035 2066 }
a6a0cdb7 2067 elseif ($fieldName === 'product_name') {
6a488035
TO
2068 list($products, $options) = CRM_Contribute_BAO_Premium::getPremiumProductInfo();
2069 $sel = &$form->addElement('hierselect', $name, $title);
a2eb6152 2070 $products = ['0' => ts('- select %1 -', [1 => $title])] + $products;
a6a0cdb7 2071 $sel->setOptions([$products, $options]);
6a488035 2072 }
a6a0cdb7 2073 elseif ($fieldName === 'payment_instrument') {
6a488035 2074 $form->add('select', $name, $title,
a2eb6152 2075 CRM_Contribute_PseudoConstant::paymentInstrument(), $required, ['placeholder' => TRUE]);
6a488035 2076 }
a6a0cdb7 2077 elseif ($fieldName === 'financial_type') {
6a488035 2078 $form->add('select', $name, $title,
a2eb6152 2079 CRM_Contribute_PseudoConstant::financialType(), $required, ['placeholder' => TRUE]
6a488035
TO
2080 );
2081 }
a6a0cdb7 2082 elseif ($fieldName === 'contribution_status_id') {
9805da76 2083 $contributionStatuses = CRM_Contribute_BAO_Contribution_Utils::getContributionStatuses();
6a73ef3f 2084
6a488035 2085 $form->add('select', $name, $title,
a2eb6152 2086 $contributionStatuses, $required, ['placeholder' => TRUE]
6a488035
TO
2087 );
2088 }
a6a0cdb7 2089 elseif ($fieldName === 'soft_credit_type') {
9e1854a1 2090 $name = "soft_credit_type[$rowNumber]";
51fa20cb 2091 $form->add('select', $name, $title,
a2eb6152 2092 CRM_Core_OptionGroup::values("soft_credit_type"), ['placeholder' => TRUE]
51fa20cb 2093 );
9e1854a1 2094 //CRM-15350: choose SCT field default value as 'Gift' for membership use
2095 //else (for contribution), use configured SCT default value
2096 $SCTDefaultValue = CRM_Core_OptionGroup::getDefaultValue("soft_credit_type");
2097 if ($field['field_type'] == 'Membership') {
233319bf 2098 $SCTDefaultValue = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_ContributionSoft', 'soft_credit_type_id', 'gift');
9e1854a1 2099 }
a6a0cdb7 2100 $form->addElement('hidden', 'sct_default_id', $SCTDefaultValue, ['id' => 'sct_default_id']);
51fa20cb 2101 }
a4a361c9 2102 elseif ($fieldName == 'contribution_soft_credit_pcp_id') {
03ad81ae 2103 CRM_Contribute_Form_SoftCredit::addPCPFields($form, "[$rowNumber]");
51fa20cb 2104 }
6a488035 2105 elseif ($fieldName == 'currency') {
483a53a8 2106 $form->addCurrency($name, $title, $required, NULL, FALSE, FALSE);
6a488035
TO
2107 }
2108 elseif ($fieldName == 'contribution_page_id') {
2109 $form->add('select', $name, $title,
a2eb6152
AH
2110 CRM_Contribute_PseudoConstant::contributionPage(), $required, [
2111 'class' => 'big',
2112 'placeholder' => TRUE,
2113 ]
6a488035
TO
2114 );
2115 }
6a488035
TO
2116 elseif ($fieldName == 'activity_status_id') {
2117 $form->add('select', $name, $title,
a2eb6152 2118 CRM_Core_PseudoConstant::activityStatus(), $required, ['placeholder' => TRUE]
6a488035
TO
2119 );
2120 }
2121 elseif ($fieldName == 'activity_engagement_level') {
2122 $form->add('select', $name, $title,
a2eb6152 2123 CRM_Campaign_PseudoConstant::engagementLevel(), $required, ['placeholder' => TRUE]
6a488035
TO
2124 );
2125 }
6a488035
TO
2126 elseif ($fieldName == 'participant_status') {
2127 $cond = NULL;
2128 if ($online == TRUE) {
2129 $cond = 'visibility_id = 1';
2130 }
2131 $form->add('select', $name, $title,
a2eb6152 2132 CRM_Event_PseudoConstant::participantStatus(NULL, $cond, 'label'), $required, ['placeholder' => TRUE]
6a488035
TO
2133 );
2134 }
2135 elseif ($fieldName == 'participant_role') {
a7488080 2136 if (!empty($field['is_multiple'])) {
6a488035
TO
2137 $form->addCheckBox($name, $title, CRM_Event_PseudoConstant::participantRole(), NULL, NULL, NULL, NULL, '&nbsp', TRUE);
2138 }
2139 else {
2140 $form->add('select', $name, $title,
a2eb6152 2141 CRM_Event_PseudoConstant::participantRole(), $required, ['placeholder' => TRUE]
6a488035
TO
2142 );
2143 }
2144 }
2145 elseif ($fieldName == 'world_region') {
1f177c6b 2146 $form->add('select', $name, $title, CRM_Core_PseudoConstant::worldRegion(), $required, $selectAttributes);
6a488035
TO
2147 }
2148 elseif ($fieldName == 'signature_html') {
5d51a2f9 2149 $form->add('wysiwyg', $name, $title, CRM_Core_DAO::getAttribute('CRM_Core_DAO_Email', $fieldName));
6a488035
TO
2150 }
2151 elseif ($fieldName == 'signature_text') {
2152 $form->add('textarea', $name, $title, CRM_Core_DAO::getAttribute('CRM_Core_DAO_Email', $fieldName));
2153 }
2154 elseif (substr($fieldName, -11) == 'campaign_id') {
2155 if (CRM_Campaign_BAO_Campaign::isCampaignEnable()) {
2156 $campaigns = CRM_Campaign_BAO_Campaign::getCampaigns(CRM_Utils_Array::value($contactId,
2157 $form->_componentCampaigns
2158 ));
2159 $form->add('select', $name, $title,
a2eb6152 2160 $campaigns, $required,
a6a0cdb7 2161 [
a2eb6152
AH
2162 'class' => 'crm-select2 big',
2163 'placeholder' => TRUE,
2164 ]
6a488035
TO
2165 );
2166 }
2167 }
2168 elseif ($fieldName == 'activity_details') {
a6a0cdb7 2169 $form->add('wysiwyg', $fieldName, $title, ['rows' => 4, 'cols' => 60], $required);
6a488035
TO
2170 }
2171 elseif ($fieldName == 'activity_duration') {
2172 $form->add('text', $name, $title, $attributes, $required);
2173 $form->addRule($name, ts('Please enter the duration as number of minutes (integers only).'), 'positiveInteger');
2174 }
888da08c
MW
2175 elseif ($fieldName == 'case_status') {
2176 $form->add('select', $name, $title,
a2eb6152
AH
2177 CRM_Case_BAO_Case::buildOptions('case_status_id', 'create'),
2178 $required, ['placeholder' => TRUE]
888da08c
MW
2179 );
2180 }
6a488035
TO
2181 else {
2182 if (substr($fieldName, 0, 3) === 'is_' or substr($fieldName, 0, 7) === 'do_not_') {
2183 $form->add('advcheckbox', $name, $title, $attributes, $required);
2184 }
2732685b 2185 elseif (CRM_Utils_Array::value('html_type', $field) === 'Select Date') {
2186 $extra = isset($field['datepicker']) ? $field['datepicker']['extra'] : CRM_Utils_Date::getDatePickerExtra($field);
2187 $attributes = isset($field['datepicker']) ? $field['datepicker']['attributes'] : CRM_Utils_Date::getDatePickerAttributes($field);
2188 $form->add('datepicker', $name, $title, $attributes, $required, $extra);
2189 }
6a488035
TO
2190 else {
2191 $form->add('text', $name, $title, $attributes, $required);
2192 }
2193 }
2194
2195 static $hiddenSubtype = FALSE;
2196 if (!$hiddenSubtype && CRM_Contact_BAO_ContactType::isaSubType($field['field_type'])) {
2197 // In registration mode params are submitted via POST and we don't have any clue
2198 // about profile-id or the profile-type (which could be a subtype)
2199 // To generalize the behavior and simplify the process,
2200 // lets always add the hidden
2201 //subtype value if there is any, and we won't have to
2202 // compute it while processing.
133e2c99 2203 if ($usedFor) {
2204 $form->addElement('hidden', $usedFor . '[contact_sub_type]', $field['field_type']);
6a488035
TO
2205 }
2206 else {
2207 $form->addElement('hidden', 'contact_sub_type_hidden', $field['field_type']);
2208 }
2209 $hiddenSubtype = TRUE;
2210 }
2211
2212 if (($view && $mode != CRM_Profile_Form::MODE_SEARCH) || $isShared) {
2213 $form->freeze($name);
2214 }
2215
2216 //add the rules
a6a0cdb7 2217 if (in_array($fieldName, [
5d6ac993
TO
2218 'non_deductible_amount',
2219 'total_amount',
2220 'fee_amount',
21dfd5f5 2221 'net_amount',
a6a0cdb7 2222 ])) {
6a488035
TO
2223 $form->addRule($name, ts('Please enter a valid amount.'), 'money');
2224 }
6a488035
TO
2225 if ($rule) {
2226 if (!($rule == 'email' && $mode == CRM_Profile_Form::MODE_SEARCH)) {
a6a0cdb7 2227 $form->addRule($name, ts('Please enter a valid %1', [1 => $title]), $rule);
6a488035
TO
2228 }
2229 }
2230 }
2231
2232 /**
fe482240 2233 * Set profile defaults.
6a488035 2234 *
6a0b768e
TO
2235 * @param int $contactId
2236 * Contact id.
2237 * @param array $fields
2238 * Associative array of fields.
2239 * @param array $defaults
2240 * Defaults array.
2241 * @param bool $singleProfile
b581842f 2242 * True for single profile else false(Update multiple items).
6a0b768e
TO
2243 * @param int $componentId
2244 * Id for specific components like contribute, event etc.
2245 * @param null $component
6a488035 2246 */
e7483cbe 2247 public static function setProfileDefaults(
f9f40af3
TO
2248 $contactId, &$fields, &$defaults,
2249 $singleProfile = TRUE, $componentId = NULL, $component = NULL
6a488035
TO
2250 ) {
2251 if (!$componentId) {
2252 //get the contact details
00bb741c 2253 $contactDetails = CRM_Contact_BAO_Contact::getHierContactDetails($contactId, $fields);
9c1bc317 2254 $details = $contactDetails[$contactId] ?? NULL;
a6a0cdb7 2255 $multipleFields = ['website' => 'url'];
6a488035
TO
2256
2257 //start of code to set the default values
2258 foreach ($fields as $name => $field) {
2259 // skip pseudo fields
2260 if (substr($name, 0, 9) == 'phone_ext') {
2261 continue;
2262 }
2263
b581842f 2264 //set the field name depending upon the profile mode(single/multiple)
6a488035
TO
2265 if ($singleProfile) {
2266 $fldName = $name;
2267 }
2268 else {
2269 $fldName = "field[$contactId][$name]";
2270 }
2271
2272 if ($name == 'group') {
2273 CRM_Contact_Form_Edit_TagsAndGroups::setDefaults($contactId, $defaults, CRM_Contact_Form_Edit_TagsAndGroups::GROUP, $fldName);
2274 }
2275 if ($name == 'tag') {
2276 CRM_Contact_Form_Edit_TagsAndGroups::setDefaults($contactId, $defaults, CRM_Contact_Form_Edit_TagsAndGroups::TAG, $fldName);
2277 }
2278
a7488080 2279 if (!empty($details[$name]) || isset($details[$name])) {
6a488035 2280 //to handle custom data (checkbox) to be written
58f00377 2281 // to handle birth/deceased date, greeting_type and few other fields
2732685b 2282 if (in_array($name, CRM_Contact_BAO_Contact::$_greetingTypes)) {
6a488035
TO
2283 $defaults[$fldName] = $details[$name . '_id'];
2284 $defaults[$name . '_custom'] = $details[$name . '_custom'];
2285 }
2286 elseif ($name == 'preferred_communication_method') {
d5a9068c
PN
2287 $v = $details[$name];
2288 if (!is_array($details[$name])) {
2289 $v = explode(CRM_Core_DAO::VALUE_SEPARATOR, $v);
2290 }
6a488035
TO
2291 foreach ($v as $item) {
2292 if ($item) {
2293 $defaults[$fldName . "[$item]"] = 1;
2294 }
2295 }
2296 }
8aefc657
RK
2297 elseif ($name == 'contact_sub_type') {
2298 $defaults[$fldName] = explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($details[$name], CRM_Core_DAO::VALUE_SEPARATOR));
2299 }
6a488035
TO
2300 elseif ($name == 'world_region') {
2301 $defaults[$fldName] = $details['worldregion_id'];
2302 }
d4502c25
CW
2303 elseif (CRM_Core_BAO_CustomField::getKeyID($name)) {
2304 $defaults[$fldName] = self::formatCustomValue($field, $details[$name]);
f1d04518
JM
2305 if (!$singleProfile && $field['html_type'] === 'CheckBox') {
2306 // For batch update profile there needs to be a key lik
2307 // $defaults['field[166]['custom_8'][2]'] => 1 where
2308 // 166 is the conntact id, 8 is the field id and 2 is the checkbox option.
2309 foreach ($defaults[$fldName] as $itemKey => $itemValue) {
2310 $defaults[$fldName . '[' . $itemKey . ']'] = $itemValue;
2311 }
2312 }
6a488035
TO
2313 }
2314 else {
2315 $defaults[$fldName] = $details[$name];
2316 }
2317 }
2318 else {
a6a0cdb7 2319 $blocks = ['email', 'phone', 'im', 'openid'];
6a488035
TO
2320 list($fieldName, $locTypeId, $phoneTypeId) = CRM_Utils_System::explode('-', $name, 3);
2321 if (!in_array($fieldName, $multipleFields)) {
2322 if (is_array($details)) {
2323 foreach ($details as $key => $value) {
2324 // when we fixed CRM-5319 - get primary loc
2325 // type as per loc field and removed below code.
2326 $primaryLocationType = FALSE;
2327 if ($locTypeId == 'Primary') {
5d6ac993 2328 if (is_array($value) && array_key_exists($fieldName, $value)) {
6a488035 2329 $primaryLocationType = TRUE;
5d6ac993 2330 if (in_array($fieldName, $blocks)) {
6a488035
TO
2331 $locTypeId = CRM_Contact_BAO_Contact::getPrimaryLocationType($contactId, FALSE, $fieldName);
2332 }
5d6ac993 2333 else {
6a488035
TO
2334 $locTypeId = CRM_Contact_BAO_Contact::getPrimaryLocationType($contactId, FALSE, 'address');
2335 }
2336 }
2337 }
2338
2339 // fixed for CRM-665
2340 if (is_numeric($locTypeId)) {
2341 if ($primaryLocationType || $locTypeId == CRM_Utils_Array::value('location_type_id', $value)) {
a7488080 2342 if (!empty($value[$fieldName])) {
6a488035
TO
2343 //to handle stateprovince and country
2344 if ($fieldName == 'state_province') {
2345 $defaults[$fldName] = $value['state_province_id'];
2346 }
2347 elseif ($fieldName == 'county') {
2348 $defaults[$fldName] = $value['county_id'];
2349 }
2350 elseif ($fieldName == 'country') {
6a488035
TO
2351 if (!isset($value['country_id']) || !$value['country_id']) {
2352 $config = CRM_Core_Config::singleton();
2353 if ($config->defaultContactCountry) {
2354 $defaults[$fldName] = $config->defaultContactCountry;
2355 }
2356 }
b3071092
DL
2357 else {
2358 $defaults[$fldName] = $value['country_id'];
2359 }
6a488035
TO
2360 }
2361 elseif ($fieldName == 'phone') {
2362 if ($phoneTypeId) {
2363 if (isset($value['phone'][$phoneTypeId])) {
2364 $defaults[$fldName] = $value['phone'][$phoneTypeId];
2365 }
2366 if (isset($value['phone_ext'][$phoneTypeId])) {
2367 $defaults[str_replace('phone', 'phone_ext', $fldName)] = $value['phone_ext'][$phoneTypeId];
2368 }
2369 }
2370 else {
9c1bc317 2371 $phoneDefault = $value['phone'] ?? NULL;
6a488035
TO
2372 // CRM-9216
2373 if (!is_array($phoneDefault)) {
2374 $defaults[$fldName] = $phoneDefault;
2375 }
2376 }
2377 }
2378 elseif ($fieldName == 'email') {
2379 //adding the first email (currently we don't support multiple emails of same location type)
2380 $defaults[$fldName] = $value['email'];
2381 }
2382 elseif ($fieldName == 'im') {
2383 //adding the first im (currently we don't support multiple ims of same location type)
2384 $defaults[$fldName] = $value['im'];
2385 $defaults[$fldName . '-provider_id'] = $value['im_provider_id'];
2386 }
2387 else {
2388 $defaults[$fldName] = $value[$fieldName];
2389 }
2390 }
d4502c25
CW
2391 elseif (strpos($fieldName, 'address_custom') === 0 && !empty($value[substr($fieldName, 8)])) {
2392 $defaults[$fldName] = self::formatCustomValue($field, $value[substr($fieldName, 8)]);
6a488035
TO
2393 }
2394 }
2395 }
d4502c25
CW
2396 elseif (strpos($fieldName, 'address_custom') === 0 && !empty($value[substr($fieldName, 8)])) {
2397 $defaults[$fldName] = self::formatCustomValue($field, $value[substr($fieldName, 8)]);
2076c0d2 2398 }
6a488035
TO
2399 }
2400 }
2401 }
2402 else {
2403 if (is_array($details)) {
1fd63589
EM
2404 if ($fieldName === 'url'
2405 && !empty($details['website'])
5d6ac993
TO
2406 && !empty($details['website'][$locTypeId])
2407 ) {
9c1bc317 2408 $defaults[$fldName] = $details['website'][$locTypeId]['url'] ?? NULL;
6a488035
TO
2409 }
2410 }
2411 }
2412 }
2413 }
2414 }
2415
888da08c 2416 // Handling Contribution Part of the batch profile
6a488035
TO
2417 if (CRM_Core_Permission::access('CiviContribute') && $component == 'Contribute') {
2418 self::setComponentDefaults($fields, $componentId, $component, $defaults);
2419 }
2420
888da08c 2421 // Handling Event Participation Part of the batch profile
6a488035
TO
2422 if (CRM_Core_Permission::access('CiviEvent') && $component == 'Event') {
2423 self::setComponentDefaults($fields, $componentId, $component, $defaults);
2424 }
2425
888da08c 2426 // Handling membership Part of the batch profile
6a488035
TO
2427 if (CRM_Core_Permission::access('CiviMember') && $component == 'Membership') {
2428 self::setComponentDefaults($fields, $componentId, $component, $defaults);
2429 }
2430
888da08c 2431 // Handling Activity Part of the batch profile
6a488035
TO
2432 if ($component == 'Activity') {
2433 self::setComponentDefaults($fields, $componentId, $component, $defaults);
2434 }
888da08c
MW
2435
2436 // Handling Case Part of the batch profile
2437 if (CRM_Core_Permission::access('CiviCase') && $component == 'Case') {
2438 self::setComponentDefaults($fields, $componentId, $component, $defaults);
2439 }
6a488035
TO
2440 }
2441
2442 /**
100fef9d 2443 * Get profiles by type eg: pure Individual etc
6a488035 2444 *
6a0b768e
TO
2445 * @param array $types
2446 * Associative array of types eg: types('Individual').
2447 * @param bool $onlyPure
2448 * True if only pure profiles are required.
6a488035 2449 *
a6c01b45
CW
2450 * @return array
2451 * associative array of profiles
6a488035 2452 */
00be9182 2453 public static function getProfiles($types, $onlyPure = FALSE) {
a6a0cdb7 2454 $profiles = [];
ff4f7744 2455 $ufGroups = CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id');
6a488035
TO
2456
2457 CRM_Utils_Hook::aclGroup(CRM_Core_Permission::ADMIN, NULL, 'civicrm_uf_group', $ufGroups, $ufGroups);
2458
2459 // Exclude Batch Data Entry profiles - CRM-10901
2460 $batchProfiles = CRM_Core_BAO_UFGroup::getBatchProfiles();
2461
2462 foreach ($ufGroups as $id => $title) {
2463 $ptype = CRM_Core_BAO_UFField::getProfileType($id, FALSE, $onlyPure);
2464 if (in_array($ptype, $types) && !array_key_exists($id, $batchProfiles)) {
2465 $profiles[$id] = $title;
2466 }
2467 }
2468 return $profiles;
2469 }
2470
2471 /**
100fef9d 2472 * Check whether a profile is valid combination of
6a488035
TO
2473 * required and/or optional profile types
2474 *
6a0b768e
TO
2475 * @param array $required
2476 * Array of types those are required.
2477 * @param array $optional
2478 * Array of types those are optional.
6a488035 2479 *
a6c01b45
CW
2480 * @return array
2481 * associative array of profiles
6a488035 2482 */
00be9182 2483 public static function getValidProfiles($required, $optional = NULL) {
6a488035 2484 if (!is_array($required) || empty($required)) {
e7483cbe 2485 return NULL;
6a488035
TO
2486 }
2487
a6a0cdb7 2488 $profiles = [];
ff4f7744 2489 $ufGroups = CRM_Core_PseudoConstant::get('CRM_Core_DAO_UFField', 'uf_group_id');
6a488035
TO
2490
2491 CRM_Utils_Hook::aclGroup(CRM_Core_Permission::ADMIN, NULL, 'civicrm_uf_group', $ufGroups, $ufGroups);
2492
2493 foreach ($ufGroups as $id => $title) {
2494 $type = CRM_Core_BAO_UFField::checkValidProfileType($id, $required, $optional);
2495 if ($type) {
2496 $profiles[$id] = $title;
2497 }
2498 }
2499
2500 return $profiles;
2501 }
2502
2503 /**
100fef9d 2504 * Check whether a profile is valid combination of
6a488035
TO
2505 * required profile fields
2506 *
6a0b768e
TO
2507 * @param array $ufId
2508 * Integer id of the profile.
2509 * @param array $required
2510 * Array of fields those are required in the profile.
6a488035 2511 *
a6c01b45
CW
2512 * @return array
2513 * associative array of profiles
6a488035 2514 */
00be9182 2515 public static function checkValidProfile($ufId, $required = NULL) {
6a488035
TO
2516 $validProfile = FALSE;
2517 if (!$ufId) {
2518 return $validProfile;
2519 }
2520
2521 if (!CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $ufId, 'is_active')) {
2522 return $validProfile;
2523 }
2524
2525 $profileFields = self::getFields($ufId, FALSE, CRM_Core_Action::VIEW, NULL,
2526 NULL, FALSE, NULL, FALSE, NULL,
2527 CRM_Core_Permission::CREATE, NULL
2528 );
2529
a6a0cdb7 2530 $validProfile = [];
6a488035
TO
2531 if (!empty($profileFields)) {
2532 $fields = array_keys($profileFields);
2533 foreach ($fields as $val) {
2534 foreach ($required as $key => $field) {
2535 if (strpos($val, $field) === 0) {
2536 unset($required[$key]);
2537 }
2538 }
2539 }
2540
63d76404 2541 $validProfile = (empty($required));
6a488035
TO
2542 }
2543
2544 return $validProfile;
2545 }
2546
2547 /**
100fef9d 2548 * Get default value for Register.
6a488035 2549 *
72b3a70c
CW
2550 * @param array $fields
2551 * @param array $defaults
77b97be7 2552 *
72b3a70c 2553 * @return array
6a488035 2554 */
00be9182 2555 public static function setRegisterDefaults(&$fields, &$defaults) {
b525f1cc 2556 $config = CRM_Core_Config::singleton();
6a488035
TO
2557 foreach ($fields as $name => $field) {
2558 if (substr($name, 0, 8) == 'country-') {
b525f1cc 2559 if (!empty($config->defaultContactCountry)) {
6a488035
TO
2560 $defaults[$name] = $config->defaultContactCountry;
2561 }
2562 }
2563 elseif (substr($name, 0, 15) == 'state_province-') {
b525f1cc 2564 if (!empty($config->defaultContactStateProvince)) {
6a488035
TO
2565 $defaults[$name] = $config->defaultContactStateProvince;
2566 }
2567 }
2568 }
2569 return $defaults;
2570 }
2571
2572 /**
dc195289 2573 * make a copy of a profile, including
6a488035
TO
2574 * all the fields in the profile
2575 *
6a0b768e
TO
2576 * @param int $id
2577 * The profile id to copy.
6a488035 2578 *
8eedd10a 2579 * @return \CRM_Core_DAO
6a488035 2580 */
00be9182 2581 public static function copy($id) {
9739890f 2582 $maxId = CRM_Core_DAO::singleValueQuery("SELECT max(id) FROM civicrm_uf_group");
2583
a6a0cdb7 2584 $title = ts('[Copy id %1]', [1 => $maxId + 1]);
2585 $fieldsFix = [
2586 'suffix' => [
9739890f 2587 'title' => ' ' . $title,
2588 'name' => '__Copy_id_' . ($maxId + 1) . '_',
a6a0cdb7 2589 ],
2590 ];
9739890f 2591
3fec1adc 2592 $copy = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFGroup',
a6a0cdb7 2593 ['id' => $id],
6a488035
TO
2594 NULL,
2595 $fieldsFix
2596 );
2597
2598 if ($pos = strrpos($copy->name, "_{$id}")) {
2599 $copy->name = substr_replace($copy->name, '', $pos);
2600 }
2601 $copy->name = CRM_Utils_String::munge($copy->name, '_', 56) . "_{$copy->id}";
2602 $copy->save();
2603
3fec1adc 2604 $copyUFJoin = CRM_Core_DAO::copyGeneric('CRM_Core_DAO_UFJoin',
a6a0cdb7 2605 ['uf_group_id' => $id],
2606 ['uf_group_id' => $copy->id],
6a488035
TO
2607 NULL,
2608 'entity_table'
2609 );
2610
3fec1adc 2611 $copyUFField = CRM_Core_DAO::copyGeneric('CRM_Core_BAO_UFField',
a6a0cdb7 2612 ['uf_group_id' => $id],
2613 ['uf_group_id' => $copy->id]
6a488035
TO
2614 );
2615
2616 $maxWeight = CRM_Utils_Weight::getMax('CRM_Core_DAO_UFJoin', NULL, 'weight');
2617
2618 //update the weight
2619 $query = "
2620UPDATE civicrm_uf_join
2621SET weight = %1
2622WHERE uf_group_id = %2
2623AND ( entity_id IS NULL OR entity_id <= 0 )
2624";
a6a0cdb7 2625 $p = [
2626 1 => [$maxWeight + 1, 'Integer'],
2627 2 => [$copy->id, 'Integer'],
2628 ];
6a488035
TO
2629 CRM_Core_DAO::executeQuery($query, $p);
2630 if ($copy->is_reserved) {
2631 $query = "UPDATE civicrm_uf_group SET is_reserved = 0 WHERE id = %1";
a6a0cdb7 2632 $params = [1 => [$copy->id, 'Integer']];
6a488035
TO
2633 CRM_Core_DAO::executeQuery($query, $params);
2634 }
2635 CRM_Utils_Hook::copy('UFGroup', $copy);
2636
2637 return $copy;
2638 }
2639
2640 /**
2641 * Process that send notification e-mails
2642 *
6a0b768e
TO
2643 * @param int $contactID
2644 * Contact id.
2645 * @param array $values
2646 * Associative array of name/value pair.
6a488035 2647 */
00be9182 2648 public static function commonSendMail($contactID, &$values) {
6a488035
TO
2649 if (!$contactID || !$values) {
2650 return;
2651
2652 }
2653 $template = CRM_Core_Smarty::singleton();
2654
2655 $displayName = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
2656 $contactID,
2657 'display_name'
2658 );
2659
2660 self::profileDisplay($values['id'], $values['values'], $template);
2661 $emailList = explode(',', $values['email']);
2662
2663 $contactLink = CRM_Utils_System::url('civicrm/contact/view',
2664 "reset=1&cid=$contactID",
5d6ac993 2665 TRUE, NULL, FALSE, FALSE, TRUE
6a488035
TO
2666 );
2667
2668 //get the default domain email address.
2669 list($domainEmailName, $domainEmailAddress) = CRM_Core_BAO_Domain::getNameAndEmail();
2670
2671 if (!$domainEmailAddress || $domainEmailAddress == 'info@EXAMPLE.ORG') {
2672 $fixUrl = CRM_Utils_System::url('civicrm/admin/domain', 'action=update&reset=1');
ac15829d 2673 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]));
6a488035
TO
2674 }
2675
2676 foreach ($emailList as $emailTo) {
2677 // FIXME: take the below out of the foreach loop
c6327d7d 2678 CRM_Core_BAO_MessageTemplate::sendTemplate(
a6a0cdb7 2679 [
6a488035
TO
2680 'groupName' => 'msg_tpl_workflow_uf',
2681 'valueName' => 'uf_notify',
2682 'contactId' => $contactID,
a6a0cdb7 2683 'tplParams' => [
6a488035
TO
2684 'displayName' => $displayName,
2685 'currentDate' => date('r'),
2686 'contactLink' => $contactLink,
a6a0cdb7 2687 ],
6a488035
TO
2688 'from' => "$domainEmailName <$domainEmailAddress>",
2689 'toEmail' => $emailTo,
a6a0cdb7 2690 ]
6a488035
TO
2691 );
2692 }
2693 }
2694
2695 /**
2696 * Given a contact id and a group id, returns the field values from the db
2697 * for this group and notify email only if group's notify field is
2698 * set and field values are not empty
2699 *
6a0b768e
TO
2700 * @param int $gid
2701 * Group id.
2702 * @param int $cid
2703 * Contact id.
c490a46a 2704 * @param array $params
1fd63589
EM
2705 * @param bool $skipCheck
2706 *
6a488035 2707 * @return array
6a488035 2708 */
00be9182 2709 public function checkFieldsEmptyValues($gid, $cid, $params, $skipCheck = FALSE) {
6a488035
TO
2710 if ($gid) {
2711 if (CRM_Core_BAO_UFGroup::filterUFGroups($gid, $cid) || $skipCheck) {
a6a0cdb7 2712 $values = [];
6a488035
TO
2713 $fields = CRM_Core_BAO_UFGroup::getFields($gid, FALSE, CRM_Core_Action::VIEW);
2714 CRM_Core_BAO_UFGroup::getValues($cid, $fields, $values, FALSE, $params, TRUE);
2715
2716 $email = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'notify');
2717
2718 if (!empty($values) &&
2719 !empty($email)
2720 ) {
a6a0cdb7 2721 $val = [
6a488035
TO
2722 'id' => $gid,
2723 'values' => $values,
2724 'email' => $email,
a6a0cdb7 2725 ];
6a488035
TO
2726 return $val;
2727 }
2728 }
2729 }
2730 return NULL;
2731 }
2732
2733 /**
fe482240 2734 * Assign uf fields to template.
6a488035 2735 *
6a0b768e
TO
2736 * @param int $gid
2737 * Group id.
c490a46a
CW
2738 * @param array $values
2739 * @param CRM_Core_Smarty $template
6a488035 2740 */
518fa0ee 2741 public static function profileDisplay($gid, $values, $template) {
6a488035
TO
2742 $groupTitle = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'title');
2743 $template->assign('grouptitle', $groupTitle);
2744 if (count($values)) {
2745 $template->assign('values', $values);
2746 }
2747 }
2748
2749 /**
fe482240 2750 * Format fields for dupe Contact Matching.
6a488035 2751 *
6a0b768e 2752 * @param array $params
6a488035 2753 *
100fef9d 2754 * @param int $contactId
1fd63589 2755 *
a6c01b45 2756 * @return array
b44e3f84 2757 * associated formatted array
6a488035 2758 */
00be9182 2759 public static function formatFields($params, $contactId = NULL) {
6a488035
TO
2760 if ($contactId) {
2761 // get the primary location type id and email
2762 list($name, $primaryEmail, $primaryLocationType) = CRM_Contact_BAO_Contact_Location::getEmailDetails($contactId);
2763 }
2764 else {
2765 $defaultLocationType = CRM_Core_BAO_LocationType::getDefault();
2766 $primaryLocationType = $defaultLocationType->id;
2767 }
2768
a6a0cdb7 2769 $data = [];
2770 $locationType = [];
5d6ac993 2771 $count = 1;
6a488035
TO
2772 $primaryLocation = 0;
2773 foreach ($params as $key => $value) {
2774 list($fieldName, $locTypeId, $phoneTypeId) = explode('-', $key);
2775
2776 if ($locTypeId == 'Primary') {
2777 $locTypeId = $primaryLocationType;
2778 }
2779
2780 if (is_numeric($locTypeId)) {
2781 if (!in_array($locTypeId, $locationType)) {
2782 $locationType[$count] = $locTypeId;
2783 $count++;
2784 }
2785 $loc = CRM_Utils_Array::key($locTypeId, $locationType);
2786
2787 $data['location'][$loc]['location_type_id'] = $locTypeId;
2788
2789 // if we are getting in a new primary email, dont overwrite the new one
2790 if ($locTypeId == $primaryLocationType) {
a7488080 2791 if (!empty($params['email-' . $primaryLocationType])) {
6a488035
TO
2792 $data['location'][$loc]['email'][$loc]['email'] = $fields['email-' . $primaryLocationType];
2793 }
2794 elseif (isset($primaryEmail)) {
2795 $data['location'][$loc]['email'][$loc]['email'] = $primaryEmail;
2796 }
2797 $primaryLocation++;
2798 }
2799
2800 if ($loc == 1) {
2801 $data['location'][$loc]['is_primary'] = 1;
2802 }
2803 if ($fieldName == 'phone') {
2804 if ($phoneTypeId) {
2805 $data['location'][$loc]['phone'][$loc]['phone_type_id'] = $phoneTypeId;
2806 }
2807 else {
2808 $data['location'][$loc]['phone'][$loc]['phone_type_id'] = '';
2809 }
2810 $data['location'][$loc]['phone'][$loc]['phone'] = $value;
2811 }
2812 elseif ($fieldName == 'email') {
2813 $data['location'][$loc]['email'][$loc]['email'] = $value;
2814 }
2815 elseif ($fieldName == 'im') {
2816 $data['location'][$loc]['im'][$loc]['name'] = $value;
2817 }
2818 else {
2819 if ($fieldName === 'state_province') {
2820 $data['location'][$loc]['address']['state_province_id'] = $value;
2821 }
2822 elseif ($fieldName === 'country') {
2823 $data['location'][$loc]['address']['country_id'] = $value;
2824 }
2825 else {
2826 $data['location'][$loc]['address'][$fieldName] = $value;
2827 }
2828 }
2829 }
2830 else {
67744c4e 2831 // TODO: prefix, suffix and gender translation may no longer be necessary - check inputs
6a488035
TO
2832 if ($key === 'individual_suffix') {
2833 $data['suffix_id'] = $value;
2834 }
2835 elseif ($key === 'individual_prefix') {
2836 $data['prefix_id'] = $value;
2837 }
2838 elseif ($key === 'gender') {
2839 $data['gender_id'] = $value;
2840 }
2841 elseif (substr($key, 0, 6) === 'custom') {
2842 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
2843 //fix checkbox
2844 if ($customFields[$customFieldID]['html_type'] == 'CheckBox') {
2845 $value = implode(CRM_Core_DAO::VALUE_SEPARATOR, array_keys($value));
2846 }
2847 // fix the date field
2848 if ($customFields[$customFieldID]['data_type'] == 'Date') {
2849 $date = CRM_Utils_Date::format($value);
2850 if (!$date) {
2851 $date = '';
2852 }
2853 $value = $date;
2854 }
2855
a6a0cdb7 2856 $data['custom'][$customFieldID] = [
6a488035
TO
2857 'id' => $id,
2858 'value' => $value,
2859 'extends' => $customFields[$customFieldID]['extends'],
2860 'type' => $customFields[$customFieldID]['data_type'],
2861 'custom_field_id' => $customFieldID,
a6a0cdb7 2862 ];
6a488035
TO
2863 }
2864 }
2865 elseif ($key == 'edit') {
2866 continue;
2867 }
2868 else {
2869 $data[$key] = $value;
2870 }
2871 }
2872 }
2873
2874 if (!$primaryLocation) {
2875 $loc++;
2876 $data['location'][$loc]['email'][$loc]['email'] = $primaryEmail;
2877 }
2878
6a488035
TO
2879 return $data;
2880 }
2881
2882 /**
100fef9d 2883 * Calculate the profile type 'group_type' as per profile fields.
6a488035 2884 *
6a0b768e
TO
2885 * @param int $gId
2886 * Profile id.
1fd63589 2887 * @param bool $includeTypeValues
6a0b768e
TO
2888 * @param int $ignoreFieldId
2889 * Ignore particular profile field.
6a488035 2890 *
a6c01b45
CW
2891 * @return array
2892 * list of calculated group type
6a488035 2893 */
00be9182 2894 public static function calculateGroupType($gId, $includeTypeValues = FALSE, $ignoreFieldId = NULL) {
6a488035
TO
2895 //get the profile fields.
2896 $ufFields = self::getFields($gId, FALSE, NULL, NULL, NULL, TRUE, NULL, TRUE);
2897 return self::_calculateGroupType($ufFields, $includeTypeValues, $ignoreFieldId);
2898 }
2899
2900 /**
100fef9d 2901 * Calculate the profile type 'group_type' as per profile fields.
6a488035 2902 *
1fd63589
EM
2903 * @param $ufFields
2904 * @param bool $includeTypeValues
6a0b768e
TO
2905 * @param int $ignoreFieldId
2906 * Ignore perticular profile field.
6a488035 2907 *
a6c01b45
CW
2908 * @return array
2909 * list of calculated group type
6a488035 2910 */
00be9182 2911 public static function _calculateGroupType($ufFields, $includeTypeValues = FALSE, $ignoreFieldId = NULL) {
a6a0cdb7 2912 $groupType = $groupTypeValues = $customFieldIds = [];
6a488035
TO
2913 if (!empty($ufFields)) {
2914 foreach ($ufFields as $fieldName => $fieldValue) {
2915 //ignore field from group type when provided.
2916 //in case of update profile field.
2917 if ($ignoreFieldId && ($ignoreFieldId == $fieldValue['field_id'])) {
2918 continue;
2919 }
2920 if (!in_array($fieldValue['field_type'], $groupType)) {
2921 $groupType[$fieldValue['field_type']] = $fieldValue['field_type'];
2922 }
2923
2924 if ($includeTypeValues && ($fldId = CRM_Core_BAO_CustomField::getKeyID($fieldName))) {
2925 $customFieldIds[$fldId] = $fldId;
2926 }
2927 }
2928 }
2929
2930 if (!empty($customFieldIds)) {
2931 $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) . ')';
2932
2933 $customGroups = CRM_Core_DAO::executeQuery($query);
2934 while ($customGroups->fetch()) {
2935 if (!$customGroups->extends_entity_column_value) {
2936 continue;
2937 }
2938
2939 $groupTypeName = "{$customGroups->extends}Type";
2940 if ($customGroups->extends == 'Participant' && $customGroups->extends_entity_column_id) {
233319bf 2941 $groupTypeName = CRM_Core_PseudoConstant::getName('CRM_Core_DAO_CustomGroup', 'extends_entity_column_id', $customGroups->extends_entity_column_id);
6a488035
TO
2942 }
2943
2944 foreach (explode(CRM_Core_DAO::VALUE_SEPARATOR, $customGroups->extends_entity_column_value) as $val) {
2945 if ($val) {
2946 $groupTypeValues[$groupTypeName][$val] = $val;
2947 }
2948 }
2949 }
2950
2951 if (!empty($groupTypeValues)) {
2952 $groupType = array_merge($groupType, $groupTypeValues);
2953 }
2954 }
2955
2956 return $groupType;
2957 }
2958
2959 /**
2960 * Update the profile type 'group_type' as per profile fields including group types and group subtype values.
f0fed404 2961 * Build and store string like: group_type1,group_type2[VALUE_SEPARATOR]group_type1Type:1:2:3,group_type2Type:1:2
6a488035
TO
2962 *
2963 * FIELDS GROUP_TYPE
2964 * BirthDate + Email Individual,Contact
2965 * BirthDate + Subject Individual,Activity
2966 * BirthDate + Subject + SurveyOnlyField Individual,Activity\0ActivityType:28
2967 * BirthDate + Subject + SurveyOnlyField + PhoneOnlyField (Not allowed)
2968 * BirthDate + SurveyOnlyField Individual,Activity\0ActivityType:28
2969 * BirthDate + Subject + SurveyOrPhoneField Individual,Activity\0ActivityType:2:28
2970 * BirthDate + SurveyOrPhoneField Individual,Activity\0ActivityType:2:28
2971 * BirthDate + SurveyOrPhoneField + SurveyOnlyField Individual,Activity\0ActivityType:2:28
2972 * BirthDate + StudentField + Subject + SurveyOnlyField Individual,Activity,Student\0ActivityType:28
2973 *
c490a46a 2974 * @param int $gId
6a0b768e
TO
2975 * @param array $groupTypes
2976 * With key having group type names.
6a488035 2977 *
e7483cbe 2978 * @return bool
6a488035 2979 */
a6a0cdb7 2980 public static function updateGroupTypes($gId, $groupTypes = []) {
6a488035
TO
2981 if (!is_array($groupTypes) || !$gId) {
2982 return FALSE;
2983 }
2984
2985 // If empty group types set group_type as 'null'
2986 if (empty($groupTypes)) {
2987 return CRM_Core_DAO::setFieldValue('CRM_Core_DAO_UFGroup', $gId, 'group_type', 'null');
2988 }
2989
a6a0cdb7 2990 $componentGroupTypes = ['Contribution', 'Participant', 'Membership', 'Activity', 'Case'];
2991 $validGroupTypes = array_merge([
5d6ac993
TO
2992 'Contact',
2993 'Individual',
2994 'Organization',
21dfd5f5 2995 'Household',
a6a0cdb7 2996 ], $componentGroupTypes, CRM_Contact_BAO_ContactType::subTypes());
6a488035 2997
a6a0cdb7 2998 $gTypes = $gTypeValues = [];
6a488035 2999
a6a0cdb7 3000 $participantExtends = ['ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
6a488035
TO
3001 // Get valid group type and group subtypes
3002 foreach ($groupTypes as $groupType => $value) {
3003 if (in_array($groupType, $validGroupTypes) && !in_array($groupType, $gTypes)) {
3004 $gTypes[] = $groupType;
3005 }
3006
3007 $subTypesOf = NULL;
3008
3009 if (in_array($groupType, $participantExtends)) {
3010 $subTypesOf = $groupType;
3011 }
3012 elseif (strpos($groupType, 'Type') > 0) {
3013 $subTypesOf = substr($groupType, 0, strpos($groupType, 'Type'));
3014 }
3015 else {
3016 continue;
3017 }
3018
3019 if (!empty($value) &&
3020 (in_array($subTypesOf, $componentGroupTypes) ||
3021 in_array($subTypesOf, $participantExtends)
3022 )
3023 ) {
3024 $gTypeValues[$subTypesOf] = $groupType . ":" . implode(':', $value);
3025 }
3026 }
3027
3028 if (empty($gTypes)) {
3029 return FALSE;
3030 }
3031
3032 // Build String to store group types and group subtypes
3033 $groupTypeString = implode(',', $gTypes);
3034 if (!empty($gTypeValues)) {
3035 $groupTypeString .= CRM_Core_DAO::VALUE_SEPARATOR . implode(',', $gTypeValues);
3036 }
3037
3038 return CRM_Core_DAO::setFieldValue('CRM_Core_DAO_UFGroup', $gId, 'group_type', $groupTypeString);
3039 }
3040
3041 /**
fe482240 3042 * Create a "group_type" string.
6a488035 3043 *
6a0b768e
TO
3044 * @param array $coreTypes
3045 * E.g. array('Individual','Contact','Student').
3046 * @param array $subTypes
3047 * E.g. array('ActivityType' => array(7, 11)).
6a488035 3048 * @param string $delim
1fd63589
EM
3049 *
3050 * @return string
6a488035
TO
3051 * @throws CRM_Core_Exception
3052 */
00be9182 3053 public static function encodeGroupType($coreTypes, $subTypes, $delim = CRM_Core_DAO::VALUE_SEPARATOR) {
6a488035
TO
3054 $groupTypeExpr = '';
3055 if ($coreTypes) {
3056 $groupTypeExpr .= implode(',', $coreTypes);
3057 }
3058 if ($subTypes) {
99e239bc 3059 //CRM-15427 Allow Multiple subtype filtering
3060 //if (count($subTypes) > 1) {
5d6ac993 3061 //throw new CRM_Core_Exception("Multiple subtype filtering is not currently supported by widget.");
99e239bc 3062 //}
6a488035
TO
3063 foreach ($subTypes as $subType => $subTypeIds) {
3064 $groupTypeExpr .= $delim . $subType . ':' . implode(':', $subTypeIds);
3065 }
3066 }
3067 return $groupTypeExpr;
3068 }
3069
3070 /**
dc195289 3071 * setDefault componet specific profile fields.
6a488035 3072 *
6a0b768e
TO
3073 * @param array $fields
3074 * Profile fields.
3075 * @param int $componentId
3076 * ComponetID.
3077 * @param string $component
3078 * Component name.
3079 * @param array $defaults
3080 * An array of default values.
1fd63589
EM
3081 *
3082 * @param bool $isStandalone
6a488035 3083 */
f0385140 3084 public static function setComponentDefaults(&$fields, $componentId, $component, &$defaults, $isStandalone = FALSE) {
6a488035 3085 if (!$componentId ||
a6a0cdb7 3086 !in_array($component, ['Contribute', 'Membership', 'Event', 'Activity', 'Case'])
6a488035
TO
3087 ) {
3088 return;
3089 }
3090
3091 $componentBAO = $componentSubType = NULL;
3092 switch ($component) {
3093 case 'Membership':
5d6ac993 3094 $componentBAO = 'CRM_Member_BAO_Membership';
6a488035 3095 $componentBAOName = 'Membership';
a6a0cdb7 3096 $componentSubType = ['membership_type_id'];
6a488035
TO
3097 break;
3098
3099 case 'Contribute':
5d6ac993 3100 $componentBAO = 'CRM_Contribute_BAO_Contribution';
6a488035 3101 $componentBAOName = 'Contribution';
a6a0cdb7 3102 $componentSubType = ['financial_type_id'];
6a488035
TO
3103 break;
3104
3105 case 'Event':
5d6ac993 3106 $componentBAO = 'CRM_Event_BAO_Participant';
6a488035 3107 $componentBAOName = 'Participant';
a6a0cdb7 3108 $componentSubType = ['role_id', 'event_id', 'event_type_id'];
6a488035
TO
3109 break;
3110
3111 case 'Activity':
5d6ac993 3112 $componentBAO = 'CRM_Activity_BAO_Activity';
6a488035 3113 $componentBAOName = 'Activity';
a6a0cdb7 3114 $componentSubType = ['activity_type_id'];
6a488035 3115 break;
888da08c
MW
3116
3117 case 'Case':
3118 $componentBAO = 'CRM_Case_BAO_Case';
3119 $componentBAOName = 'Case';
a6a0cdb7 3120 $componentSubType = ['case_type_id'];
888da08c 3121 break;
6a488035
TO
3122 }
3123
a6a0cdb7 3124 $values = [];
3125 $params = ['id' => $componentId];
6a488035
TO
3126
3127 //get the component values.
3128 CRM_Core_DAO::commonRetrieve($componentBAO, $params, $values);
d623de42 3129 if ($componentBAOName == 'Participant') {
a6a0cdb7 3130 $values += ['event_type_id' => CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $values['event_id'], 'event_type_id')];
d623de42 3131 }
6a488035 3132
a6a0cdb7 3133 $formattedGroupTree = [];
a86839e7 3134
6a488035
TO
3135 foreach ($fields as $name => $field) {
3136 $fldName = $isStandalone ? $name : "field[$componentId][$name]";
3d927ee7 3137 if (array_key_exists($name, $values)) {
6a488035
TO
3138 $defaults[$fldName] = $values[$name];
3139 }
3140 elseif ($name == 'participant_note') {
5d6ac993 3141 $noteDetails = CRM_Core_BAO_Note::getNote($componentId, 'civicrm_participant');
6a488035
TO
3142 $defaults[$fldName] = array_pop($noteDetails);
3143 }
a6a0cdb7 3144 elseif (in_array($name, [
5d6ac993
TO
3145 'financial_type',
3146 'payment_instrument',
3147 'participant_status',
21dfd5f5 3148 'participant_role',
a6a0cdb7 3149 ])) {
6a488035
TO
3150 $defaults[$fldName] = $values["{$name}_id"];
3151 }
3152 elseif ($name == 'membership_type') {
3153 // since membership_type field is a hierselect -
e7483cbe
J
3154 $defaults[$fldName][0]
3155 = CRM_Core_DAO::getFieldValue('CRM_Member_DAO_MembershipType', $values['membership_type_id'], 'member_of_contact_id', 'id');
6a488035
TO
3156 $defaults[$fldName][1] = $values['membership_type_id'];
3157 }
3158 elseif ($name == 'membership_status') {
3159 $defaults[$fldName] = $values['status_id'];
3160 }
888da08c
MW
3161 elseif ($name == 'case_status') {
3162 $defaults[$fldName] = $values['case_status_id'];
3163 }
a6a0cdb7 3164 elseif (CRM_Core_BAO_CustomField::getKeyID($name, TRUE) !== [NULL, NULL]) {
6a488035
TO
3165 if (empty($formattedGroupTree)) {
3166 //get the groupTree as per subTypes.
a6a0cdb7 3167 $groupTree = [];
6a488035 3168 foreach ($componentSubType as $subType) {
0b330e6d 3169 $subTree = CRM_Core_BAO_CustomGroup::getTree($componentBAOName, NULL,
6a488035
TO
3170 $componentId, 0, $values[$subType]
3171 );
3172 $groupTree = CRM_Utils_Array::crmArrayMerge($groupTree, $subTree);
3173 }
1273d77c 3174 $formattedGroupTree = CRM_Core_BAO_CustomGroup::formatGroupTree($groupTree, 1);
6a488035
TO
3175 CRM_Core_BAO_CustomGroup::setDefaults($formattedGroupTree, $defaults);
3176 }
3177
3178 //FIX ME: We need to loop defaults, but once we move to custom_1_x convention this code can be simplified.
3179 foreach ($defaults as $customKey => $customValue) {
3180 if ($customFieldDetails = CRM_Core_BAO_CustomField::getKeyID($customKey, TRUE)) {
3181 if ($name == 'custom_' . $customFieldDetails[0]) {
3182
3183 //hack to set default for checkbox
3184 //basically this is for weired field name like field[33][custom_19]
3185 //we are converting this field name to array structure and assign value.
3186 $skipValue = FALSE;
3187
3188 foreach ($formattedGroupTree as $tree) {
d623de42
M
3189 if (!empty($tree['fields'][$customFieldDetails[0]])) {
3190 if ('CheckBox' == CRM_Utils_Array::value('html_type', $tree['fields'][$customFieldDetails[0]])) {
3191 $skipValue = TRUE;
3192 $defaults['field'][$componentId][$name] = $customValue;
3193 break;
3194 }
3195 elseif (CRM_Utils_Array::value('data_type', $tree['fields'][$customFieldDetails[0]]) == 'Date') {
3196 $skipValue = TRUE;
6a488035 3197
d623de42
M
3198 // CRM-6681, $default contains formatted date, time values.
3199 $defaults[$fldName] = $customValue;
3200 if (!empty($defaults[$customKey . '_time'])) {
3201 $defaults['field'][$componentId][$name . '_time'] = $defaults[$customKey . '_time'];
3202 }
6a488035
TO
3203 }
3204 }
3205 }
3206
3207 if (!$skipValue || $isStandalone) {
3208 $defaults[$fldName] = $customValue;
3209 }
3210 unset($defaults[$customKey]);
3211 break;
3212 }
3213 }
3214 }
3215 }
04b40eab 3216 elseif (isset($values[$fldName])) {
3217 $defaults[$fldName] = $values[$fldName];
3218 }
6a488035
TO
3219 }
3220 }
3221
6a488035 3222 /**
fe482240 3223 * Retrieve groups of profiles.
6a488035 3224 *
6a0b768e
TO
3225 * @param int $profileID
3226 * Id of the profile.
6a488035 3227 *
a6c01b45
CW
3228 * @return array
3229 * returns array
6a488035 3230 */
00be9182 3231 public static function profileGroups($profileID) {
a6a0cdb7 3232 $groupTypes = [];
6a488035
TO
3233 $profileTypes = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $profileID, 'group_type');
3234 if ($profileTypes) {
3235 $groupTypeParts = explode(CRM_Core_DAO::VALUE_SEPARATOR, $profileTypes);
3236 $groupTypes = explode(',', $groupTypeParts[0]);
3237 }
3238 return $groupTypes;
3239 }
3240
3241 /**
100fef9d 3242 * Alter contact params by filtering existing subscribed groups and returns
6a488035
TO
3243 * unsubscribed groups array for subscription.
3244 *
6a0b768e
TO
3245 * @param array $params
3246 * Contact params.
3247 * @param int $contactId
3248 * User contact id.
6a488035 3249 *
a6c01b45
CW
3250 * @return array
3251 * This contains array of groups for subscription
6a488035 3252 */
00be9182 3253 public static function getDoubleOptInGroupIds(&$params, $contactId = NULL) {
6a488035 3254 $config = CRM_Core_Config::singleton();
a6a0cdb7 3255 $subscribeGroupIds = [];
6a488035
TO
3256
3257 // process further only if profileDoubleOptIn enabled and if groups exist
3258 if (!array_key_exists('group', $params) ||
3259 !self::isProfileDoubleOptin() ||
3260 CRM_Utils_System::isNull($params['group'])
3261 ) {
3262 return $subscribeGroupIds;
3263 }
3264
3265 //check if contact email exist.
3266 $hasEmails = FALSE;
3267 foreach ($params as $name => $value) {
3268 if (strpos($name, 'email-') !== FALSE) {
3269 $hasEmails = TRUE;
3270 break;
3271 }
3272 }
3273
3274 //Proceed furthur only if email present
3275 if (!$hasEmails) {
3276 return $subscribeGroupIds;
3277 }
3278
3279 //do check for already subscriptions.
a6a0cdb7 3280 $contactGroups = [];
6a488035
TO
3281 if ($contactId) {
3282 $query = "
3283SELECT group_id
3284 FROM civicrm_group_contact
3285 WHERE status = 'Added'
3286 AND contact_id = %1";
3287
a6a0cdb7 3288 $dao = CRM_Core_DAO::executeQuery($query, [1 => [$contactId, 'Integer']]);
6a488035
TO
3289 while ($dao->fetch()) {
3290 $contactGroups[$dao->group_id] = $dao->group_id;
3291 }
3292 }
3293
3294 //since we don't have names, compare w/ label.
3295 $mailingListGroupType = array_search('Mailing List', CRM_Core_OptionGroup::values('group_type'));
3296
3297 //actual processing start.
3298 foreach ($params['group'] as $groupId => $isSelected) {
3299 //unset group those are not selected.
3300 if (!$isSelected) {
3301 unset($params['group'][$groupId]);
3302 continue;
3303 }
3304
3305 $groupTypes = explode(CRM_Core_DAO::VALUE_SEPARATOR,
3306 CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $groupId, 'group_type', 'id')
3307 );
3308 //get only mailing type group and unset it from params
3309 if (in_array($mailingListGroupType, $groupTypes) && !in_array($groupId, $contactGroups)) {
3310 $subscribeGroupIds[$groupId] = $groupId;
3311 unset($params['group'][$groupId]);
3312 }
3313 }
3314
3315 return $subscribeGroupIds;
3316 }
3317
3318 /**
fe482240 3319 * Check if we are rendering mixed profiles.
6a488035 3320 *
6a0b768e
TO
3321 * @param array $profileIds
3322 * Associated array of profile ids.
6a488035 3323 *
e7483cbe 3324 * @return bool
a6c01b45 3325 * true if profile is mixed
6a488035 3326 */
00be9182 3327 public static function checkForMixProfiles($profileIds) {
6a488035
TO
3328 $mixProfile = FALSE;
3329
a6a0cdb7 3330 $contactTypes = ['Individual', 'Household', 'Organization'];
6a488035
TO
3331 $subTypes = CRM_Contact_BAO_ContactType::subTypes();
3332
a6a0cdb7 3333 $components = ['Contribution', 'Participant', 'Membership', 'Activity'];
6a488035 3334
a6a0cdb7 3335 $typeCount = ['ctype' => [], 'subtype' => []];
6a488035
TO
3336 foreach ($profileIds as $gid) {
3337 $profileType = CRM_Core_BAO_UFField::getProfileType($gid);
3338 // ignore profile of type Contact
3339 if ($profileType == 'Contact') {
3340 continue;
3341 }
3342 if (in_array($profileType, $contactTypes)) {
3343 if (!isset($typeCount['ctype'][$profileType])) {
3344 $typeCount['ctype'][$profileType] = 1;
3345 }
3346
3347 // check if we are rendering profile of different contact types
3348 if (count($typeCount['ctype']) == 2) {
3349 $mixProfile = TRUE;
3350 break;
3351 }
3352 }
3353 elseif (in_array($profileType, $components)) {
3354 $mixProfile = TRUE;
3355 break;
3356 }
3357 else {
3358 if (!isset($typeCount['subtype'][$profileType])) {
3359 $typeCount['subtype'][$profileType] = 1;
3360 }
3361 // check if we are rendering profile of different contact sub types
3362 if (count($typeCount['subtype']) == 2) {
3363 $mixProfile = TRUE;
3364 break;
3365 }
3366 }
3367 }
3368 return $mixProfile;
3369 }
3370
3371 /**
fe482240 3372 * Determine of we show overlay profile or not.
6a488035 3373 *
e7483cbe 3374 * @return bool
a6c01b45 3375 * true if profile should be shown else false
6a488035 3376 */
00be9182 3377 public static function showOverlayProfile() {
6a488035
TO
3378 $showOverlay = TRUE;
3379
3380 // get the id of overlay profile
3381 $overlayProfileId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', 'summary_overlay', 'id', 'name');
3382 $query = "SELECT count(id) FROM civicrm_uf_field WHERE uf_group_id = {$overlayProfileId} AND visibility IN ('Public Pages', 'Public Pages and Listings') ";
3383
3384 $count = CRM_Core_DAO::singleValueQuery($query);
3385
3386 //check if there are no public fields and use is anonymous
3387 $session = CRM_Core_Session::singleton();
3388 if (!$count && !$session->get('userID')) {
3389 $showOverlay = FALSE;
3390 }
3391
3392 return $showOverlay;
3393 }
3394
3395 /**
fe482240 3396 * Get group type values of the profile.
6a488035 3397 *
c490a46a
CW
3398 * @param int $profileId
3399 * @param string $groupType
1fd63589 3400 *
e7483cbe 3401 * @return array
a6c01b45 3402 * group type values
6a488035 3403 */
00be9182 3404 public static function groupTypeValues($profileId, $groupType = NULL) {
a6a0cdb7 3405 $groupTypeValue = [];
6a488035
TO
3406 $groupTypes = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $profileId, 'group_type');
3407
3408 $groupTypeParts = explode(CRM_Core_DAO::VALUE_SEPARATOR, $groupTypes);
a7488080 3409 if (empty($groupTypeParts[1])) {
6a488035
TO
3410 return $groupTypeValue;
3411 }
a6a0cdb7 3412 $participantExtends = ['ParticipantRole', 'ParticipantEventName', 'ParticipantEventType'];
6a488035
TO
3413
3414 foreach (explode(',', $groupTypeParts[1]) as $groupTypeValues) {
a6a0cdb7 3415 $values = [];
6a488035
TO
3416 $valueParts = explode(':', $groupTypeValues);
3417 if ($groupType &&
3418 ($valueParts[0] != "{$groupType}Type" ||
3419 ($groupType == 'Participant' &&
3420 !in_array($valueParts[0], $participantExtends)
3421 )
3422 )
3423 ) {
3424 continue;
3425 }
3426 foreach ($valueParts as $val) {
3427 if (CRM_Utils_Rule::integer($val)) {
3428 $values[$val] = $val;
3429 }
3430 }
3431 if (!empty($values)) {
3432 $typeName = substr($valueParts[0], 0, -4);
3433 if (in_array($valueParts[0], $participantExtends)) {
3434 $typeName = $valueParts[0];
3435 }
3436 $groupTypeValue[$typeName] = $values;
3437 }
3438 }
3439
3440 return $groupTypeValue;
3441 }
3442
b5c2afd0
EM
3443 /**
3444 * @return bool|object
3445 */
00be9182 3446 public static function isProfileDoubleOptin() {
6a488035
TO
3447 // check for double optin
3448 $config = CRM_Core_Config::singleton();
3449 if (in_array('CiviMail', $config->enableComponents)) {
aaffa79f 3450 return Civi::settings()->get('profile_double_optin');
6a488035
TO
3451 }
3452 return FALSE;
3453 }
3454
b5c2afd0
EM
3455 /**
3456 * @return bool|object
3457 */
00be9182 3458 public static function isProfileAddToGroupDoubleOptin() {
6a488035
TO
3459 // check for add to group double optin
3460 $config = CRM_Core_Config::singleton();
3461 if (in_array('CiviMail', $config->enableComponents)) {
aaffa79f 3462 return Civi::settings()->get('profile_add_to_group_double_optin');
6a488035
TO
3463 }
3464 return FALSE;
3465 }
3466
3467 /**
fe482240 3468 * Get profiles used for batch entry.
6a488035 3469 *
a6c01b45
CW
3470 * @return array
3471 * profileIds profile ids
6a488035 3472 */
00be9182 3473 public static function getBatchProfiles() {
6a488035
TO
3474 $query = "SELECT id
3475 FROM civicrm_uf_group
3476 WHERE name IN ('contribution_batch_entry', 'membership_batch_entry')";
5d6ac993 3477 $dao = CRM_Core_DAO::executeQuery($query);
a6a0cdb7 3478 $profileIds = [];
5d6ac993 3479 while ($dao->fetch()) {
6a488035
TO
3480 $profileIds[$dao->id] = $dao->id;
3481 }
3482 return $profileIds;
3483 }
3484
1fd63589 3485 /**
1fd63589
EM
3486 * @param $source
3487 * @param $destination
3488 * @param bool $returnMultiSummaryFields
3489 *
3490 * @return array|null
a6a0cdb7 3491 * @todo what do I do?
1fd63589 3492 */
00be9182 3493 public static function shiftMultiRecordFields(&$source, &$destination, $returnMultiSummaryFields = FALSE) {
a6a0cdb7 3494 $multiSummaryFields = $returnMultiSummaryFields ? [] : NULL;
6a488035
TO
3495 foreach ($source as $field => $properties) {
3496 if (!CRM_Core_BAO_CustomField::getKeyID($field)) {
3497 continue;
3498 }
3499 if (CRM_Core_BAO_CustomField::isMultiRecordField($field)) {
3500 $destination[$field] = $properties;
3501 if ($returnMultiSummaryFields) {
3502 if ($properties['is_multi_summary']) {
3503 $multiSummaryFields[$field] = $properties;
3504 }
3505 }
3506 unset($source[$field]);
3507 }
3508 }
3509 return $multiSummaryFields;
3510 }
3511
3512 /**
fe482240 3513 * This is function is used to format pseudo fields.
6a488035 3514 *
6a0b768e
TO
3515 * @param array $fields
3516 * Associated array of profile fields.
6a488035 3517 *
6a488035 3518 */
00be9182 3519 public static function reformatProfileFields(&$fields) {
6a488035
TO
3520 //reformat fields array
3521 foreach ($fields as $name => $field) {
3522 //reformat phone and extension field
5d6ac993 3523 if (substr($field['name'], 0, 13) == 'phone_and_ext') {
6a488035
TO
3524 $fieldSuffix = str_replace('phone_and_ext-', '', $field['name']);
3525
3526 // retain existing element properties and just update and replace key
3527 CRM_Utils_Array::crmReplaceKey($fields, $name, "phone-{$fieldSuffix}");
3528 $fields["phone-{$fieldSuffix}"]['name'] = "phone-{$fieldSuffix}";
3529 $fields["phone-{$fieldSuffix}"]['where'] = 'civicrm_phone.phone';
3530
3531 // add additional phone extension field
3532 $fields["phone_ext-{$fieldSuffix}"] = $field;
5d6ac993 3533 $fields["phone_ext-{$fieldSuffix}"]['title'] = $field['title'] . ' - ' . ts('Ext.');
6a488035
TO
3534 $fields["phone_ext-{$fieldSuffix}"]['name'] = "phone_ext-{$fieldSuffix}";
3535 $fields["phone_ext-{$fieldSuffix}"]['where'] = 'civicrm_phone.phone_ext';
3536 $fields["phone_ext-{$fieldSuffix}"]['skipDisplay'] = 1;
3537 //ignore required for extension field
3538 $fields["phone_ext-{$fieldSuffix}"]['is_required'] = 0;
3539 }
3540 }
3541 }
96025800 3542
ed7e2e99 3543 /**
3544 * Get the frontend_title for the profile, falling back on 'title' if none.
3545 *
3546 * @param int $profileID
3547 *
3548 * @return string
3549 *
3550 * @throws \CiviCRM_API3_Exception
3551 */
3552 public static function getFrontEndTitle(int $profileID) {
3553 $profile = civicrm_api3('UFGroup', 'getsingle', ['id' => $profileID, 'return' => ['title', 'frontend_title']]);
3554 return $profile['frontend_title'] ?? $profile['title'];
3555 }
3556
cd48635d 3557 /**
d4502c25 3558 * Format custom field value for use in prepopulating a quickform profile field.
cd48635d
VP
3559 *
3560 * @param array $field
d4502c25 3561 * Field metadata.
cd48635d 3562 * @param string $value
d4502c25 3563 * Raw value
cd48635d 3564 *
d4502c25 3565 * @return mixed
cd48635d
VP
3566 * String or array, depending on the html type
3567 */
d4502c25
CW
3568 private static function formatCustomValue($field, $value) {
3569 if (CRM_Core_BAO_CustomField::isSerialized($field)) {
3570 $value = CRM_Utils_Array::explodePadded($value);
cd48635d 3571
f1d04518 3572 // This may not be required now.
d4502c25
CW
3573 if ($field['html_type'] === 'CheckBox') {
3574 $checkboxes = [];
3575 foreach (array_filter($value) as $item) {
3576 $checkboxes[$item] = 1;
3577 // CRM-2969 seems like we need this for QF style checkboxes in profile where its multiindexed
3578 $checkboxes["[{$item}]"] = 1;
cd48635d 3579 }
d4502c25
CW
3580 return $checkboxes;
3581 }
cd48635d 3582 }
d4502c25 3583 return $value;
cd48635d
VP
3584 }
3585
6a488035 3586}