Merge pull request #23174 from braders/nodefaults-contrib-links
[civicrm-core.git] / CRM / Event / Form / Registration / Register.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 * @package CRM
14 * @copyright CiviCRM LLC https://civicrm.org/licensing
15 */
16
17 /**
18 * This class generates form components for processing Event.
19 */
20 class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
21
22 /**
23 * The fields involved in this page.
24 *
25 * @var array
26 */
27 public $_fields;
28
29 /**
30 * The status message that user view.
31 *
32 * @var string
33 */
34 protected $_waitlistMsg;
35 protected $_requireApprovalMsg;
36
37 /**
38 * Deprecated parameter that we hope to remove.
39 *
40 * @var bool
41 */
42 public $_quickConfig;
43
44 /**
45 * Skip duplicate check.
46 *
47 * This can be set using hook_civicrm_buildForm() to override the registration dupe check.
48 *
49 * @var bool
50 * @see https://issues.civicrm.org/jira/browse/CRM-7604
51 */
52 public $_skipDupeRegistrationCheck = FALSE;
53
54 public $_paymentProcessorID;
55
56 /**
57 * Show fee block or not.
58 *
59 * @var bool
60 */
61 public $_noFees;
62
63 /**
64 * Fee Block.
65 *
66 * @var array
67 */
68 public $_feeBlock;
69
70 /**
71 * Array of payment related fields to potentially display on this form (generally credit card or debit card fields).
72 *
73 * This is rendered via billingBlock.tpl.
74 *
75 * @var array
76 */
77 public $_paymentFields = [];
78
79 /**
80 * Is this submission incurring no costs.
81 *
82 * @param array $fields
83 * @param \CRM_Event_Form_Registration_Register $form
84 *
85 * @return bool
86 */
87 protected static function isZeroAmount($fields, $form): bool {
88 $isZeroAmount = FALSE;
89 if (!empty($fields['priceSetId'])) {
90 if (empty($fields['amount'])) {
91 $isZeroAmount = TRUE;
92 }
93 }
94 elseif (!empty($fields['amount']) &&
95 (isset($form->_values['discount'][$fields['amount']])
96 && CRM_Utils_Array::value('value', $form->_values['discount'][$fields['amount']]) == 0
97 )
98 ) {
99 $isZeroAmount = TRUE;
100 }
101 elseif (!empty($fields['amount']) &&
102 (isset($form->_values['fee'][$fields['amount']])
103 && CRM_Utils_Array::value('value', $form->_values['fee'][$fields['amount']]) == 0
104 )
105 ) {
106 $isZeroAmount = TRUE;
107 }
108 return $isZeroAmount;
109 }
110
111 /**
112 * Get the contact id for the registration.
113 *
114 * @param array $fields
115 * @param CRM_Event_Form_Registration $form
116 * @param bool $isAdditional
117 *
118 * @return int|null
119 */
120 public static function getRegistrationContactID($fields, $form, $isAdditional) {
121 $contactID = NULL;
122 if (!$isAdditional) {
123 $contactID = $form->getContactID();
124 }
125 if (!$contactID && is_array($fields) && $fields) {
126 $contactID = CRM_Contact_BAO_Contact::getFirstDuplicateContact($fields, 'Individual', 'Unsupervised', [], FALSE, CRM_Utils_Array::value('dedupe_rule_group_id', $form->_values['event']), ['event_id' => CRM_Utils_Array::value('id', $form->_values['event'])]);
127 }
128 return $contactID;
129 }
130
131 /**
132 * Get the active UFGroups (profiles) on this form
133 * Many forms load one or more UFGroups (profiles).
134 * This provides a standard function to retrieve the IDs of those profiles from the form
135 * so that you can implement things such as "is is_captcha field set on any of the active profiles on this form?"
136 *
137 * NOT SUPPORTED FOR USE OUTSIDE CORE EXTENSIONS - Added for reCAPTCHA core extension.
138 *
139 * @return array
140 */
141 public function getUFGroupIDs() {
142 $ufGroupIDs = [];
143 if (!empty($this->_values['custom_pre_id'])) {
144 $ufGroupIDs[] = $this->_values['custom_pre_id'];
145 }
146 if (!empty($this->_values['custom_post_id'])) {
147 // custom_post_id can be an array (because we can have multiple for events).
148 // It is handled as array for contribution page as well though they don't support multiple profiles.
149 if (!is_array($this->_values['custom_post_id'])) {
150 $ufGroupIDs[] = $this->_values['custom_post_id'];
151 }
152 else {
153 $ufGroupIDs = array_merge($ufGroupIDs, $this->_values['custom_post_id']);
154 }
155 }
156 return $ufGroupIDs;
157 }
158
159 /**
160 * Set variables up before form is built.
161 *
162 * @throws \CRM_Core_Exception
163 */
164 public function preProcess() {
165 parent::preProcess();
166
167 //CRM-4320.
168 //here we can't use parent $this->_allowWaitlist as user might
169 //walk back and we might set this value in this postProcess.
170 //(we set when spaces < group count and want to allow become part of waiting )
171 $eventFull = CRM_Event_BAO_Participant::eventFull($this->_eventId, FALSE, CRM_Utils_Array::value('has_waitlist', $this->_values['event']));
172
173 // Get payment processors if appropriate for this event
174 // We hide the payment fields if the event is full or requires approval,
175 // and the current user has not yet been approved CRM-12279
176 $this->_noFees = (($eventFull || $this->_requireApproval) && !$this->_allowConfirmation);
177 $this->_paymentProcessors = $this->_noFees ? [] : $this->get('paymentProcessors');
178 $this->preProcessPaymentOptions();
179
180 $this->_allowWaitlist = FALSE;
181 if ($eventFull && !$this->_allowConfirmation && !empty($this->_values['event']['has_waitlist'])) {
182 $this->_allowWaitlist = TRUE;
183 $this->_waitlistMsg = $this->_values['event']['waitlist_text'] ?? NULL;
184 if (!$this->_waitlistMsg) {
185 $this->_waitlistMsg = ts('This event is currently full. However you can register now and get added to a waiting list. You will be notified if spaces become available.');
186 }
187 }
188 $this->set('allowWaitlist', $this->_allowWaitlist);
189
190 //To check if the user is already registered for the event(CRM-2426)
191 if (!$this->_skipDupeRegistrationCheck) {
192 self::checkRegistration(NULL, $this);
193 }
194
195 $this->assign('availableRegistrations', $this->_availableRegistrations);
196
197 // get the participant values from EventFees.php, CRM-4320
198 if ($this->_allowConfirmation) {
199 CRM_Event_Form_EventFees::preProcess($this);
200 }
201 }
202
203 /**
204 * Set default values for the form.
205 *
206 * @return array
207 *
208 * @throws \CRM_Core_Exception
209 * @throws \CiviCRM_API3_Exception
210 */
211 public function setDefaultValues() {
212 $this->_defaults = [];
213 if (!$this->_allowConfirmation && $this->_requireApproval) {
214 $this->_defaults['bypass_payment'] = 1;
215 }
216 $contactID = $this->getContactID();
217 CRM_Core_Payment_Form::setDefaultValues($this, $contactID);
218
219 CRM_Event_BAO_Participant::formatFieldsAndSetProfileDefaults($contactID, $this);
220
221 // Set default payment processor as default payment_processor radio button value
222 if (!empty($this->_paymentProcessors)) {
223 foreach ($this->_paymentProcessors as $pid => $value) {
224 if (!empty($value['is_default'])) {
225 $this->_defaults['payment_processor_id'] = $pid;
226 }
227 }
228 }
229
230 //if event is monetary and pay later is enabled and payment
231 //processor is not available then freeze the pay later checkbox with
232 //default check
233 if (!empty($this->_values['event']['is_pay_later']) &&
234 !is_array($this->_paymentProcessor)
235 ) {
236 $this->_defaults['is_pay_later'] = 1;
237 }
238
239 //set custom field defaults
240 if (!empty($this->_fields)) {
241 //load default campaign from page.
242 if (array_key_exists('participant_campaign_id', $this->_fields)) {
243 $this->_defaults['participant_campaign_id'] = CRM_Utils_Array::value('campaign_id',
244 $this->_values['event']
245 );
246 }
247
248 foreach ($this->_fields as $name => $field) {
249 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) {
250 // fix for CRM-1743
251 if (!isset($this->_defaults[$name])) {
252 CRM_Core_BAO_CustomField::setProfileDefaults($customFieldID, $name, $this->_defaults,
253 NULL, CRM_Profile_Form::MODE_REGISTER
254 );
255 }
256 }
257 }
258 }
259
260 //fix for CRM-3088, default value for discount set.
261 $discountId = NULL;
262 if (!empty($this->_values['discount'])) {
263 $discountId = CRM_Core_BAO_Discount::findSet($this->_eventId, 'civicrm_event');
264 if ($discountId) {
265 if (isset($this->_values['event']['default_discount_fee_id'])) {
266 $discountKey = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue',
267 $this->_values['event']['default_discount_fee_id'],
268 'weight', 'id'
269 );
270
271 $this->_defaults['amount'] = key(array_slice($this->_values['discount'][$discountId],
272 $discountKey - 1, $discountKey, TRUE
273 ));
274 }
275 }
276 }
277
278 // add this event's default participant role to defaults array
279 // (for cases where participant_role field is included in form via profile)
280 if ($this->_values['event']['default_role_id']) {
281 $this->_defaults['participant_role']
282 = $this->_defaults['participant_role_id'] = $this->_values['event']['default_role_id'];
283 }
284 if ($this->_priceSetId && !empty($this->_feeBlock)) {
285 foreach ($this->_feeBlock as $key => $val) {
286 if (empty($val['options'])) {
287 continue;
288 }
289 $optionFullIds = CRM_Utils_Array::value('option_full_ids', $val, []);
290 foreach ($val['options'] as $keys => $values) {
291 $priceFieldName = 'price_' . $values['price_field_id'];
292 $priceFieldValue = CRM_Price_BAO_PriceSet::getPriceFieldValueFromURL($this, $priceFieldName);
293 if (!empty($priceFieldValue)) {
294 CRM_Price_BAO_PriceSet::setDefaultPriceSetField($priceFieldName, $priceFieldValue, $val['html_type'], $this->_defaults);
295 // break here to prevent overwriting of default due to 'is_default'
296 // option configuration. The value sent via URL get's higher priority.
297 break;
298 }
299 else {
300 if ($values['is_default'] && empty($values['is_full'])) {
301 if ($val['html_type'] == 'CheckBox') {
302 $this->_defaults["price_{$key}"][$keys] = 1;
303 }
304 else {
305 $this->_defaults["price_{$key}"] = $keys;
306 }
307 }
308 }
309 }
310 $unsetSubmittedOptions[$val['id']] = $optionFullIds;
311 }
312 //reset values for all options those are full.
313 CRM_Event_Form_Registration::resetElementValue($unsetSubmittedOptions, $this);
314 }
315
316 //set default participant fields, CRM-4320.
317 $hasAdditionalParticipants = FALSE;
318 if ($this->_allowConfirmation) {
319 $this->_contactId = $contactID;
320 $this->_discountId = $discountId;
321 $forcePayLater = CRM_Utils_Array::value('is_pay_later', $this->_defaults, FALSE);
322 $this->_defaults = array_merge($this->_defaults, CRM_Event_Form_EventFees::setDefaultValues($this));
323 $this->_defaults['is_pay_later'] = $forcePayLater;
324
325 if ($this->_additionalParticipantIds) {
326 $hasAdditionalParticipants = TRUE;
327 $this->_defaults['additional_participants'] = count($this->_additionalParticipantIds);
328 }
329 }
330 $this->assign('hasAdditionalParticipants', $hasAdditionalParticipants);
331
332 // //hack to simplify credit card entry for testing
333 // $this->_defaults['credit_card_type'] = 'Visa';
334 // $this->_defaults['credit_card_number'] = '4807731747657838';
335 // $this->_defaults['cvv2'] = '000';
336 // $this->_defaults['credit_card_exp_date'] = array( 'Y' => '2010', 'M' => '05' );
337
338 // to process Custom data that are appended to URL
339 $getDefaults = CRM_Core_BAO_CustomGroup::extractGetParams($this, "'Contact', 'Individual', 'Contribution', 'Participant'");
340 if (!empty($getDefaults)) {
341 $this->_defaults = array_merge($this->_defaults, $getDefaults);
342 }
343
344 return $this->_defaults;
345 }
346
347 /**
348 * Build the form object.
349 */
350 public function buildQuickForm() {
351 // build profiles first so that we can determine address fields etc
352 // and then show copy address checkbox
353 $this->buildCustom($this->_values['custom_pre_id'], 'customPre');
354 $this->buildCustom($this->_values['custom_post_id'], 'customPost');
355
356 // CRM-18399: used by template to pass pre profile id as a url arg
357 $this->assign('custom_pre_id', $this->_values['custom_pre_id']);
358
359 // Required for currency formatting in the JS layer
360
361 // Required for currency formatting in the JS layer
362 // this is a temporary fix intended to resolve a regression quickly
363 // And assigning moneyFormat for js layer formatting
364 // will only work until that is done.
365 // https://github.com/civicrm/civicrm-core/pull/19151
366 $this->assign('moneyFormat', CRM_Utils_Money::format(1234.56));
367
368 CRM_Core_Payment_ProcessorForm::buildQuickForm($this);
369
370 $contactID = $this->getContactID();
371 if ($contactID) {
372 $this->assign('contact_id', $contactID);
373 $this->assign('display_name', CRM_Contact_BAO_Contact::displayName($contactID));
374 }
375
376 $bypassPayment = $allowGroupOnWaitlist = $isAdditionalParticipants = FALSE;
377 if ($this->_values['event']['is_multiple_registrations']) {
378 // don't allow to add additional during confirmation if not preregistered.
379 if (!$this->_allowConfirmation || $this->_additionalParticipantIds) {
380 // CRM-17745: Make maximum additional participants configurable
381 // Label is value + 1, since the code sees this is ADDITIONAL participants (in addition to "self")
382 $additionalOptions = [];
383 $additionalOptions[''] = 1;
384 for ($i = 1; $i <= $this->_values['event']['max_additional_participants']; $i++) {
385 $additionalOptions[$i] = $i + 1;
386 }
387 $this->add('select', 'additional_participants',
388 ts('How many people are you registering?'),
389 $additionalOptions,
390 NULL
391 );
392 $isAdditionalParticipants = TRUE;
393 }
394 }
395
396 if (!$this->_allowConfirmation) {
397 $bypassPayment = TRUE;
398 }
399
400 //hack to allow group to register w/ waiting
401 if ((!empty($this->_values['event']['is_multiple_registrations']) ||
402 $this->_priceSetId
403 ) &&
404 !$this->_allowConfirmation &&
405 is_numeric($this->_availableRegistrations) && !empty($this->_values['event']['has_waitlist'])
406 ) {
407 $bypassPayment = TRUE;
408 //case might be group become as a part of waitlist.
409 //If not waitlist then they require admin approve.
410 $allowGroupOnWaitlist = TRUE;
411 $this->_waitlistMsg = ts("This event has only %1 space(s) left. If you continue and register more than %1 people (including yourself ), the whole group will be wait listed. Or, you can reduce the number of people you are registering to %1 to avoid being put on the waiting list.", [1 => $this->_availableRegistrations]);
412
413 if ($this->_requireApproval) {
414 $this->_requireApprovalMsg = CRM_Utils_Array::value('approval_req_text', $this->_values['event'],
415 ts('Registration for this event requires approval. Once your registration(s) have been reviewed, you will receive an email with a link to a web page where you can complete the registration process.')
416 );
417 }
418 }
419
420 //case where only approval needed - no waitlist.
421 if ($this->_requireApproval &&
422 !$this->_allowWaitlist && !$bypassPayment
423 ) {
424 $this->_requireApprovalMsg = CRM_Utils_Array::value('approval_req_text', $this->_values['event'],
425 ts('Registration for this event requires approval. Once your registration has been reviewed, you will receive an email with a link to a web page where you can complete the registration process.')
426 );
427 }
428
429 //lets display status to primary page only.
430 $this->assign('waitlistMsg', $this->_waitlistMsg);
431 $this->assign('requireApprovalMsg', $this->_requireApprovalMsg);
432 $this->assign('allowGroupOnWaitlist', $allowGroupOnWaitlist);
433 $this->assign('isAdditionalParticipants', $isAdditionalParticipants);
434
435 if ($this->_values['event']['is_monetary']) {
436 self::buildAmount($this);
437 }
438
439 if ($contactID === 0 && !$this->_values['event']['is_multiple_registrations']) {
440 //@todo we are blocking for multiple registrations because we haven't tested
441 $this->addCIDZeroOptions();
442 }
443
444 if ($this->_values['event']['is_monetary']) {
445 $this->addPaymentProcessorFieldsToForm();
446 }
447
448 $this->addElement('hidden', 'bypass_payment', NULL, ['id' => 'bypass_payment']);
449
450 $this->assign('bypassPayment', $bypassPayment);
451
452 if (!$contactID) {
453 $createCMSUser = FALSE;
454
455 if ($this->_values['custom_pre_id']) {
456 $profileID = $this->_values['custom_pre_id'];
457 $createCMSUser = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $profileID, 'is_cms_user');
458 }
459
460 if (!$createCMSUser &&
461 $this->_values['custom_post_id']
462 ) {
463 if (!is_array($this->_values['custom_post_id'])) {
464 $profileIDs = [$this->_values['custom_post_id']];
465 }
466 else {
467 $profileIDs = $this->_values['custom_post_id'];
468 }
469 foreach ($profileIDs as $pid) {
470 if (CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $pid, 'is_cms_user')) {
471 $profileID = $pid;
472 $createCMSUser = TRUE;
473 break;
474 }
475 }
476 }
477
478 if ($createCMSUser) {
479 CRM_Core_BAO_CMSUser::buildForm($this, $profileID, TRUE);
480 }
481 }
482
483 //we have to load confirm contribution button in template
484 //when multiple payment processor as the user
485 //can toggle with payment processor selection
486 $billingModePaymentProcessors = 0;
487 if (!CRM_Utils_System::isNull($this->_paymentProcessors)) {
488 foreach ($this->_paymentProcessors as $key => $values) {
489 if ($values['billing_mode'] == CRM_Core_Payment::BILLING_MODE_BUTTON) {
490 $billingModePaymentProcessors++;
491 }
492 }
493 }
494
495 if ($billingModePaymentProcessors && count($this->_paymentProcessors) == $billingModePaymentProcessors) {
496 $allAreBillingModeProcessors = TRUE;
497 }
498 else {
499 $allAreBillingModeProcessors = FALSE;
500 }
501
502 if (!$allAreBillingModeProcessors || !empty($this->_values['event']['is_pay_later']) || $bypassPayment
503 ) {
504
505 //freeze button to avoid multiple calls.
506 if (empty($this->_values['event']['is_monetary'])) {
507 $this->submitOnce = TRUE;
508 }
509
510 // CRM-11182 - Optional confirmation screen
511 // Change button label depending on whether the next action is confirm or register
512 $buttonParams = [
513 'type' => 'upload',
514 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
515 'isDefault' => TRUE,
516 ];
517 if (
518 !$this->_values['event']['is_multiple_registrations']
519 && !$this->_values['event']['is_monetary']
520 && !$this->_values['event']['is_confirm_enabled']
521 ) {
522 $buttonParams['name'] = ts('Register');
523 }
524 else {
525 $buttonParams['name'] = ts('Review');
526 $buttonParams['icon'] = 'fa-chevron-right';
527 }
528
529 $this->addButtons([$buttonParams]);
530 }
531
532 $this->addFormRule(['CRM_Event_Form_Registration_Register', 'formRule'], $this);
533 $this->unsavedChangesWarn = TRUE;
534
535 // add pcp fields
536 if ($this->_pcpId) {
537 CRM_PCP_BAO_PCP::buildPcp($this->_pcpId, $this);
538 }
539 }
540
541 /**
542 * Build the radio/text form elements for the amount field
543 *
544 * @param CRM_Event_Form_Registration_Register $form
545 * Form object.
546 * @param bool $required
547 * True if you want to add formRule.
548 * @param int $discountId
549 * Discount id for the event.
550 *
551 * @throws \CRM_Core_Exception
552 */
553 public static function buildAmount(&$form, $required = TRUE, $discountId = NULL) {
554 // build amount only when needed, skip incase of event full and waitlisting is enabled
555 // and few other conditions check preProcess()
556 if (property_exists($form, '_noFees') && $form->_noFees) {
557 return;
558 }
559
560 //if payment done, no need to build the fee block.
561 if (!empty($form->_paymentId)) {
562 //fix to display line item in update mode.
563 $form->assign('priceSet', $form->_priceSet ?? NULL);
564 return;
565 }
566
567 $feeFields = $form->_values['fee'] ?? NULL;
568
569 if (is_array($feeFields)) {
570 $form->_feeBlock = &$form->_values['fee'];
571 }
572
573 //check for discount.
574 $discountedFee = $form->_values['discount'] ?? NULL;
575 if (is_array($discountedFee) && !empty($discountedFee)) {
576 if (!$discountId) {
577 $form->_discountId = $discountId = CRM_Core_BAO_Discount::findSet($form->_eventId, 'civicrm_event');
578 }
579 if ($discountId) {
580 $form->_feeBlock = &$form->_values['discount'][$discountId];
581 }
582 }
583 if (!is_array($form->_feeBlock)) {
584 $form->_feeBlock = [];
585 }
586
587 //its time to call the hook.
588 CRM_Utils_Hook::buildAmount('event', $form, $form->_feeBlock);
589
590 //reset required if participant is skipped.
591 $button = substr($form->controller->getButtonName(), -4);
592 if ($required && $button == 'skip') {
593 $required = FALSE;
594 }
595
596 $className = CRM_Utils_System::getClassName($form);
597
598 //build the priceset fields.
599 if (isset($form->_priceSetId) && $form->_priceSetId) {
600
601 //format price set fields across option full.
602 self::formatFieldsForOptionFull($form);
603
604 if (!empty($form->_priceSet['is_quick_config'])) {
605 $form->_quickConfig = $form->_priceSet['is_quick_config'];
606 }
607 $form->add('hidden', 'priceSetId', $form->_priceSetId);
608
609 // CRM-14492 Admin price fields should show up on event registration if user has 'administer CiviCRM' permissions
610 $adminFieldVisible = FALSE;
611 if (CRM_Core_Permission::check('administer CiviCRM data')) {
612 $adminFieldVisible = TRUE;
613 }
614
615 $hideAdminValues = TRUE;
616 if (CRM_Core_Permission::check('edit event participants')) {
617 $hideAdminValues = FALSE;
618 }
619
620 foreach ($form->_feeBlock as $field) {
621 // public AND admin visibility fields are included for back-office registration and back-office change selections
622 if (CRM_Utils_Array::value('visibility', $field) == 'public' ||
623 (CRM_Utils_Array::value('visibility', $field) == 'admin' && $adminFieldVisible == TRUE) ||
624 $className == 'CRM_Event_Form_Participant' ||
625 $className === 'CRM_Event_Form_Task_Register' ||
626 $className == 'CRM_Event_Form_ParticipantFeeSelection'
627 ) {
628 $fieldId = $field['id'];
629 $elementName = 'price_' . $fieldId;
630
631 $isRequire = $field['is_required'] ?? NULL;
632 if ($button == 'skip') {
633 $isRequire = FALSE;
634 }
635
636 //user might modified w/ hook.
637 $options = $field['options'] ?? NULL;
638 $formClasses = ['CRM_Event_Form_Participant', 'CRM_Event_Form_Task_Register', 'CRM_Event_Form_ParticipantFeeSelection'];
639
640 if (!is_array($options)) {
641 continue;
642 }
643 elseif ($hideAdminValues && !in_array($className, $formClasses)) {
644 $publicVisibilityID = CRM_Price_BAO_PriceField::getVisibilityOptionID('public');
645 $adminVisibilityID = CRM_Price_BAO_PriceField::getVisibilityOptionID('admin');
646
647 foreach ($options as $key => $currentOption) {
648 $optionVisibility = CRM_Utils_Array::value('visibility_id', $currentOption, $publicVisibilityID);
649 if ($optionVisibility == $adminVisibilityID) {
650 unset($options[$key]);
651 }
652 }
653 }
654
655 $optionFullIds = CRM_Utils_Array::value('option_full_ids', $field, []);
656
657 //soft suppress required rule when option is full.
658 if (!empty($optionFullIds) && (count($options) == count($optionFullIds))) {
659 $isRequire = FALSE;
660 }
661 if (!empty($options)) {
662 //build the element.
663 CRM_Price_BAO_PriceField::addQuickFormElement($form,
664 $elementName,
665 $fieldId,
666 FALSE,
667 $isRequire,
668 NULL,
669 $options,
670 $optionFullIds
671 );
672 }
673 }
674 }
675 $form->_priceSet['id'] = $form->_priceSet['id'] ?? $form->_priceSetId;
676 $form->assign('priceSet', $form->_priceSet);
677 }
678 else {
679 $eventFeeBlockValues = $elements = $elementJS = [];
680 foreach ($form->_feeBlock as $fee) {
681 if (is_array($fee)) {
682
683 //CRM-7632, CRM-6201
684 $totalAmountJs = NULL;
685 if ($className == 'CRM_Event_Form_Participant' || $className === 'CRM_Event_Form_Task_Register') {
686 $totalAmountJs = ['onClick' => "fillTotalAmount(" . $fee['value'] . ")"];
687 }
688
689 $eventFeeBlockValues['amount_id_' . $fee['amount_id']] = $fee['value'];
690 $elements[$fee['amount_id']] = CRM_Utils_Money::format($fee['value']) . ' ' . $fee['label'];
691 $elementJS[$fee['amount_id']] = $totalAmountJs;
692 }
693 }
694 $form->assign('eventFeeBlockValues', json_encode($eventFeeBlockValues));
695
696 $form->_defaults['amount'] = $form->_values['event']['default_fee_id'] ?? NULL;
697 $element = &$form->addRadio('amount', ts('Event Fee(s)'), $elements, [], '<br />', FALSE, $elementJS);
698 if (isset($form->_online) && $form->_online) {
699 $element->freeze();
700 }
701 if ($required) {
702 $form->addRule('amount', ts('Fee Level is a required field.'), 'required');
703 }
704 }
705 }
706
707 /**
708 * @param CRM_Event_Form_Registration $form
709 */
710 public static function formatFieldsForOptionFull(&$form) {
711 $priceSet = $form->get('priceSet');
712 $priceSetId = $form->get('priceSetId');
713 $defaultPricefieldIds = [];
714 if (!empty($form->_values['line_items'])) {
715 foreach ($form->_values['line_items'] as $lineItem) {
716 $defaultPricefieldIds[] = $lineItem['price_field_value_id'];
717 }
718 }
719 if (!$priceSetId ||
720 !is_array($priceSet) ||
721 empty($priceSet) || empty($priceSet['optionsMaxValueTotal'])
722 ) {
723 return;
724 }
725
726 $skipParticipants = $formattedPriceSetDefaults = [];
727 if (!empty($form->_allowConfirmation) && (isset($form->_pId) || isset($form->_additionalParticipantId))) {
728 $participantId = $form->_pId ?? $form->_additionalParticipantId;
729 $pricesetDefaults = CRM_Event_Form_EventFees::setDefaultPriceSet($participantId,
730 $form->_eventId
731 );
732 // modify options full to respect the selected fields
733 // options on confirmation.
734 $formattedPriceSetDefaults = self::formatPriceSetParams($form, $pricesetDefaults);
735
736 // to skip current registered participants fields option count on confirmation.
737 $skipParticipants[] = $form->_participantId;
738 if (!empty($form->_additionalParticipantIds)) {
739 $skipParticipants = array_merge($skipParticipants, $form->_additionalParticipantIds);
740 }
741 }
742
743 $className = CRM_Utils_System::getClassName($form);
744
745 //get the current price event price set options count.
746 $currentOptionsCount = self::getPriceSetOptionCount($form);
747 $recordedOptionsCount = CRM_Event_BAO_Participant::priceSetOptionsCount($form->_eventId, $skipParticipants);
748 $optionFullTotalAmount = 0;
749 $currentParticipantNo = (int) substr($form->_name, 12);
750 foreach ($form->_feeBlock as & $field) {
751 $optionFullIds = [];
752 $fieldId = $field['id'];
753 if (!is_array($field['options'])) {
754 continue;
755 }
756 foreach ($field['options'] as & $option) {
757 $optId = $option['id'];
758 $count = CRM_Utils_Array::value('count', $option, 0);
759 $maxValue = CRM_Utils_Array::value('max_value', $option, 0);
760 $dbTotalCount = CRM_Utils_Array::value($optId, $recordedOptionsCount, 0);
761 $currentTotalCount = CRM_Utils_Array::value($optId, $currentOptionsCount, 0);
762
763 $totalCount = $currentTotalCount + $dbTotalCount;
764 $isFull = FALSE;
765 if ($maxValue &&
766 (($totalCount >= $maxValue) &&
767 (empty($form->_lineItem[$currentParticipantNo][$optId]['price_field_id']) || $dbTotalCount >= $maxValue))
768 ) {
769 $isFull = TRUE;
770 $optionFullIds[$optId] = $optId;
771 if ($field['html_type'] != 'Select') {
772 if (in_array($optId, $defaultPricefieldIds)) {
773 $optionFullTotalAmount += CRM_Utils_Array::value('amount', $option);
774 }
775 }
776 else {
777 if (!empty($defaultPricefieldIds) && in_array($optId, $defaultPricefieldIds)) {
778 unset($optionFullIds[$optId]);
779 }
780 }
781 }
782 //here option is not full,
783 //but we don't want to allow participant to increase
784 //seats at the time of re-walking registration.
785 if ($count &&
786 !empty($form->_allowConfirmation) &&
787 !empty($formattedPriceSetDefaults)
788 ) {
789 if (empty($formattedPriceSetDefaults["price_{$field}"]) || empty($formattedPriceSetDefaults["price_{$fieldId}"][$optId])) {
790 $optionFullIds[$optId] = $optId;
791 $isFull = TRUE;
792 }
793 }
794 $option['is_full'] = $isFull;
795 $option['db_total_count'] = $dbTotalCount;
796 $option['total_option_count'] = $dbTotalCount + $currentTotalCount;
797 }
798
799 //ignore option full for offline registration.
800 if ($className == 'CRM_Event_Form_Participant' || $className === 'CRM_Event_Form_Task_Register') {
801 $optionFullIds = [];
802 }
803
804 //finally get option ids in.
805 $field['option_full_ids'] = $optionFullIds;
806 }
807 $form->assign('optionFullTotalAmount', $optionFullTotalAmount);
808 }
809
810 /**
811 * Global form rule.
812 *
813 * @param array $fields
814 * The input form values.
815 * @param array $files
816 * The uploaded files if any.
817 * @param \CRM_Event_Form_Registration_Register $form
818 *
819 * @return bool|array
820 * true if no errors, else array of errors
821 *
822 * @throws \CRM_Core_Exception
823 */
824 public static function formRule($fields, $files, $form) {
825 $errors = [];
826 //check that either an email or firstname+lastname is included in the form(CRM-9587)
827 self::checkProfileComplete($fields, $errors, $form->_eventId);
828 //To check if the user is already registered for the event(CRM-2426)
829 if (!$form->_skipDupeRegistrationCheck) {
830 self::checkRegistration($fields, $form);
831 }
832 //check for availability of registrations.
833 if (!$form->_allowConfirmation && empty($fields['bypass_payment']) &&
834 is_numeric($form->_availableRegistrations) &&
835 CRM_Utils_Array::value('additional_participants', $fields) >= $form->_availableRegistrations
836 ) {
837 $errors['additional_participants'] = ts("There is only enough space left on this event for %1 participant(s).", [1 => $form->_availableRegistrations]);
838 }
839
840 $numberAdditionalParticipants = $fields['additional_participants'] ?? 0;
841
842 if ($numberAdditionalParticipants && !CRM_Utils_Rule::positiveInteger($fields['additional_participants'])) {
843 $errors['additional_participants'] = ts('Please enter a whole number for Number of additional people.');
844 }
845
846 // during confirmation don't allow to increase additional participants, CRM-4320
847 if ($form->_allowConfirmation && $numberAdditionalParticipants &&
848 is_array($form->_additionalParticipantIds) &&
849 $numberAdditionalParticipants > count($form->_additionalParticipantIds)
850 ) {
851 $errors['additional_participants'] = ts("It looks like you are trying to increase the number of additional people you are registering for. You can confirm registration for a maximum of %1 additional people.", [1 => count($form->_additionalParticipantIds)]);
852 }
853
854 //don't allow to register w/ waiting if enough spaces available.
855 if (!empty($fields['bypass_payment']) && $form->_allowConfirmation) {
856 if (!is_numeric($form->_availableRegistrations) ||
857 (empty($fields['priceSetId']) && CRM_Utils_Array::value('additional_participants', $fields) < $form->_availableRegistrations)
858 ) {
859 $errors['bypass_payment'] = ts("You have not been added to the waiting list because there are spaces available for this event. We recommend registering yourself for an available space instead.");
860 }
861 }
862
863 // priceset validations
864 if (!empty($fields['priceSetId']) &&
865 !$form->_requireApproval && !$form->_allowWaitlist
866 ) {
867 //format params.
868 $formatted = self::formatPriceSetParams($form, $fields);
869 $ppParams = [$formatted];
870 $priceSetErrors = self::validatePriceSet($form, $ppParams);
871 $primaryParticipantCount = self::getParticipantCount($form, $ppParams);
872
873 //get price set fields errors in.
874 $errors = array_merge($errors, CRM_Utils_Array::value(0, $priceSetErrors, []));
875
876 $totalParticipants = $primaryParticipantCount;
877 if ($numberAdditionalParticipants) {
878 $totalParticipants += $numberAdditionalParticipants;
879 }
880
881 if (empty($fields['bypass_payment']) &&
882 !$form->_allowConfirmation &&
883 is_numeric($form->_availableRegistrations) &&
884 $form->_availableRegistrations < $totalParticipants
885 ) {
886 $errors['_qf_default'] = ts("Only %1 Registrations available.", [1 => $form->_availableRegistrations]);
887 }
888
889 $lineItem = [];
890 CRM_Price_BAO_PriceSet::processAmount($form->_values['fee'], $fields, $lineItem);
891
892 $minAmt = CRM_Core_DAO::getFieldValue('CRM_Price_DAO_PriceSet', $fields['priceSetId'], 'min_amount');
893 if ($fields['amount'] < 0) {
894 $errors['_qf_default'] = ts('Event Fee(s) can not be less than zero. Please select the options accordingly');
895 }
896 elseif (!empty($minAmt) && $fields['amount'] < $minAmt) {
897 $errors['_qf_default'] = ts('A minimum amount of %1 should be selected from Event Fee(s).', [
898 1 => CRM_Utils_Money::format($minAmt),
899 ]);
900 }
901 }
902 foreach (CRM_Contact_BAO_Contact::$_greetingTypes as $greeting) {
903 if ($greetingType = CRM_Utils_Array::value($greeting, $fields)) {
904 $customizedValue = CRM_Core_PseudoConstant::getKey('CRM_Contact_BAO_Contact', $greeting . '_id', 'Customized');
905 if ($customizedValue == $greetingType && empty($fields[$greeting . '_custom'])) {
906 $errors[$greeting . '_custom'] = ts('Custom %1 is a required field if %1 is of type Customized.',
907 [1 => ucwords(str_replace('_', ' ', $greeting))]
908 );
909 }
910 }
911 }
912
913 // @todo - can we remove the 'is_monetary' concept?
914 if ($form->_values['event']['is_monetary']) {
915 if (empty($form->_requireApproval) && !empty($fields['amount']) && $fields['amount'] > 0 &&
916 !isset($fields['payment_processor_id'])) {
917 $errors['payment_processor_id'] = ts('Please select a Payment Method');
918 }
919
920 if (self::isZeroAmount($fields, $form)) {
921 return empty($errors) ? TRUE : $errors;
922 }
923
924 // also return if zero fees for valid members
925 if (!empty($fields['bypass_payment']) ||
926 (!$form->_allowConfirmation && ($form->_requireApproval || $form->_allowWaitlist))
927 ) {
928 return empty($errors) ? TRUE : $errors;
929 }
930 CRM_Core_Payment_Form::validatePaymentInstrument(
931 $fields['payment_processor_id'],
932 $fields,
933 $errors,
934 (!$form->_isBillingAddressRequiredForPayLater ? NULL : 'billing')
935 );
936 }
937
938 return empty($errors) ? TRUE : $errors;
939 }
940
941 /**
942 * Check if profiles are complete when event registration occurs(CRM-9587).
943 *
944 * @param array $fields
945 * @param array $errors
946 * @param int $eventId
947 */
948 public static function checkProfileComplete($fields, &$errors, $eventId) {
949 $email = '';
950 foreach ($fields as $fieldname => $fieldvalue) {
951 if (substr($fieldname, 0, 6) == 'email-' && $fieldvalue) {
952 $email = $fieldvalue;
953 }
954 }
955
956 if (!$email && !(!empty($fields['first_name']) && !empty($fields['last_name']))) {
957 $defaults = $params = ['id' => $eventId];
958 CRM_Event_BAO_Event::retrieve($params, $defaults);
959 $message = ts("Mandatory fields (first name and last name, OR email address) are missing from this form.");
960 $errors['_qf_default'] = $message;
961 }
962 }
963
964 /**
965 * Process the form submission.
966 */
967 public function postProcess() {
968 // get the submitted form values.
969 $params = $this->controller->exportValues($this->_name);
970
971 //set as Primary participant
972 $params['is_primary'] = 1;
973
974 if ($this->_values['event']['is_pay_later']
975 && (!array_key_exists('hidden_processor', $params) || $params['payment_processor_id'] == 0)
976 ) {
977 $params['is_pay_later'] = 1;
978 }
979 else {
980 $params['is_pay_later'] = 0;
981 }
982
983 $this->set('is_pay_later', $params['is_pay_later']);
984
985 // assign pay later stuff
986 $this->_params['is_pay_later'] = CRM_Utils_Array::value('is_pay_later', $params, FALSE);
987 $this->assign('is_pay_later', $params['is_pay_later']);
988 $this->assign('pay_later_text', $params['is_pay_later'] ? $this->_values['event']['pay_later_text'] : NULL);
989 $this->assign('pay_later_receipt', $params['is_pay_later'] ? $this->_values['event']['pay_later_receipt'] : NULL);
990
991 if (!$this->_allowConfirmation) {
992 // check if the participant is already registered
993 if (!$this->_skipDupeRegistrationCheck) {
994 $params['contact_id'] = self::getRegistrationContactID($params, $this, FALSE);
995 }
996 }
997
998 if (!empty($params['image_URL'])) {
999 CRM_Contact_BAO_Contact::processImageParams($params);
1000 }
1001
1002 //carry campaign to partcipants.
1003 if (array_key_exists('participant_campaign_id', $params)) {
1004 $params['campaign_id'] = $params['participant_campaign_id'];
1005 }
1006 else {
1007 $params['campaign_id'] = $this->_values['event']['campaign_id'] ?? NULL;
1008 }
1009
1010 //hack to allow group to register w/ waiting
1011 $primaryParticipantCount = self::getParticipantCount($this, $params);
1012
1013 $totalParticipants = $primaryParticipantCount;
1014 if (!empty($params['additional_participants'])) {
1015 $totalParticipants += $params['additional_participants'];
1016 }
1017 if (!$this->_allowConfirmation && !empty($params['bypass_payment']) &&
1018 is_numeric($this->_availableRegistrations) &&
1019 $totalParticipants > $this->_availableRegistrations
1020 ) {
1021 $this->_allowWaitlist = TRUE;
1022 $this->set('allowWaitlist', TRUE);
1023 }
1024
1025 //carry participant id if pre-registered.
1026 if ($this->_allowConfirmation && $this->_participantId) {
1027 $params['participant_id'] = $this->_participantId;
1028 }
1029
1030 $params['defaultRole'] = 1;
1031 if (array_key_exists('participant_role', $params)) {
1032 $params['participant_role_id'] = $params['participant_role'];
1033 }
1034
1035 if (array_key_exists('participant_role_id', $params)) {
1036 $params['defaultRole'] = 0;
1037 }
1038 if (empty($params['participant_role_id']) &&
1039 $this->_values['event']['default_role_id']
1040 ) {
1041 $params['participant_role_id'] = $this->_values['event']['default_role_id'];
1042 }
1043
1044 $config = CRM_Core_Config::singleton();
1045 $params['currencyID'] = $config->defaultCurrency;
1046
1047 if ($this->_values['event']['is_monetary']) {
1048 // we first reset the confirm page so it accepts new values
1049 $this->controller->resetPage('Confirm');
1050
1051 //added for discount
1052 $discountId = CRM_Core_BAO_Discount::findSet($this->_eventId, 'civicrm_event');
1053 $params['amount_level'] = $this->getAmountLevel($params, $discountId);
1054 if (!empty($this->_values['discount'][$discountId])) {
1055 $params['discount_id'] = $discountId;
1056 $params['amount'] = $this->_values['discount'][$discountId][$params['amount']]['value'];
1057 }
1058 elseif (empty($params['priceSetId'])) {
1059 if (!empty($params['amount'])) {
1060 $params['amount'] = $this->_values['fee'][$params['amount']]['value'];
1061 }
1062 else {
1063 $params['amount'] = '';
1064 }
1065 }
1066 else {
1067 $lineItem = [];
1068 CRM_Price_BAO_PriceSet::processAmount($this->_values['fee'], $params, $lineItem);
1069 if ($params['tax_amount']) {
1070 $this->set('tax_amount', $params['tax_amount']);
1071 }
1072 $submittedLineItems = $this->get('lineItem');
1073 if (!empty($submittedLineItems) && is_array($submittedLineItems)) {
1074 $submittedLineItems[0] = $lineItem;
1075 }
1076 else {
1077 $submittedLineItems = [$lineItem];
1078 }
1079 $submittedLineItems = array_filter($submittedLineItems);
1080 $this->set('lineItem', $submittedLineItems);
1081 $this->set('lineItemParticipantsCount', [$primaryParticipantCount]);
1082 }
1083
1084 $this->set('amount', $params['amount']);
1085 $this->set('amount_level', $params['amount_level']);
1086
1087 // generate and set an invoiceID for this transaction
1088 $invoiceID = md5(uniqid(rand(), TRUE));
1089 $this->set('invoiceID', $invoiceID);
1090
1091 if ($this->_paymentProcessor) {
1092 $payment = $this->_paymentProcessor['object'];
1093 $payment->setBaseReturnUrl('civicrm/event/register');
1094 }
1095
1096 // ContributeMode is a deprecated concept. It is short-hand for a bunch of
1097 // assumptions we are working to remove.
1098 $this->set('contributeMode', 'direct');
1099
1100 if ($this->_values['event']['is_monetary']) {
1101 $params['currencyID'] = $config->defaultCurrency;
1102 $params['invoiceID'] = $invoiceID;
1103 }
1104 $this->_params = $this->get('params');
1105 // Set the button so we know what
1106 $params['button'] = $this->controller->getButtonName();
1107 if (!empty($this->_params) && is_array($this->_params)) {
1108 $this->_params[0] = $params;
1109 }
1110 else {
1111 $this->_params = [];
1112 $this->_params[] = $params;
1113 }
1114 $this->set('params', $this->_params);
1115 if ($this->_paymentProcessor &&
1116 // Actually we don't really need to check if it supports pre-approval - we could just call
1117 // it regardless as the function we call re-acts tot the rests of the preApproval call.
1118 $this->_paymentProcessor['object']->supports('preApproval')
1119 && !$this->_allowWaitlist &&
1120 !$this->_requireApproval
1121 ) {
1122
1123 // The concept of contributeMode is deprecated - but still needs removal from the message templates.
1124 $this->set('contributeMode', 'express');
1125
1126 // Send Event Name & Id in Params
1127 $params['eventName'] = $this->_values['event']['title'];
1128 $params['eventId'] = $this->_values['event']['id'];
1129
1130 $params['cancelURL'] = CRM_Utils_System::url('civicrm/event/register',
1131 "_qf_Register_display=1&qfKey={$this->controller->_key}",
1132 TRUE, NULL, FALSE
1133 );
1134 if (!empty($params['additional_participants'])) {
1135 $urlArgs = "_qf_Participant_1_display=1&rfp=1&qfKey={$this->controller->_key}";
1136 }
1137 else {
1138 $urlArgs = "_qf_Confirm_display=1&rfp=1&qfKey={$this->controller->_key}";
1139 }
1140 $params['returnURL'] = CRM_Utils_System::url('civicrm/event/register',
1141 $urlArgs,
1142 TRUE, NULL, FALSE
1143 );
1144 $params['invoiceID'] = $invoiceID;
1145
1146 $params['component'] = 'event';
1147 // This code is duplicated multiple places and should be consolidated.
1148 $params = $this->prepareParamsForPaymentProcessor($params);
1149 $this->handlePreApproval($params);
1150 }
1151 elseif ($this->_paymentProcessor &&
1152 (int) $this->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_NOTIFY
1153 ) {
1154 // The concept of contributeMode is deprecated - but still needs removal from the message templates.
1155 $this->set('contributeMode', 'notify');
1156 }
1157 }
1158 else {
1159 $params['description'] = ts('Online Event Registration') . ' ' . $this->_values['event']['title'];
1160
1161 $this->_params = [];
1162 $this->_params[] = $params;
1163 $this->set('params', $this->_params);
1164
1165 if (
1166 empty($params['additional_participants'])
1167 // CRM-11182 - Optional confirmation screen
1168 && !$this->_values['event']['is_confirm_enabled']
1169 ) {
1170 $this->processRegistration($this->_params);
1171 }
1172 }
1173
1174 // If registering > 1 participant, give status message
1175 if (!empty($params['additional_participants'])) {
1176 $statusMsg = ts('Registration information for participant 1 has been saved.');
1177 CRM_Core_Session::setStatus($statusMsg, ts('Saved'), 'success');
1178 }
1179 }
1180
1181 /**
1182 * Method to check if the user is already registered for the event.
1183 * and if result found redirect to the event info page
1184 *
1185 * @param array $fields
1186 * The input form values(anonymous user).
1187 * @param CRM_Event_Form_Registration_Register $form
1188 * Event data.
1189 * @param bool $isAdditional
1190 * Treat isAdditional participants a bit differently.
1191 *
1192 * @return int
1193 */
1194 public static function checkRegistration($fields, $form, $isAdditional = FALSE) {
1195 // CRM-3907, skip check for preview registrations
1196 // CRM-4320 participant need to walk wizard
1197 if (
1198 ($form->getPaymentMode() === 'test' || $form->_allowConfirmation)
1199 ) {
1200 return FALSE;
1201 }
1202
1203 $contactID = self::getRegistrationContactID($fields, $form, $isAdditional);
1204
1205 if ($contactID) {
1206 $participant = new CRM_Event_BAO_Participant();
1207 $participant->contact_id = $contactID;
1208 $participant->event_id = $form->_values['event']['id'];
1209 if (!empty($fields['participant_role']) && is_numeric($fields['participant_role'])) {
1210 $participant->role_id = $fields['participant_role'];
1211 }
1212 else {
1213 $participant->role_id = $form->_values['event']['default_role_id'];
1214 }
1215 $participant->is_test = 0;
1216 $participant->find();
1217 // Event#30 - Anyone whose status type has `is_counted` OR is on the waitlist should be considered as registered.
1218 $statusTypes = CRM_Event_PseudoConstant::participantStatus(NULL, 'is_counted = 1') + CRM_Event_PseudoConstant::participantStatus(NULL, "name = 'On waitlist'");
1219 while ($participant->fetch()) {
1220 if (array_key_exists($participant->status_id, $statusTypes)) {
1221 if (!$isAdditional && !$form->_values['event']['allow_same_participant_emails']) {
1222 $registerUrl = CRM_Utils_System::url('civicrm/event/register',
1223 "reset=1&id={$form->_values['event']['id']}&cid=0"
1224 );
1225 if ($form->_pcpId) {
1226 $registerUrl .= '&pcpId=' . $form->_pcpId;
1227 }
1228 $registrationType = (CRM_Event_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'participant_status_id', 'On waitlist') == $participant->status_id) ? 'waitlisted' : 'registered';
1229 if ($registrationType == 'waitlisted') {
1230 $status = ts("It looks like you are already waitlisted for this event. If you want to change your registration, or you feel that you've received this message in error, please contact the site administrator.");
1231 }
1232 else {
1233 $status = ts("It looks like you are already registered for this event. If you want to change your registration, or you feel that you've received this message in error, please contact the site administrator.");
1234 }
1235 $status .= ' ' . ts('You can also <a href="%1">register another participant</a>.', [1 => $registerUrl]);
1236 CRM_Core_Session::singleton()->setStatus($status, '', 'alert');
1237 $url = CRM_Utils_System::url('civicrm/event/info',
1238 "reset=1&id={$form->_values['event']['id']}&noFullMsg=true"
1239 );
1240 if ($form->_action & CRM_Core_Action::PREVIEW) {
1241 $url .= '&action=preview';
1242 }
1243
1244 if ($form->_pcpId) {
1245 $url .= '&pcpId=' . $form->_pcpId;
1246 }
1247
1248 CRM_Utils_System::redirect($url);
1249 }
1250
1251 if ($isAdditional) {
1252 $status = ts("It looks like this participant is already registered for this event. If you want to change your registration, or you feel that you've received this message in error, please contact the site administrator.");
1253 CRM_Core_Session::singleton()->setStatus($status, '', 'alert');
1254 return $participant->id;
1255 }
1256 }
1257 }
1258 }
1259 }
1260
1261 }