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