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