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