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