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