(NFC) Update CRM/Event folder for the new coder style
[civicrm-core.git] / CRM / Event / Form / ManageEvent / Registration.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 * @package CRM
30 * @copyright CiviCRM LLC (c) 2004-2019
31 */
32
33 /**
34 * This class generates form components for processing Event.
35 */
36 class CRM_Event_Form_ManageEvent_Registration extends CRM_Event_Form_ManageEvent {
37
38 /**
39 * What blocks should we show and hide.
40 *
41 * @var CRM_Core_ShowHideBlocks
42 */
43 protected $_showHide;
44
45 protected $_profilePostMultiple = [];
46 protected $_profilePostMultipleAdd = [];
47
48 /**
49 * Set variables up before form is built.
50 */
51 public function preProcess() {
52 $this->_addProfileBottom = CRM_Utils_Array::value('addProfileBottom', $_GET, FALSE);
53 $this->_profileBottomNum = CRM_Utils_Array::value('addProfileNum', $_GET, 0);
54 $this->_addProfileBottomAdd = CRM_Utils_Array::value('addProfileBottomAdd', $_GET, FALSE);
55 $this->_profileBottomNumAdd = CRM_Utils_Array::value('addProfileNumAdd', $_GET, 0);
56
57 parent::preProcess();
58 $this->assign('selectedChild', 'registration');
59
60 $this->assign('addProfileBottom', $this->_addProfileBottom);
61 $this->assign('profileBottomNum', $this->_profileBottomNum);
62
63 $urlParams = "id={$this->_id}&addProfileBottom=1&qfKey={$this->controller->_key}";
64 $this->assign('addProfileParams', $urlParams);
65
66 if ($addProfileBottom = CRM_Utils_Array::value('custom_post_id_multiple', $_POST)) {
67 foreach (array_keys($addProfileBottom) as $profileNum) {
68 self::buildMultipleProfileBottom($this, $profileNum);
69 }
70 }
71
72 $this->assign('addProfileBottomAdd', $this->_addProfileBottomAdd);
73 $this->assign('profileBottomNumAdd', $this->_profileBottomNumAdd);
74
75 $urlParamsAdd = "id={$this->_id}&addProfileBottomAdd=1&qfKey={$this->controller->_key}";
76 $this->assign('addProfileParamsAdd', $urlParamsAdd);
77
78 if ($addProfileBottomAdd = CRM_Utils_Array::value('additional_custom_post_id_multiple', $_POST)) {
79 foreach (array_keys($addProfileBottomAdd) as $profileNum) {
80 self::buildMultipleProfileBottom($this, $profileNum, 'additional_', ts('Profile for Additional Participants'));
81 }
82 }
83 }
84
85 /**
86 * Set default values for the form.
87 *
88 * The default values are retrieved from the database.
89 */
90 public function setDefaultValues() {
91 if ($this->_addProfileBottom || $this->_addProfileBottomAdd) {
92 return;
93 }
94 $eventId = $this->_id;
95
96 $defaults = parent::setDefaultValues();
97
98 $this->setShowHide($defaults);
99 if (isset($eventId)) {
100 $params = ['id' => $eventId];
101 CRM_Event_BAO_Event::retrieve($params, $defaults);
102
103 $ufJoinParams = [
104 'entity_table' => 'civicrm_event',
105 'module' => 'CiviEvent',
106 'entity_id' => $eventId,
107 ];
108
109 list($defaults['custom_pre_id'],
110 $defaults['custom_post']
111 ) = CRM_Core_BAO_UFJoin::getUFGroupIds($ufJoinParams);
112
113 // Get the id for the event registration profile
114 $eventRegistrationIdParams = $eventRegistrationIdDefaults = [
115 'name' => 'event_registration',
116 ];
117 CRM_Core_BAO_UFGroup::retrieve($eventRegistrationIdParams, $eventRegistrationIdDefaults);
118
119 // Set event registration as the default profile if none selected
120 if (!$defaults['custom_pre_id'] && count($defaults['custom_post']) == 0) {
121 $defaults['custom_pre_id'] = CRM_Utils_Array::value('id', $eventRegistrationIdDefaults);
122 }
123 if (isset($defaults['custom_post']) && is_numeric($defaults['custom_post'])) {
124 $defaults['custom_post_id'] = $defaults['custom_post'];
125 }
126 elseif (!empty($defaults['custom_post'])) {
127 $defaults['custom_post_id'] = $defaults['custom_post'][0];
128 unset($defaults['custom_post'][0]);
129 $this->_profilePostMultiple = $defaults['custom_post'];
130 foreach ($defaults['custom_post'] as $key => $value) {
131 self::buildMultipleProfileBottom($this, $key);
132 $defaults["custom_post_id_multiple[$key]"] = $value;
133 }
134 }
135
136 $this->assign('profilePostMultiple', CRM_Utils_Array::value('custom_post', $defaults));
137
138 // CRM-17745: Make max additional participants configurable
139 if (empty($defaults['max_additional_participants'])) {
140 $defaults['max_additional_participants'] = 9;
141 }
142
143 if (!empty($defaults['is_multiple_registrations'])) {
144 // CRM-4377: set additional participants’ profiles – set to ‘none’ if explicitly unset (non-active)
145
146 $ufJoinAddParams = [
147 'entity_table' => 'civicrm_event',
148 'module' => 'CiviEvent_Additional',
149 'entity_id' => $eventId,
150 ];
151
152 list($defaults['additional_custom_pre_id'],
153 $defaults['additional_custom_post']
154 ) = CRM_Core_BAO_UFJoin::getUFGroupIds($ufJoinAddParams);
155
156 if (isset($defaults['additional_custom_post']) && is_numeric($defaults['additional_custom_post'])) {
157 $defaults['additional_custom_post_id'] = $defaults['additional_custom_post'];
158 }
159 elseif (!empty($defaults['additional_custom_post'])) {
160 $defaults['additional_custom_post_id'] = $defaults['additional_custom_post'][0];
161 unset($defaults['additional_custom_post'][0]);
162
163 $this->_profilePostMultipleAdd = $defaults['additional_custom_post'];
164 foreach ($defaults['additional_custom_post'] as $key => $value) {
165 self::buildMultipleProfileBottom($this, $key, 'additional_', ts('Profile for Additional Participants'));
166 $defaults["additional_custom_post_id_multiple[$key]"] = $value;
167 }
168 }
169 $this->assign('profilePostMultipleAdd', CRM_Utils_Array::value('additional_custom_post', $defaults));
170 }
171 }
172 else {
173 $defaults['is_email_confirm'] = 0;
174 }
175
176 // provide defaults for required fields if empty (and as a 'hint' for approval message field)
177 $defaults['registration_link_text'] = CRM_Utils_Array::value('registration_link_text', $defaults, ts('Register Now'));
178 $defaults['confirm_title'] = CRM_Utils_Array::value('confirm_title', $defaults, ts('Confirm Your Registration Information'));
179 $defaults['thankyou_title'] = CRM_Utils_Array::value('thankyou_title', $defaults, ts('Thank You for Registering'));
180 $defaults['approval_req_text'] = CRM_Utils_Array::value('approval_req_text', $defaults, ts('Participation in this event requires approval. Submit your registration request here. Once approved, you will receive an email with a link to a web page where you can complete the registration process.'));
181
182 return $defaults;
183 }
184
185 /**
186 * Fix what blocks to show/hide based on the default values set
187 *
188 * @param array $defaults
189 * The array of default values.
190 *
191 * @return void
192 */
193 public function setShowHide($defaults) {
194 $this->_showHide = new CRM_Core_ShowHideBlocks(['registration' => 1],
195 ''
196 );
197 if (empty($defaults)) {
198 $this->_showHide->addHide('registration');
199 $this->_showHide->addHide('id-approval-text');
200 }
201 else {
202 if (empty($defaults['requires_approval'])) {
203 $this->_showHide->addHide('id-approval-text');
204 }
205 }
206 $this->assign('defaultsEmpty', empty($defaults));
207 $this->_showHide->addToTemplate();
208 }
209
210 /**
211 * Build the form object.
212 *
213 * @return void
214 */
215 public function buildQuickForm() {
216 if ($this->_addProfileBottom) {
217 return self::buildMultipleProfileBottom($this, $this->_profileBottomNum);
218 }
219
220 if ($this->_addProfileBottomAdd) {
221 return self::buildMultipleProfileBottom($this, $this->_profileBottomNumAdd, 'additional_', ts('Profile for Additional Participants'));
222 }
223
224 $this->applyFilter('__ALL__', 'trim');
225 $attributes = CRM_Core_DAO::getAttribute('CRM_Event_DAO_Event');
226
227 $this->addElement('checkbox',
228 'is_online_registration',
229 ts('Allow Online Registration'),
230 NULL,
231 [
232 'onclick' => "return showHideByValue('is_online_registration'," .
233 "''," .
234 "'registration_blocks'," .
235 "'block'," .
236 "'radio'," .
237 "false );",
238 ]
239 );
240
241 $this->add('text', 'registration_link_text', ts('Registration Link Text'));
242
243 if (!$this->_isTemplate) {
244 $this->add('datepicker', 'registration_start_date', ts('Registration Start Date'), [], FALSE, ['time' => TRUE]);
245 $this->add('datepicker', 'registration_end_date', ts('Registration End Date'), [], FALSE, ['time' => TRUE]);
246 }
247
248 $params = [
249 'used' => 'Supervised',
250 'contact_type' => 'Individual',
251 ];
252 $dedupeRuleFields = CRM_Dedupe_BAO_Rule::dedupeRuleFields($params);
253
254 foreach ($dedupeRuleFields as $key => $fields) {
255 $ruleFields[$key] = ucwords(str_replace('_', ' ', $fields));
256 }
257
258 $this->addElement('checkbox',
259 'is_multiple_registrations',
260 ts('Register multiple participants?')
261 );
262
263 // CRM-17745: Make maximum additional participants configurable
264 $numericOptions = CRM_Core_SelectValues::getNumericOptions(1, 9);
265 $this->add('select', 'max_additional_participants', ts('Maximum additional participants'), $numericOptions, FALSE, ['class' => 'required']);
266
267 $this->addElement('checkbox',
268 'allow_same_participant_emails',
269 ts('Same email address?')
270 );
271 $this->assign('ruleFields', json_encode($ruleFields));
272
273 $dedupeRules = [
274 '' => '- Unsupervised rule -',
275 ];
276 $dedupeRules += CRM_Dedupe_BAO_RuleGroup::getByType('Individual');
277 $this->add('select', 'dedupe_rule_group_id', ts('Duplicate matching rule'), $dedupeRules);
278
279 $participantStatuses = CRM_Event_PseudoConstant::participantStatus();
280 if (in_array('Awaiting approval', $participantStatuses) and in_array('Pending from approval', $participantStatuses) and in_array('Rejected', $participantStatuses)) {
281 $this->addElement('checkbox',
282 'requires_approval',
283 ts('Require participant approval?'),
284 NULL,
285 ['onclick' => "return showHideByValue('requires_approval', '', 'id-approval-text', 'table-row', 'radio', false);"]
286 );
287 $this->add('textarea', 'approval_req_text', ts('Approval message'), $attributes['approval_req_text']);
288 }
289
290 $this->add('text', 'expiration_time', ts('Pending participant expiration (hours)'));
291 $this->addRule('expiration_time', ts('Please enter the number of hours (as an integer).'), 'integer');
292 $this->addField('allow_selfcancelxfer', ['label' => ts('Allow self-service cancellation or transfer?'), 'type' => 'advcheckbox']);
293 $this->add('text', 'selfcancelxfer_time', ts('Cancellation or transfer time limit (hours)'));
294 $this->addRule('selfcancelxfer_time', ts('Please enter the number of hours (as an integer).'), 'integer');
295 self::buildRegistrationBlock($this);
296 self::buildConfirmationBlock($this);
297 self::buildMailBlock($this);
298 self::buildThankYouBlock($this);
299
300 parent::buildQuickForm();
301 }
302
303 /**
304 * Build Registration Block.
305 *
306 * @param CRM_Core_Form $form
307 *
308 */
309 public function buildRegistrationBlock(&$form) {
310 $attributes = CRM_Core_DAO::getAttribute('CRM_Event_DAO_Event', 'intro_text') + ['class' => 'collapsed', 'preset' => 'civievent'];
311 $form->add('wysiwyg', 'intro_text', ts('Introductory Text'), $attributes);
312 $form->add('wysiwyg', 'footer_text', ts('Footer Text'), $attributes);
313
314 extract(self::getProfileSelectorTypes());
315 //CRM-15427
316 $form->addProfileSelector('custom_pre_id', ts('Include Profile') . '<br />' . ts('(top of page)'), $allowCoreTypes, $allowSubTypes, $profileEntities, TRUE, $usedFor);
317 $form->addProfileSelector('custom_post_id', ts('Include Profile') . '<br />' . ts('(bottom of page)'), $allowCoreTypes, $allowSubTypes, $profileEntities, TRUE, $usedFor);
318
319 $form->addProfileSelector('additional_custom_pre_id', ts('Profile for Additional Participants') . '<br />' . ts('(top of page)'), $allowCoreTypes, $allowSubTypes, $profileEntities, TRUE, $usedFor);
320 $form->addProfileSelector('additional_custom_post_id', ts('Profile for Additional Participants') . '<br />' . ts('(bottom of page)'), $allowCoreTypes, $allowSubTypes, $profileEntities, TRUE, $usedFor);
321 }
322
323 /**
324 * Subroutine to insert a Profile Editor widget.
325 * depends on getProfileSelectorTypes
326 *
327 * @param array &$form
328 * @param int $count
329 * Unique index.
330 * @param string $prefix
331 * Dom element ID prefix.
332 * @param string $label
333 * Label.
334 * @param array $configs
335 * Optional, for addProfileSelector(), defaults to using getProfileSelectorTypes().
336 */
337 public function buildMultipleProfileBottom(&$form, $count, $prefix = '', $label = 'Include Profile', $configs = NULL) {
338 extract((is_null($configs)) ? self::getProfileSelectorTypes() : $configs);
339 $element = $prefix . "custom_post_id_multiple[$count]";
340 $label .= '<br />' . ts('(bottom of page)');
341 $form->addProfileSelector($element, $label, $allowCoreTypes, $allowSubTypes, $profileEntities, TRUE, $usedFor);
342 }
343
344 /**
345 * Create initializers for addprofileSelector.
346 *
347 * @return array
348 * ['allowCoreTypes' => array, 'allowSubTypes' => array, 'profileEntities' => array]
349 */
350 public static function getProfileSelectorTypes() {
351 $configs = [
352 'allowCoreTypes' => [],
353 'allowSubTypes' => [],
354 'profileEntities' => [],
355 'usedFor' => [],
356 ];
357
358 $configs['allowCoreTypes'] = array_merge([
359 'Contact',
360 'Individual',
361 ], CRM_Contact_BAO_ContactType::subTypes('Individual'));
362 $configs['allowCoreTypes'][] = 'Participant';
363 if (CRM_Core_Permission::check('manage event profiles') && !CRM_Core_Permission::check('administer CiviCRM')) {
364 $configs['usedFor'][] = 'CiviEvent';
365 }
366 //CRM-15427
367 $id = CRM_Utils_Request::retrieve('id', 'Integer');
368 if ($id) {
369 $participantEventType = CRM_Core_DAO::getFieldValue("CRM_Event_DAO_Event", $id, 'event_type_id', 'id');
370 $participantRole = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $id, 'default_role_id');
371 $configs['allowSubTypes']['ParticipantEventName'] = [$id];
372 $configs['allowSubTypes']['ParticipantEventType'] = [$participantEventType];
373 $configs['allowSubTypes']['ParticipantRole'] = [$participantRole];
374 }
375 $configs['profileEntities'][] = ['entity_name' => 'contact_1', 'entity_type' => 'IndividualModel'];
376 $configs['profileEntities'][] = [
377 'entity_name' => 'participant_1',
378 'entity_type' => 'ParticipantModel',
379 'entity_sub_type' => '*',
380 ];
381
382 return $configs;
383 }
384
385 /**
386 * Build Confirmation Block.
387 *
388 * @param CRM_Core_Form $form
389 *
390 */
391 public function buildConfirmationBlock(&$form) {
392 $attributes = CRM_Core_DAO::getAttribute('CRM_Event_DAO_Event');
393 // CRM-11182 - Optional confirmation page for free events
394 $is_monetary = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Event', $form->_id, 'is_monetary');
395 $form->assign('is_monetary', $is_monetary);
396 if ($is_monetary == "0") {
397 $form->addYesNo('is_confirm_enabled', ts('Use a confirmation screen?'), NULL, NULL, ['onclick' => "return showHideByValue('is_confirm_enabled','','confirm_screen_settings','block','radio',false);"]);
398 }
399 $form->add('text', 'confirm_title', ts('Title'), $attributes['confirm_title']);
400 $form->add('wysiwyg', 'confirm_text', ts('Introductory Text'), $attributes['confirm_text'] + ['class' => 'collapsed', 'preset' => 'civievent']);
401 $form->add('wysiwyg', 'confirm_footer_text', ts('Footer Text'), $attributes['confirm_text'] + ['class' => 'collapsed', 'preset' => 'civievent']);
402 }
403
404 /**
405 * Build Email Block.
406 *
407 * @param CRM_Core_Form $form
408 *
409 */
410 public function buildMailBlock(&$form) {
411 $form->registerRule('emailList', 'callback', 'emailList', 'CRM_Utils_Rule');
412 $attributes = CRM_Core_DAO::getAttribute('CRM_Event_DAO_Event');
413 $form->addYesNo('is_email_confirm', ts('Send Confirmation Email?'), NULL, NULL, ['onclick' => "return showHideByValue('is_email_confirm','','confirmEmail','block','radio',false);"]);
414 $form->add('textarea', 'confirm_email_text', ts('Text'), $attributes['confirm_email_text']);
415 $form->add('text', 'cc_confirm', ts('CC Confirmation To'), CRM_Core_DAO::getAttribute('CRM_Event_DAO_Event', 'cc_confirm'));
416 $form->addRule('cc_confirm', ts('Please enter a valid list of comma delimited email addresses'), 'emailList');
417 $form->add('text', 'bcc_confirm', ts('BCC Confirmation To'), CRM_Core_DAO::getAttribute('CRM_Event_DAO_Event', 'bcc_confirm'));
418 $form->addRule('bcc_confirm', ts('Please enter a valid list of comma delimited email addresses'), 'emailList');
419 $form->add('text', 'confirm_from_name', ts('Confirm From Name'));
420 $form->add('text', 'confirm_from_email', ts('Confirm From Email'));
421 $form->addRule('confirm_from_email', ts('Email is not valid.'), 'email');
422 }
423
424 /**
425 * @param CRM_Core_Form $form
426 */
427 public function buildThankYouBlock(&$form) {
428 $attributes = CRM_Core_DAO::getAttribute('CRM_Event_DAO_Event');
429 $form->add('text', 'thankyou_title', ts('Title'), $attributes['thankyou_title']);
430 $form->add('wysiwyg', 'thankyou_text', ts('Introductory Text'), $attributes['thankyou_text'] + ['class' => 'collapsed', 'preset' => 'civievent']);
431 $form->add('wysiwyg', 'thankyou_footer_text', ts('Footer Text'), $attributes['thankyou_text'] + ['class' => 'collapsed', 'preset' => 'civievent']);
432 }
433
434 /**
435 * Add local and global form rules.
436 *
437 *
438 * @return void
439 */
440 public function addRules() {
441 if ($this->_addProfileBottom || $this->_addProfileBottomAdd) {
442 return;
443 }
444 $this->addFormRule(['CRM_Event_Form_ManageEvent_Registration', 'formRule'], $this);
445 }
446
447 /**
448 * Global validation rules for the form.
449 *
450 * @param array $values
451 * @param $files
452 * @param CRM_Core_Form $form
453 *
454 * @return array
455 * list of errors to be posted back to the form
456 */
457 public static function formRule($values, $files, $form) {
458 if (!empty($values['is_online_registration'])) {
459
460 if (!$values['confirm_title']) {
461 $errorMsg['confirm_title'] = ts('Please enter a Title for the registration Confirmation Page');
462 }
463 if (!$values['thankyou_title']) {
464 $errorMsg['thankyou_title'] = ts('Please enter a Title for the registration Thank-you Page');
465 }
466 if ($values['is_email_confirm']) {
467 if (!$values['confirm_from_name']) {
468 $errorMsg['confirm_from_name'] = ts('Please enter Confirmation Email FROM Name.');
469 }
470
471 if (!$values['confirm_from_email']) {
472 $errorMsg['confirm_from_email'] = ts('Please enter Confirmation Email FROM Email Address.');
473 }
474 }
475
476 if (isset($values['registration_start_date']) && isset($values['registration_end_date'])) {
477 if ($values['registration_end_date'] < $values['registration_start_date']) {
478 $errorMsg['registration_end_date'] = ts('Registration end date should be after Registration start date');
479 }
480 }
481
482 //check that the selected profiles have either firstname+lastname or email required
483 $profileIds = [
484 CRM_Utils_Array::value('custom_pre_id', $values),
485 CRM_Utils_Array::value('custom_post_id', $values),
486 ];
487 $additionalProfileIds = [
488 CRM_Utils_Array::value('additional_custom_pre_id', $values),
489 CRM_Utils_Array::value('additional_custom_post_id', $values),
490 ];
491 //additional profile fields default to main if not set
492 if (!is_numeric($additionalProfileIds[0])) {
493 $additionalProfileIds[0] = $profileIds[0];
494 }
495 if (!is_numeric($additionalProfileIds[1])) {
496 $additionalProfileIds[1] = $profileIds[1];
497 }
498 //add multiple profiles if set
499 self::addMultipleProfiles($profileIds, $values, 'custom_post_id_multiple');
500 self::addMultipleProfiles($additionalProfileIds, $values, 'additional_custom_post_id_multiple');
501 $isProfileComplete = self::isProfileComplete($profileIds);
502 $isAdditionalProfileComplete = self::isProfileComplete($additionalProfileIds);
503
504 //Check main profiles have an email address available if 'send confirmation email' is selected
505 if ($values['is_email_confirm']) {
506 $emailFields = self::getEmailFields($profileIds);
507 if (!count($emailFields)) {
508 $errorMsg['is_email_confirm'] = ts("Please add a profile with an email address if 'Send Confirmation Email?' is selected");
509 }
510 }
511 $additionalCustomPreId = $additionalCustomPostId = NULL;
512 $isPreError = $isPostError = TRUE;
513 if (!empty($values['allow_same_participant_emails']) && !empty($values['is_multiple_registrations'])) {
514 $types = array_merge(['Individual'], CRM_Contact_BAO_ContactType::subTypes('Individual'));
515 $profiles = CRM_Core_BAO_UFGroup::getProfiles($types);
516
517 //check for additional custom pre profile
518 $additionalCustomPreId = CRM_Utils_Array::value('additional_custom_pre_id', $values);
519 if (!empty($additionalCustomPreId)) {
520 if (!($additionalCustomPreId == 'none')) {
521 $customPreId = $additionalCustomPreId;
522 }
523 else {
524 $isPreError = FALSE;
525 }
526 }
527 else {
528 $customPreId = !empty($values['custom_pre_id']) ? $values['custom_pre_id'] : NULL;
529 }
530 //check whether the additional custom pre profile is of type 'Individual' and its subtypes
531 if (!empty($customPreId)) {
532 $profileTypes = CRM_Core_BAO_UFGroup::profileGroups($customPreId);
533 foreach ($types as $individualTypes) {
534 if (in_array($individualTypes, $profileTypes)) {
535 $isPreError = FALSE;
536 break;
537 }
538 }
539 }
540 else {
541 $isPreError = FALSE;
542 }
543
544 // We don't have required Individual fields in the pre-custom profile, so now check the post-custom profile
545 if ($isPreError) {
546 $additionalCustomPostId = CRM_Utils_Array::value('additional_custom_post_id', $values);
547 if (!empty($additionalCustomPostId)) {
548 if (!($additionalCustomPostId == 'none')) {
549 $customPostId = $additionalCustomPostId;
550 }
551 else {
552 $isPostError = FALSE;
553 }
554 }
555 else {
556 $customPostId = !empty($values['custom_post_id']) ? $values['custom_post_id'] : NULL;
557 }
558 //check whether the additional custom post profile is of type 'Individual' and its subtypes
559 if (!empty($customPostId)) {
560 $profileTypes = CRM_Core_BAO_UFGroup::profileGroups($customPostId);
561 foreach ($types as $individualTypes) {
562 if (in_array($individualTypes, $profileTypes)) {
563 $isPostError = FALSE;
564 break;
565 }
566 }
567 }
568 else {
569 $isPostError = FALSE;
570 }
571
572 if (empty($customPreId) && empty($customPostId)) {
573 $errorMsg['additional_custom_pre_id'] = ts("Allow multiple registrations from the same email address requires a profile of type 'Individual'");
574 }
575 if ($isPostError) {
576 $errorMsg['additional_custom_post_id'] = ts("Allow multiple registrations from the same email address requires a profile of type 'Individual'");
577 }
578 }
579 }
580 if (!$isProfileComplete) {
581 $errorMsg['custom_pre_id'] = ts("Please include a Profile for online registration that contains an Email Address field and / or First Name + Last Name fields.");
582 }
583 if (!$isAdditionalProfileComplete) {
584 $errorMsg['additional_custom_pre_id'] = ts("Please include a Profile for online registration of additional participants that contains an Email Address field and / or First Name + Last Name fields.");
585 }
586
587 // // CRM-8485
588 // $config = CRM_Core_Config::singleton();
589 // if ( $config->doNotAttachPDFReceipt ) {
590 // if (!empty($values['custom_post_id_multiple'])) {
591 // foreach( $values['custom_post_id_multiple'] as $count => $customPostMultiple ) {
592 // if ( $customPostMultiple ) {
593 // $errorMsg["custom_post_id_multiple[{$count}]"] = ts('Please disable PDF receipt as an attachment in <a href="%1">Miscellaneous Settings</a> if you want to add additional profiles.', array( 1 => CRM_Utils_System::url( 'civicrm/admin/setting/misc', 'reset=1' ) ) );
594 // break;
595 // }
596 // }
597 // }
598 //
599 // if (!empty($values['is_multiple_registrations']) &&
600 // CRM_Utils_Array::value('additional_custom_post_id_multiple', $values) ) {
601 // foreach( $values['additional_custom_post_id_multiple'] as $count => $customPostMultiple ) {
602 // if ( $customPostMultiple ) {
603 // $errorMsg["additional_custom_post_id_multiple[{$count}]"] = ts('Please disable PDF receipt as an attachment in <a href="%1">Miscellaneous Settings</a> if you want to add additional profiles.', array( 1 => CRM_Utils_System::url( 'civicrm/admin/setting/misc', 'reset=1' ) ) );
604 // break;
605 // }
606 // }
607 // }
608 // }
609
610 if (!empty($errorMsg)) {
611 if (!empty($values['custom_post_id_multiple'])) {
612 foreach ($values['custom_post_id_multiple'] as $count => $customPostMultiple) {
613 self::buildMultipleProfileBottom($form, $count);
614 }
615 $form->assign('profilePostMultiple', $values['custom_post_id_multiple']);
616 }
617 if (!empty($values['additional_custom_post_id_multiple'])) {
618 foreach ($values['additional_custom_post_id_multiple'] as $count => $customPostMultiple) {
619 self::buildMultipleProfileBottom($form, $count, 'additional_', ts('Profile for Additional Participants'));
620 }
621 $form->assign('profilePostMultipleAdd', $values['additional_custom_post_id_multiple']);
622 }
623 }
624 }
625
626 if (!empty($errorMsg)) {
627 return $errorMsg;
628 }
629
630 return TRUE;
631 }
632
633 /**
634 * Collect all email fields for an array of profile ids.
635 *
636 * @param $profileIds
637 * @return bool
638 */
639 public static function getEmailFields($profileIds) {
640 $emailFields = [];
641 foreach ($profileIds as $profileId) {
642 if ($profileId && is_numeric($profileId)) {
643 $fields = CRM_Core_BAO_UFGroup::getFields($profileId);
644 foreach ($fields as $field) {
645 if (substr_count($field['name'], 'email')) {
646 $emailFields[] = $field;
647 }
648 }
649 }
650 }
651 return $emailFields;
652 }
653
654 /**
655 * Check if a profile contains required fields.
656 *
657 * @param $profileIds
658 * @return bool
659 */
660 public static function isProfileComplete($profileIds) {
661 $profileReqFields = [];
662 foreach ($profileIds as $profileId) {
663 if ($profileId && is_numeric($profileId)) {
664 $fields = CRM_Core_BAO_UFGroup::getFields($profileId);
665 foreach ($fields as $field) {
666 switch (TRUE) {
667 case substr_count($field['name'], 'email'):
668 $profileReqFields[] = 'email';
669 break;
670
671 case substr_count($field['name'], 'first_name'):
672 $profileReqFields[] = 'first_name';
673 break;
674
675 case substr_count($field['name'], 'last_name'):
676 $profileReqFields[] = 'last_name';
677 break;
678 }
679 }
680 }
681 }
682 $profileComplete = (in_array('email', $profileReqFields)
683 || (in_array('first_name', $profileReqFields) && in_array('last_name', $profileReqFields))
684 );
685 return $profileComplete;
686 }
687
688 /**
689 * Check if the profiles collect enough information to dedupe.
690 *
691 * @param $profileIds
692 * @param int $rgId
693 * @return bool
694 */
695 public static function canProfilesDedupe($profileIds, $rgId = 0) {
696 // find the unsupervised rule
697 $rgParams = [
698 'used' => 'Unsupervised',
699 'contact_type' => 'Individual',
700 ];
701 if ($rgId > 0) {
702 $rgParams['id'] = $rgId;
703 }
704 $activeRg = CRM_Dedupe_BAO_RuleGroup::dedupeRuleFieldsWeight($rgParams);
705
706 // get the combinations that could be a match for the rule
707 $okCombos = $combos = [];
708 CRM_Dedupe_BAO_RuleGroup::combos($activeRg[0], $activeRg[1], $combos);
709
710 // create an index of what combinations involve each field
711 $index = [];
712 foreach ($combos as $comboid => $combo) {
713 foreach ($combo as $cfield) {
714 $index[$cfield][$comboid] = TRUE;
715 }
716 $combos[$comboid] = array_fill_keys($combo, 0);
717 $okCombos[$comboid] = array_fill_keys($combo, 2);
718 }
719
720 // get profiles and see if they have the necessary combos
721 $profileReqFields = [];
722 foreach ($profileIds as $profileId) {
723 if ($profileId && is_numeric($profileId)) {
724 $fields = CRM_Core_BAO_UFGroup::getFields($profileId);
725
726 // walk through the fields in the profile
727 foreach ($fields as $field) {
728
729 // check each of the fields in the index against the profile field
730 foreach ($index as $ifield => $icombos) {
731 if (strpos($field['name'], $ifield) !== FALSE) {
732
733 // we found the field in the profile, now record it in the index
734 foreach ($icombos as $icombo => $dontcare) {
735 $combos[$icombo][$ifield] = ($combos[$icombo][$ifield] != 2 && !$field['is_required']) ? 1 : 2;
736
737 if ($combos[$icombo] == $okCombos[$icombo]) {
738 // if any combo is complete with 2s (all fields are present and required), we can go home
739 return 2;
740 }
741 }
742 }
743 }
744 }
745 }
746 }
747
748 // check the combos to see if everything is > 0
749 foreach ($combos as $comboid => $combo) {
750 $complete = FALSE;
751 foreach ($combo as $cfield) {
752 if ($cfield > 0) {
753 $complete = TRUE;
754 }
755 else {
756 // this combo isn't complete--skip to the next combo
757 continue 2;
758 }
759 }
760 if ($complete) {
761 return 1;
762 }
763 }
764
765 // no combo succeeded
766 return 0;
767 }
768
769 /**
770 * Add additional profiles from the form to an array of profile ids.
771 *
772 * @param array $profileIds
773 * @param array $values
774 * @param string $field
775 */
776 public static function addMultipleProfiles(&$profileIds, $values, $field) {
777 if ($multipleProfiles = CRM_Utils_Array::value($field, $values)) {
778 foreach ($multipleProfiles as $profileId) {
779 $profileIds[] = $profileId;
780 }
781 }
782 }
783
784 /**
785 * Process the form submission.
786 *
787 *
788 * @return void
789 */
790 public function postProcess() {
791 $params = $this->exportValues();
792
793 $params['id'] = $this->_id;
794
795 // format params
796 $params['is_online_registration'] = CRM_Utils_Array::value('is_online_registration', $params, FALSE);
797 // CRM-11182
798 $params['is_confirm_enabled'] = CRM_Utils_Array::value('is_confirm_enabled', $params, FALSE);
799 $params['is_multiple_registrations'] = CRM_Utils_Array::value('is_multiple_registrations', $params, FALSE);
800 $params['allow_same_participant_emails'] = CRM_Utils_Array::value('allow_same_participant_emails', $params, FALSE);
801 $params['requires_approval'] = CRM_Utils_Array::value('requires_approval', $params, FALSE);
802
803 // reset is_email confirm if not online reg
804 if (!$params['is_online_registration']) {
805 $params['is_email_confirm'] = FALSE;
806 }
807 if (!empty($params['allow_selfcancelxfer'])) {
808 $params['selfcancelxfer_time'] = !empty($params['selfcancelxfer_time']) ? $params['selfcancelxfer_time'] : 0;
809 }
810
811 CRM_Event_BAO_Event::add($params);
812
813 // also update the ProfileModule tables
814 $ufJoinParams = [
815 'is_active' => 1,
816 'module' => 'CiviEvent',
817 'entity_table' => 'civicrm_event',
818 'entity_id' => $this->_id,
819 ];
820
821 // first delete all past entries
822 CRM_Core_BAO_UFJoin::deleteAll($ufJoinParams);
823
824 $uf = [];
825 $wt = 2;
826 if (!empty($params['custom_pre_id'])) {
827 $uf[1] = $params['custom_pre_id'];
828 $wt = 1;
829 }
830
831 if (!empty($params['custom_post_id'])) {
832 $uf[2] = $params['custom_post_id'];
833 }
834
835 if (!empty($params['custom_post_id_multiple'])) {
836 $uf = array_merge($uf, $params['custom_post_id_multiple']);
837 }
838 $uf = array_values($uf);
839 if (!empty($uf)) {
840 foreach ($uf as $weight => $ufGroupId) {
841 $ufJoinParams['weight'] = $weight + $wt;
842 $ufJoinParams['uf_group_id'] = $ufGroupId;
843 CRM_Core_BAO_UFJoin::create($ufJoinParams);
844 unset($ufJoinParams['id']);
845 }
846 }
847 // also update the ProfileModule tables
848 $ufJoinParamsAdd = [
849 'is_active' => 1,
850 'module' => 'CiviEvent_Additional',
851 'entity_table' => 'civicrm_event',
852 'entity_id' => $this->_id,
853 ];
854
855 // first delete all past entries
856 CRM_Core_BAO_UFJoin::deleteAll($ufJoinParamsAdd);
857 if (!empty($params['is_multiple_registrations'])) {
858 $ufAdd = [];
859 $wtAdd = 2;
860
861 if (array_key_exists('additional_custom_pre_id', $params)) {
862 if (empty($params['additional_custom_pre_id'])) {
863 $ufAdd[1] = $params['custom_pre_id'];
864 $wtAdd = 1;
865 }
866 elseif (CRM_Utils_Array::value('additional_custom_pre_id', $params) == 'none') {
867 }
868 else {
869 $ufAdd[1] = $params['additional_custom_pre_id'];
870 $wtAdd = 1;
871 }
872 }
873
874 if (array_key_exists('additional_custom_post_id', $params)) {
875 if (empty($params['additional_custom_post_id'])) {
876 $ufAdd[2] = $params['custom_post_id'];
877 }
878 elseif (CRM_Utils_Array::value('additional_custom_post_id', $params) == 'none') {
879 }
880 else {
881 $ufAdd[2] = $params['additional_custom_post_id'];
882 }
883 }
884
885 if (!empty($params['additional_custom_post_id_multiple'])) {
886 $additionalPostMultiple = [];
887 foreach ($params['additional_custom_post_id_multiple'] as $key => $value) {
888 if (is_null($value) && !empty($params['custom_post_id'])) {
889 $additionalPostMultiple[$key] = $params['custom_post_id'];
890 }
891 elseif ($value == 'none') {
892 continue;
893 }
894 elseif ($value) {
895 $additionalPostMultiple[$key] = $value;
896 }
897 }
898 $ufAdd = array_merge($ufAdd, $additionalPostMultiple);
899 }
900
901 $ufAdd = array_values($ufAdd);
902 if (!empty($ufAdd)) {
903 foreach ($ufAdd as $weightAdd => $ufGroupIdAdd) {
904
905 $ufJoinParamsAdd['weight'] = $weightAdd + $wtAdd;
906 $ufJoinParamsAdd['uf_group_id'] = $ufGroupIdAdd;
907
908 CRM_Core_BAO_UFJoin::create($ufJoinParamsAdd);
909 unset($ufJoinParamsAdd['id']);
910 }
911 }
912 }
913
914 // get the profiles to evaluate what they collect
915 $profileIds = [
916 CRM_Utils_Array::value('custom_pre_id', $params),
917 CRM_Utils_Array::value('custom_post_id', $params),
918 ];
919 $additionalProfileIds = [
920 CRM_Utils_Array::value('additional_custom_pre_id', $params),
921 CRM_Utils_Array::value('additional_custom_post_id', $params),
922 ];
923 // additional profile fields default to main if not set
924 if (!is_numeric($additionalProfileIds[0])) {
925 $additionalProfileIds[0] = $profileIds[0];
926 }
927 if (!is_numeric($additionalProfileIds[1])) {
928 $additionalProfileIds[1] = $profileIds[1];
929 }
930 //add multiple profiles if set
931 self::addMultipleProfiles($profileIds, $params, 'custom_post_id_multiple');
932 self::addMultipleProfiles($additionalProfileIds, $params, 'additional_custom_post_id_multiple');
933
934 $cantDedupe = FALSE;
935 $rgId = CRM_Utils_Array::value('dedupe_rule_group_id', $params, 0);
936
937 switch (self::canProfilesDedupe($profileIds, $rgId)) {
938 case 0:
939 $dedupeTitle = 'Duplicate Matching Impossible';
940 $cantDedupe = ts("The selected profiles do not contain the fields necessary to match registrations with existing contacts. This means all anonymous registrations will result in a new contact.");
941 break;
942
943 case 1:
944 $dedupeTitle = 'Duplicate Contacts Possible';
945 $cantDedupe = ts("The selected profiles can collect enough information to match registrations with existing contacts, but not all of the relevant fields are required. Anonymous registrations may result in duplicate contacts.");
946 }
947 if (!empty($params['is_multiple_registrations'])) {
948 switch (self::canProfilesDedupe($additionalProfileIds, $rgId)) {
949 case 0:
950 $dedupeTitle = 'Duplicate Matching Impossible';
951 if ($cantDedupe) {
952 $cantDedupe = ts("The selected profiles do not contain the fields necessary to match registrations with existing contacts. This means all anonymous registrations will result in a new contact.");
953 }
954 else {
955 $cantDedupe = ts("The selected profiles do not contain the fields necessary to match additional participants with existing contacts. This means all additional participants will result in a new contact.");
956 }
957 break;
958
959 case 1:
960 if (!$cantDedupe) {
961 $dedupeTitle = 'Duplicate Contacts Possible';
962 $cantDedupe = ts("The selected profiles can collect enough information to match additional participants with existing contacts, but not all of the relevant fields are required. This may result in duplicate contacts.");
963 }
964 }
965 }
966 if ($cantDedupe) {
967 CRM_Core_Session::setStatus($cantDedupe, $dedupeTitle, 'alert dedupenotify', ['expires' => 0]);
968 }
969
970 // Update tab "disabled" css class
971 $this->ajaxResponse['tabValid'] = !empty($params['is_online_registration']);
972
973 parent::endPostProcess();
974 }
975
976 /**
977 * Return a descriptive name for the page, used in wizard header
978 *
979 * @return string
980 */
981 public function getTitle() {
982 return ts('Online Registration');
983 }
984
985 }