f4a7c8baa266943c4c33aeb430c2462f7fa8fc31
[civicrm-core.git] / CRM / Event / Form / Registration / Register.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
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 *
31 * @package CRM
32 * @copyright CiviCRM LLC (c) 2004-2015
33 * $Id$
34 *
35 */
36
37 /**
38 * This class generates form components for processing Event
39 *
40 */
41 class CRM_Event_Form_Registration_Register extends CRM_Event_Form_Registration {
42
43 /**
44 * The fields involved in this page.
45 */
46 public $_fields;
47
48 /**
49 * The defaults involved in this page.
50 */
51 public $_defaults;
52
53 /**
54 * The status message that user view.
55 */
56 protected $_waitlistMsg = NULL;
57 protected $_requireApprovalMsg = NULL;
58
59 public $_quickConfig = NULL;
60
61 /**
62 * Allow developer to use hook_civicrm_buildForm()
63 * to override the registration dupe check
64 * CRM-7604
65 */
66 public $_skipDupeRegistrationCheck = FALSE;
67
68 public $_paymentProcessorID;
69
70 /**
71 * @var boolean determines if fee block should be shown or hidden
72 */
73 public $_noFees;
74
75 /**
76 * Array of payment related fields to potentially display on this form (generally credit card or debit card fields). This is rendered via billingBlock.tpl
77 * @var array
78 */
79 public $_paymentFields = array();
80
81 /**
82 * Set variables up before form is built.
83 *
84 * @return void
85 */
86 public function preProcess() {
87 parent::preProcess();
88
89 //CRM-4320.
90 //here we can't use parent $this->_allowWaitlist as user might
91 //walk back and we might set this value in this postProcess.
92 //(we set when spaces < group count and want to allow become part of waiting )
93 $eventFull = CRM_Event_BAO_Participant::eventFull($this->_eventId, FALSE, CRM_Utils_Array::value('has_waitlist', $this->_values['event']));
94
95 // Get payment processors if appropriate for this event
96 // We hide the payment fields if the event is full or requires approval,
97 // and the current user has not yet been approved CRM-12279
98 $this->_noFees = (($eventFull || $this->_requireApproval) && !$this->_allowConfirmation);
99 $this->_paymentProcessors = $this->_noFees ? array() : $this->get('paymentProcessors');
100 $this->preProcessPaymentOptions();
101
102 $this->_allowWaitlist = FALSE;
103 if ($eventFull && !$this->_allowConfirmation && !empty($this->_values['event']['has_waitlist'])) {
104 $this->_allowWaitlist = TRUE;
105 $this->_waitlistMsg = CRM_Utils_Array::value('waitlist_text', $this->_values['event']);
106 if (!$this->_waitlistMsg) {
107 $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.');
108 }
109 }
110 $this->set('allowWaitlist', $this->_allowWaitlist);
111
112 //To check if the user is already registered for the event(CRM-2426)
113 if (!$this->_skipDupeRegistrationCheck) {
114 self::checkRegistration(NULL, $this);
115 }
116
117 $this->assign('availableRegistrations', $this->_availableRegistrations);
118
119 // get the participant values from EventFees.php, CRM-4320
120 if ($this->_allowConfirmation) {
121 CRM_Event_Form_EventFees::preProcess($this);
122 }
123 }
124
125 /**
126 * Set default values for the form. For edit/view mode
127 * the default values are retrieved from the database
128 * Adding discussion from CRM-11915 as code comments
129 * When multiple payment processors are configured for a event and user does any selection changes for them on online event registeration page :
130 * The 'Register' page gets loaded through ajax and following happens :
131 * the setDefaults function is called with the variable _ppType set with selected payment processor type,
132 * so in the 'if' condition checked whether the selected payment processor's billing mode is of 'billing form mode'. If its not, don't setDefaults for billing form and return instead.
133 * - For payment processors of billing mode 'Notify' - return from setDefaults before the code for billing profile population execution .
134 * (done this is because for payment processors with 'Notify' mode billing profile form doesn't get rendered on UI)
135 *
136 * @return void
137 */
138 public function setDefaultValues() {
139 $this->_defaults = array();
140 $contactID = $this->getContactID();
141 $billingDefaults = $this->getProfileDefaults('Billing', $contactID);
142 $this->_defaults = array_merge($this->_defaults, $billingDefaults);
143
144 $config = CRM_Core_Config::singleton();
145 // set default country from config if no country set
146 // note the effect of this is to set the billing country to default to the site default
147 // country if the person has an address but no country (for anonymous country is set above)
148 // this could have implications if the billing profile is filled but hidden.
149 // this behaviour has been in place for a while but the use of js to hide things has increased
150 if (empty($this->_defaults["billing_country_id-{$this->_bltID}"])) {
151 $this->_defaults["billing_country_id-{$this->_bltID}"] = $config->defaultContactCountry;
152 }
153
154 // set default state/province from config if no state/province set
155 if (empty($this->_defaults["billing_state_province_id-{$this->_bltID}"])) {
156 $this->_defaults["billing_state_province_id-{$this->_bltID}"] = $config->defaultContactStateProvince;
157 }
158
159 if ($contactID) {
160 $fields = array();
161
162 if (!empty($this->_fields)) {
163 $removeCustomFieldTypes = array('Participant');
164 foreach ($this->_fields as $name => $dontCare) {
165 if (substr($name, 0, 7) == 'custom_') {
166 $id = substr($name, 7);
167 if (!$this->_allowConfirmation &&
168 !CRM_Core_BAO_CustomGroup::checkCustomField($id, $removeCustomFieldTypes)
169 ) {
170 continue;
171 }
172 // ignore component fields
173 }
174 elseif ((substr($name, 0, 12) == 'participant_')) {
175 continue;
176 }
177 $fields[$name] = 1;
178 }
179 }
180 }
181
182 if (!empty($fields)) {
183 CRM_Core_BAO_UFGroup::setProfileDefaults($contactID, $fields, $this->_defaults);
184 }
185
186 // Set default payment processor as default payment_processor radio button value
187 if (!empty($this->_paymentProcessors)) {
188 foreach ($this->_paymentProcessors as $pid => $value) {
189 if (!empty($value['is_default'])) {
190 $this->_defaults['payment_processor_id'] = $pid;
191 }
192 }
193 }
194
195 //if event is monetary and pay later is enabled and payment
196 //processor is not available then freeze the pay later checkbox with
197 //default check
198 if (!empty($this->_values['event']['is_pay_later']) &&
199 !is_array($this->_paymentProcessor)
200 ) {
201 $this->_defaults['is_pay_later'] = 1;
202 }
203
204 //set custom field defaults
205 if (!empty($this->_fields)) {
206 //load default campaign from page.
207 if (array_key_exists('participant_campaign_id', $this->_fields)) {
208 $this->_defaults['participant_campaign_id'] = CRM_Utils_Array::value('campaign_id',
209 $this->_values['event']
210 );
211 }
212
213 foreach ($this->_fields as $name => $field) {
214 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($name)) {
215 // fix for CRM-1743
216 if (!isset($this->_defaults[$name])) {
217 CRM_Core_BAO_CustomField::setProfileDefaults($customFieldID, $name, $this->_defaults,
218 NULL, CRM_Profile_Form::MODE_REGISTER
219 );
220 }
221 }
222 }
223 }
224
225 //fix for CRM-3088, default value for discount set.
226 $discountId = NULL;
227 if (!empty($this->_values['discount'])) {
228 $discountId = CRM_Core_BAO_Discount::findSet($this->_eventId, 'civicrm_event');
229 if ($discountId) {
230 if (isset($this->_values['event']['default_discount_fee_id'])) {
231 $discountKey = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionValue',
232 $this->_values['event']['default_discount_fee_id'],
233 'weight', 'id'
234 );
235
236 $this->_defaults['amount'] = key(array_slice($this->_values['discount'][$discountId],
237 $discountKey - 1, $discountKey, TRUE
238 ));
239 }
240 }
241 }
242
243 // add this event's default participant role to defaults array
244 // (for cases where participant_role field is included in form via profile)
245 if ($this->_values['event']['default_role_id']) {
246 $this->_defaults['participant_role']
247 = $this->_defaults['participant_role_id'] = $this->_values['event']['default_role_id'];
248 }
249 if ($this->_priceSetId && !empty($this->_feeBlock)) {
250 foreach ($this->_feeBlock as $key => $val) {
251 if (empty($val['options'])) {
252 continue;
253 }
254 $optionFullIds = CRM_Utils_Array::value('option_full_ids', $val, array());
255 foreach ($val['options'] as $keys => $values) {
256 if ($values['is_default'] && empty($values['is_full'])) {
257
258 if ($val['html_type'] == 'CheckBox') {
259 $this->_defaults["price_{$key}"][$keys] = 1;
260 }
261 else {
262 $this->_defaults["price_{$key}"] = $keys;
263 }
264 }
265 }
266 $unsetSubmittedOptions[$val['id']] = $optionFullIds;
267 }
268 //reset values for all options those are full.
269 CRM_Event_Form_Registration::resetElementValue($unsetSubmittedOptions, $this);
270 }
271
272 //set default participant fields, CRM-4320.
273 $hasAdditionalParticipants = FALSE;
274 if ($this->_allowConfirmation) {
275 $this->_contactId = $contactID;
276 $this->_discountId = $discountId;
277 $forcePayLater = CRM_Utils_Array::value('is_pay_later', $this->_defaults, FALSE);
278 $this->_defaults = array_merge($this->_defaults, CRM_Event_Form_EventFees::setDefaultValues($this));
279 $this->_defaults['is_pay_later'] = $forcePayLater;
280
281 if ($this->_additionalParticipantIds) {
282 $hasAdditionalParticipants = TRUE;
283 $this->_defaults['additional_participants'] = count($this->_additionalParticipantIds);
284 }
285 }
286 $this->assign('hasAdditionalParticipants', $hasAdditionalParticipants);
287
288 // //hack to simplify credit card entry for testing
289 // $this->_defaults['credit_card_type'] = 'Visa';
290 // $this->_defaults['credit_card_number'] = '4807731747657838';
291 // $this->_defaults['cvv2'] = '000';
292 // $this->_defaults['credit_card_exp_date'] = array( 'Y' => '2010', 'M' => '05' );
293
294 // to process Custom data that are appended to URL
295 $getDefaults = CRM_Core_BAO_CustomGroup::extractGetParams($this, "'Contact', 'Individual', 'Contribution', 'Participant'");
296 if (!empty($getDefaults)) {
297 $this->_defaults = array_merge($this->_defaults, $getDefaults);
298 }
299
300 return $this->_defaults;
301 }
302
303 /**
304 * Build the form object.
305 *
306 * @return void
307 */
308 public function buildQuickForm() {
309 // build profiles first so that we can determine address fields etc
310 // and then show copy address checkbox
311 $this->buildCustom($this->_values['custom_pre_id'], 'customPre');
312 $this->buildCustom($this->_values['custom_post_id'], 'customPost');
313
314 if (!empty($this->_fields) && !empty($this->_values['custom_pre_id'])) {
315 $profileAddressFields = array();
316 foreach ($this->_fields as $key => $value) {
317 CRM_Core_BAO_UFField::assignAddressField($key, $profileAddressFields, array(
318 'uf_group_id' => $this->_values['custom_pre_id'],
319 ));
320 }
321 $this->set('profileAddressFields', $profileAddressFields);
322 }
323
324 CRM_Core_Payment_ProcessorForm::buildQuickForm($this);
325
326 $contactID = $this->getContactID();
327 if ($contactID) {
328 $this->assign('contact_id', $contactID);
329 $this->assign('display_name', CRM_Contact_BAO_Contact::displayName($contactID));
330 }
331
332 $this->add('hidden', 'scriptFee', NULL);
333 $this->add('hidden', 'scriptArray', NULL);
334
335 $bypassPayment = $allowGroupOnWaitlist = $isAdditionalParticipants = FALSE;
336 if ($this->_values['event']['is_multiple_registrations']) {
337 // don't allow to add additional during confirmation if not preregistered.
338 if (!$this->_allowConfirmation || $this->_additionalParticipantIds) {
339 // Hardcode maximum number of additional participants here for now. May need to make this configurable per event.
340 // Label is value + 1, since the code sees this is ADDITIONAL participants (in addition to "self")
341 $additionalOptions = array(
342 '' => '1',
343 1 => '2',
344 2 => '3',
345 3 => '4',
346 4 => '5',
347 5 => '6',
348 6 => '7',
349 7 => '8',
350 8 => '9',
351 9 => '10',
352 );
353 $element = $this->add('select', 'additional_participants',
354 ts('How many people are you registering?'),
355 $additionalOptions,
356 NULL,
357 array('onChange' => "allowParticipant()")
358 );
359 $isAdditionalParticipants = TRUE;
360 }
361 }
362
363 //hack to allow group to register w/ waiting
364 if ((!empty($this->_values['event']['is_multiple_registrations']) ||
365 $this->_priceSetId
366 ) &&
367 !$this->_allowConfirmation &&
368 is_numeric($this->_availableRegistrations) && !empty($this->_values['event']['has_waitlist'])
369 ) {
370 $bypassPayment = TRUE;
371 //case might be group become as a part of waitlist.
372 //If not waitlist then they require admin approve.
373 $allowGroupOnWaitlist = TRUE;
374 $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.", array(1 => $this->_availableRegistrations));
375
376 if ($this->_requireApproval) {
377 $this->_requireApprovalMsg = CRM_Utils_Array::value('approval_req_text', $this->_values['event'],
378 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.')
379 );
380 }
381 }
382
383 //case where only approval needed - no waitlist.
384 if ($this->_requireApproval &&
385 !$this->_allowWaitlist && !$bypassPayment
386 ) {
387 $this->_requireApprovalMsg = CRM_Utils_Array::value('approval_req_text', $this->_values['event'],
388 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.')
389 );
390 }
391
392 //lets display status to primary page only.
393 $this->assign('waitlistMsg', $this->_waitlistMsg);
394 $this->assign('requireApprovalMsg', $this->_requireApprovalMsg);
395 $this->assign('allowGroupOnWaitlist', $allowGroupOnWaitlist);
396 $this->assign('isAdditionalParticipants', $isAdditionalParticipants);
397
398 //lets get js on two different qf elements.
399 $showHidePayfieldName = NULL;
400 $showHidePaymentInformation = FALSE;
401 if ($this->_values['event']['is_monetary']) {
402 self::buildAmount($this);
403 }
404
405 $pps = array();
406 //@todo this processor adding fn is another one duplicated on contribute - a shared
407 // common class would make this sort of thing extractable
408 $onlinePaymentProcessorEnabled = FALSE;
409 if (!empty($this->_paymentProcessors)) {
410 foreach ($this->_paymentProcessors as $key => $name) {
411 if ($name['billing_mode'] == 1) {
412 $onlinePaymentProcessorEnabled = TRUE;
413 }
414 $pps[$key] = $name['name'];
415 }
416 }
417 if ($this->getContactID() === 0 && !$this->_values['event']['is_multiple_registrations']) {
418 //@todo we are blocking for multiple registrations because we haven't tested
419 $this->addCidZeroOptions($onlinePaymentProcessorEnabled);
420 }
421 if (!empty($this->_values['event']['is_pay_later']) &&
422 ($this->_allowConfirmation || (!$this->_requireApproval && !$this->_allowWaitlist))
423 ) {
424 $pps[0] = $this->_values['event']['pay_later_text'];
425 }
426
427 if ($this->_values['event']['is_monetary']) {
428 if (count($pps) > 1) {
429 $this->addRadio('payment_processor_id', ts('Payment Method'), $pps,
430 NULL, "&nbsp;"
431 );
432 }
433 elseif (!empty($pps)) {
434 $ppKeys = array_keys($pps);
435 $currentPP = array_pop($ppKeys);
436 $this->addElement('hidden', 'payment_processor_id', $currentPP);
437 }
438 }
439
440 //lets add some qf element to bypass payment validations, CRM-4320
441 if ($bypassPayment) {
442 $this->addElement('hidden', 'bypass_payment', NULL, array('id' => 'bypass_payment'));
443 }
444 $this->assign('bypassPayment', $bypassPayment);
445 $this->assign('showHidePaymentInformation', $showHidePaymentInformation);
446
447 $userID = $this->getContactID();
448
449 if (!$userID) {
450 $createCMSUser = FALSE;
451
452 if ($this->_values['custom_pre_id']) {
453 $profileID = $this->_values['custom_pre_id'];
454 $createCMSUser = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $profileID, 'is_cms_user');
455 }
456
457 if (!$createCMSUser &&
458 $this->_values['custom_post_id']
459 ) {
460 if (!is_array($this->_values['custom_post_id'])) {
461 $profileIDs = array($this->_values['custom_post_id']);
462 }
463 else {
464 $profileIDs = $this->_values['custom_post_id'];
465 }
466 foreach ($profileIDs as $pid) {
467 if (CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $pid, 'is_cms_user')) {
468 $profileID = $pid;
469 $createCMSUser = TRUE;
470 break;
471 }
472 }
473 }
474
475 if ($createCMSUser) {
476 CRM_Core_BAO_CMSUser::buildForm($this, $profileID, TRUE);
477 }
478 }
479
480 //we have to load confirm contribution button in template
481 //when multiple payment processor as the user
482 //can toggle with payment processor selection
483 $billingModePaymentProcessors = 0;
484 if (!CRM_Utils_System::isNull($this->_paymentProcessors)) {
485 foreach ($this->_paymentProcessors as $key => $values) {
486 if ($values['billing_mode'] == CRM_Core_Payment::BILLING_MODE_BUTTON) {
487 $billingModePaymentProcessors++;
488 }
489 }
490 }
491
492 if ($billingModePaymentProcessors && count($this->_paymentProcessors) == $billingModePaymentProcessors) {
493 $allAreBillingModeProcessors = TRUE;
494 }
495 else {
496 $allAreBillingModeProcessors = FALSE;
497 }
498
499 if (!$allAreBillingModeProcessors || !empty($this->_values['event']['is_pay_later']) || $bypassPayment
500 ) {
501
502 //freeze button to avoid multiple calls.
503 $js = NULL;
504
505 if (empty($this->_values['event']['is_monetary'])) {
506 $js = array('onclick' => "return submitOnce(this,'" . $this->_name . "','" . ts('Processing') . "');");
507 }
508
509 // CRM-11182 - Optional confirmation screen
510 // Change button label depending on whether the next action is confirm or register
511 if (
512 !$this->_values['event']['is_multiple_registrations']
513 && !$this->_values['event']['is_monetary']
514 && !$this->_values['event']['is_confirm_enabled']
515 ) {
516 $buttonLabel = ts('Register');
517 }
518 else {
519 $buttonLabel = ts('Continue');
520 }
521
522 $this->addButtons(array(
523 array(
524 'type' => 'upload',
525 'name' => $buttonLabel,
526 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
527 'isDefault' => TRUE,
528 'js' => $js,
529 ),
530 )
531 );
532 }
533
534 $this->addFormRule(array('CRM_Event_Form_Registration_Register', 'formRule'), $this);
535 $this->unsavedChangesWarn = TRUE;
536
537 // add pcp fields
538 if ($this->_pcpId) {
539 CRM_PCP_BAO_PCP::buildPcp($this->_pcpId, $this);
540 }
541 }
542
543 /**
544 * Build the radio/text form elements for the amount field
545 *
546 * @param CRM_Core_Form $form
547 * Form object.
548 * @param bool $required
549 * True if you want to add formRule.
550 * @param int $discountId
551 * Discount id for the event.
552 *
553 * @return void
554 */
555 static public function buildAmount(&$form, $required = TRUE, $discountId = NULL) {
556 // build amount only when needed, skip incase of event full and waitlisting is enabled
557 // and few other conditions check preProcess()
558 if (property_exists($form, '_noFees') && $form->_noFees) {
559 return;
560 }
561
562 //if payment done, no need to build the fee block.
563 if (!empty($form->_paymentId)) {
564 //fix to display line item in update mode.
565 $form->assign('priceSet', isset($form->_priceSet) ? $form->_priceSet : NULL);
566 return;
567 }
568
569 $feeFields = CRM_Utils_Array::value('fee', $form->_values);
570
571 if (is_array($feeFields)) {
572 $form->_feeBlock = &$form->_values['fee'];
573 }
574
575 //check for discount.
576 $discountedFee = CRM_Utils_Array::value('discount', $form->_values);
577 if (is_array($discountedFee) && !empty($discountedFee)) {
578 if (!$discountId) {
579 $form->_discountId = $discountId = CRM_Core_BAO_Discount::findSet($form->_eventId, 'civicrm_event');
580 }
581 if ($discountId) {
582 $form->_feeBlock = &$form->_values['discount'][$discountId];
583 }
584 }
585 if (!is_array($form->_feeBlock)) {
586 $form->_feeBlock = array();
587 }
588
589 //its time to call the hook.
590 CRM_Utils_Hook::buildAmount('event', $form, $form->_feeBlock);
591
592 //reset required if participant is skipped.
593 $button = substr($form->controller->getButtonName(), -4);
594 if ($required && $button == 'skip') {
595 $required = FALSE;
596 }
597
598 $className = CRM_Utils_System::getClassName($form);
599
600 //build the priceset fields.
601 if (isset($form->_priceSetId) && $form->_priceSetId) {
602
603 //format price set fields across option full.
604 self::formatFieldsForOptionFull($form);
605
606 if (!empty($form->_priceSet['is_quick_config'])) {
607 $form->_quickConfig = $form->_priceSet['is_quick_config'];
608 }
609 $form->add('hidden', 'priceSetId', $form->_priceSetId);
610
611 // CRM-14492 Admin price fields should show up on event registration if user has 'administer CiviCRM' permissions
612 $adminFieldVisible = FALSE;
613 if (CRM_Core_Permission::check('administer CiviCRM')) {
614 $adminFieldVisible = TRUE;
615 }
616
617 foreach ($form->_feeBlock as $field) {
618 // public AND admin visibility fields are included for back-office registration and back-office change selections
619 if (CRM_Utils_Array::value('visibility', $field) == 'public' ||
620 (CRM_Utils_Array::value('visibility', $field) == 'admin' && $adminFieldVisible == TRUE) ||
621 $className == 'CRM_Event_Form_Participant' ||
622 $className == 'CRM_Event_Form_ParticipantFeeSelection'
623 ) {
624 $fieldId = $field['id'];
625 $elementName = 'price_' . $fieldId;
626
627 $isRequire = CRM_Utils_Array::value('is_required', $field);
628 if ($button == 'skip') {
629 $isRequire = FALSE;
630 }
631
632 //user might modified w/ hook.
633 $options = CRM_Utils_Array::value('options', $field);
634 if (!is_array($options)) {
635 continue;
636 }
637
638 $optionFullIds = CRM_Utils_Array::value('option_full_ids', $field, array());
639
640 //soft suppress required rule when option is full.
641 if (!empty($optionFullIds) && (count($options) == count($optionFullIds))) {
642 $isRequire = FALSE;
643 }
644
645 //build the element.
646 CRM_Price_BAO_PriceField::addQuickFormElement($form,
647 $elementName,
648 $fieldId,
649 FALSE,
650 $isRequire,
651 NULL,
652 $options,
653 $optionFullIds
654 );
655 }
656 }
657 $form->assign('priceSet', $form->_priceSet);
658 }
659 else {
660 $eventFeeBlockValues = array();
661 foreach ($form->_feeBlock as $fee) {
662 if (is_array($fee)) {
663
664 //CRM-7632, CRM-6201
665 $totalAmountJs = NULL;
666 if ($className == 'CRM_Event_Form_Participant') {
667 $totalAmountJs = array('onClick' => "fillTotalAmount(" . $fee['value'] . ")");
668 }
669
670 $eventFeeBlockValues['amount_id_' . $fee['amount_id']] = $fee['value'];
671 $elements[] = &$form->createElement('radio', NULL, '',
672 CRM_Utils_Money::format($fee['value']) . ' ' .
673 $fee['label'],
674 $fee['amount_id'],
675 $totalAmountJs
676 );
677 }
678 }
679 $form->assign('eventFeeBlockValues', json_encode($eventFeeBlockValues));
680
681 $form->_defaults['amount'] = CRM_Utils_Array::value('default_fee_id', $form->_values['event']);
682 $element = &$form->addGroup($elements, 'amount', ts('Event Fee(s)'), '<br />');
683 if (isset($form->_online) && $form->_online) {
684 $element->freeze();
685 }
686 if ($required) {
687 $form->addRule('amount', ts('Fee Level is a required field.'), 'required');
688 }
689 }
690 }
691
692 /**
693 * @param CRM_Core_Form $form
694 */
695 public static function formatFieldsForOptionFull(&$form) {
696 $priceSet = $form->get('priceSet');
697 $priceSetId = $form->get('priceSetId');
698 $defaultPricefieldIds = array();
699 if (!empty($form->_values['line_items'])) {
700 foreach ($form->_values['line_items'] as $lineItem) {
701 $defaultPricefieldIds[] = $lineItem['price_field_value_id'];
702 }
703 }
704 if (!$priceSetId ||
705 !is_array($priceSet) ||
706 empty($priceSet) || empty($priceSet['optionsMaxValueTotal'])
707 ) {
708 return;
709 }
710
711 $skipParticipants = $formattedPriceSetDefaults = array();
712 if (!empty($form->_allowConfirmation) && (isset($form->_pId) || isset($form->_additionalParticipantId))) {
713 $participantId = isset($form->_pId) ? $form->_pId : $form->_additionalParticipantId;
714 $pricesetDefaults = CRM_Event_Form_EventFees::setDefaultPriceSet($participantId,
715 $form->_eventId
716 );
717 // modify options full to respect the selected fields
718 // options on confirmation.
719 $formattedPriceSetDefaults = self::formatPriceSetParams($form, $pricesetDefaults);
720
721 // to skip current registered participants fields option count on confirmation.
722 $skipParticipants[] = $form->_participantId;
723 if (!empty($form->_additionalParticipantIds)) {
724 $skipParticipants = array_merge($skipParticipants, $form->_additionalParticipantIds);
725 }
726 }
727
728 $className = CRM_Utils_System::getClassName($form);
729
730 //get the current price event price set options count.
731 $currentOptionsCount = self::getPriceSetOptionCount($form);
732 $recordedOptionsCount = CRM_Event_BAO_Participant::priceSetOptionsCount($form->_eventId, $skipParticipants);
733 $optionFullTotalAmount = 0;
734 $currentParticipantNo = (int) substr($form->_name, 12);
735 foreach ($form->_feeBlock as & $field) {
736 $optionFullIds = array();
737 $fieldId = $field['id'];
738 if (!is_array($field['options'])) {
739 continue;
740 }
741 foreach ($field['options'] as & $option) {
742 $optId = $option['id'];
743 $count = CRM_Utils_Array::value('count', $option, 0);
744 $maxValue = CRM_Utils_Array::value('max_value', $option, 0);
745 $dbTotalCount = CRM_Utils_Array::value($optId, $recordedOptionsCount, 0);
746 $currentTotalCount = CRM_Utils_Array::value($optId, $currentOptionsCount, 0);
747
748 $totalCount = $currentTotalCount + $dbTotalCount;
749 $isFull = FALSE;
750 if ($maxValue &&
751 (($totalCount >= $maxValue) &&
752 (empty($form->_lineItem[$currentParticipantNo][$optId]['price_field_id']) || $dbTotalCount >= $maxValue))
753 ) {
754 $isFull = TRUE;
755 $optionFullIds[$optId] = $optId;
756 if ($field['html_type'] != 'Select') {
757 if (in_array($optId, $defaultPricefieldIds)) {
758 $optionFullTotalAmount += CRM_Utils_Array::value('amount', $option);
759 }
760 }
761 else {
762 if (!empty($defaultPricefieldIds) && in_array($optId, $defaultPricefieldIds)) {
763 unset($optionFullIds[$optId]);
764 }
765 }
766 }
767 //here option is not full,
768 //but we don't want to allow participant to increase
769 //seats at the time of re-walking registration.
770 if ($count &&
771 !empty($form->_allowConfirmation) &&
772 !empty($formattedPriceSetDefaults)
773 ) {
774 if (empty($formattedPriceSetDefaults["price_{$field}"]) || empty($formattedPriceSetDefaults["price_{$fieldId}"][$optId])) {
775 $optionFullIds[$optId] = $optId;
776 $isFull = TRUE;
777 }
778 }
779 $option['is_full'] = $isFull;
780 $option['db_total_count'] = $dbTotalCount;
781 $option['total_option_count'] = $dbTotalCount + $currentTotalCount;
782 }
783
784 //ignore option full for offline registration.
785 if ($className == 'CRM_Event_Form_Participant') {
786 $optionFullIds = array();
787 }
788
789 //finally get option ids in.
790 $field['option_full_ids'] = $optionFullIds;
791 }
792 $form->assign('optionFullTotalAmount', $optionFullTotalAmount);
793 }
794
795 /**
796 * Global form rule.
797 *
798 * @param array $fields
799 * The input form values.
800 * @param array $files
801 * The uploaded files if any.
802 * @param $self
803 *
804 *
805 * @return bool|array
806 * true if no errors, else array of errors
807 */
808 public static function formRule($fields, $files, $self) {
809 $errors = array();
810 //check that either an email or firstname+lastname is included in the form(CRM-9587)
811 self::checkProfileComplete($fields, $errors, $self->_eventId);
812 //To check if the user is already registered for the event(CRM-2426)
813 if (!$self->_skipDupeRegistrationCheck) {
814 self::checkRegistration($fields, $self);
815 }
816 //check for availability of registrations.
817 if (!$self->_allowConfirmation && empty($fields['bypass_payment']) &&
818 is_numeric($self->_availableRegistrations) &&
819 CRM_Utils_Array::value('additional_participants', $fields) >= $self->_availableRegistrations
820 ) {
821 $errors['additional_participants'] = ts("There is only enough space left on this event for %1 participant(s).", array(1 => $self->_availableRegistrations));
822 }
823
824 // during confirmation don't allow to increase additional participants, CRM-4320
825 if ($self->_allowConfirmation && !empty($fields['additional_participants']) &&
826 is_array($self->_additionalParticipantIds) &&
827 $fields['additional_participants'] > count($self->_additionalParticipantIds)
828 ) {
829 $errors['additional_participants'] = ts("Oops. 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.", array(1 => count($self->_additionalParticipantIds)));
830 }
831
832 //don't allow to register w/ waiting if enough spaces available.
833 if (!empty($fields['bypass_payment'])) {
834 if (!is_numeric($self->_availableRegistrations) ||
835 (empty($fields['priceSetId']) && CRM_Utils_Array::value('additional_participants', $fields) < $self->_availableRegistrations)
836 ) {
837 $errors['bypass_payment'] = ts("Oops. There are enough available spaces in this event. You can not add yourself to the waiting list.");
838 }
839 }
840
841 if (!empty($fields['additional_participants']) &&
842 !CRM_Utils_Rule::positiveInteger($fields['additional_participants'])
843 ) {
844 $errors['additional_participants'] = ts('Please enter a whole number for Number of additional people.');
845 }
846
847 // priceset validations
848 if (!empty($fields['priceSetId']) &&
849 !$self->_requireApproval && !$self->_allowWaitlist
850 ) {
851 //format params.
852 $formatted = self::formatPriceSetParams($self, $fields);
853 $ppParams = array($formatted);
854 $priceSetErrors = self::validatePriceSet($self, $ppParams);
855 $primaryParticipantCount = self::getParticipantCount($self, $ppParams);
856
857 //get price set fields errors in.
858 $errors = array_merge($errors, CRM_Utils_Array::value(0, $priceSetErrors, array()));
859
860 $totalParticipants = $primaryParticipantCount;
861 if (!empty($fields['additional_participants'])) {
862 $totalParticipants += $fields['additional_participants'];
863 }
864
865 if (empty($fields['bypass_payment']) &&
866 !$self->_allowConfirmation &&
867 is_numeric($self->_availableRegistrations) &&
868 $self->_availableRegistrations < $totalParticipants
869 ) {
870 $errors['_qf_default'] = ts("Only %1 Registrations available.", array(1 => $self->_availableRegistrations));
871 }
872
873 $lineItem = array();
874 CRM_Price_BAO_PriceSet::processAmount($self->_values['fee'], $fields, $lineItem);
875 if ($fields['amount'] < 0) {
876 $errors['_qf_default'] = ts('Event Fee(s) can not be less than zero. Please select the options accordingly');
877 }
878 }
879
880 if ($self->_values['event']['is_monetary']) {
881 if (empty($self->_requireApproval) && !empty($fields['amount']) && $fields['amount'] > 0 && !isset
882 ($fields['payment_processor_id'])) {
883 $errors['payment_processor_id'] = ts('Please select a Payment Method');
884 }
885 // return if this is express mode
886 if ($self->_paymentProcessor &&
887 $self->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON
888 ) {
889 if (!empty($fields[$self->_expressButtonName . '_x']) || !empty($fields[$self->_expressButtonName . '_y']) ||
890 CRM_Utils_Array::value($self->_expressButtonName, $fields)
891 ) {
892 return empty($errors) ? TRUE : $errors;
893 }
894 }
895
896 $isZeroAmount = $skipPaymentValidation = FALSE;
897 if (!empty($fields['priceSetId'])) {
898 if (CRM_Utils_Array::value('amount', $fields) == 0) {
899 $isZeroAmount = TRUE;
900 }
901 }
902 elseif (!empty($fields['amount']) &&
903 (isset($self->_values['discount'][$fields['amount']])
904 && CRM_Utils_Array::value('value', $self->_values['discount'][$fields['amount']]) == 0
905 )
906 ) {
907 $isZeroAmount = TRUE;
908 }
909 elseif (!empty($fields['amount']) &&
910 (isset($self->_values['fee'][$fields['amount']])
911 && CRM_Utils_Array::value('value', $self->_values['fee'][$fields['amount']]) == 0
912 )
913 ) {
914 $isZeroAmount = TRUE;
915 }
916
917 if ($isZeroAmount && !($self->_forcePayement && !empty($fields['additional_participants']))) {
918 $skipPaymentValidation = TRUE;
919 }
920
921 // also return if paylater mode or zero fees for valid members
922 if (!empty($fields['is_pay_later']) || !empty($fields['bypass_payment']) ||
923 $skipPaymentValidation ||
924 (!$self->_allowConfirmation && ($self->_requireApproval || $self->_allowWaitlist))
925 ) {
926 return empty($errors) ? TRUE : $errors;
927 }
928 if (!empty($self->_paymentFields)) {
929 CRM_Core_Form::validateMandatoryFields($self->_paymentFields, $fields, $errors);
930 }
931 CRM_Core_Payment_Form::validatePaymentInstrument($self->_paymentProcessorID, $fields, $errors, $self);
932 }
933
934 foreach (CRM_Contact_BAO_Contact::$_greetingTypes as $greeting) {
935 if ($greetingType = CRM_Utils_Array::value($greeting, $fields)) {
936 $customizedValue = CRM_Core_OptionGroup::getValue($greeting, 'Customized', 'name');
937 if ($customizedValue == $greetingType && empty($fields[$greeting . '_custom'])) {
938 $errors[$greeting . '_custom'] = ts('Custom %1 is a required field if %1 is of type Customized.',
939 array(1 => ucwords(str_replace('_', ' ', $greeting)))
940 );
941 }
942 }
943 }
944 return empty($errors) ? TRUE : $errors;
945 }
946
947 /**
948 * Check if profiles are complete when event registration occurs(CRM-9587)
949 */
950 public static function checkProfileComplete($fields, &$errors, $eventId) {
951 $email = '';
952 foreach ($fields as $fieldname => $fieldvalue) {
953 if (substr($fieldname, 0, 6) == 'email-' && $fieldvalue) {
954 $email = $fieldvalue;
955 }
956 }
957
958 if (!$email && !(!empty($fields['first_name']) && !empty($fields['last_name']))) {
959 $defaults = $params = array('id' => $eventId);
960 CRM_Event_BAO_Event::retrieve($params, $defaults);
961 $message = ts("Mandatory fields (first name and last name, OR email address) are missing from this form.");
962 $errors['_qf_default'] = $message;
963 }
964 }
965
966 /**
967 * Process the form submission.
968 *
969 *
970 * @return void
971 */
972 public function postProcess() {
973 // get the submitted form values.
974 $params = $this->controller->exportValues($this->_name);
975
976 //set as Primary participant
977 $params['is_primary'] = 1;
978
979 if ($this->_values['event']['is_pay_later']
980 && (!array_key_exists('hidden_processor', $params) || $params['payment_processor_id'] == 0)
981 ) {
982 $params['is_pay_later'] = 1;
983 }
984 else {
985 $params['is_pay_later'] = 0;
986 }
987
988 $this->set('is_pay_later', $params['is_pay_later']);
989
990 // assign pay later stuff
991 $this->_params['is_pay_later'] = CRM_Utils_Array::value('is_pay_later', $params, FALSE);
992 $this->assign('is_pay_later', $params['is_pay_later']);
993 if ($params['is_pay_later']) {
994 $this->assign('pay_later_text', $this->_values['event']['pay_later_text']);
995 $this->assign('pay_later_receipt', $this->_values['event']['pay_later_receipt']);
996 }
997
998 if (!$this->_allowConfirmation) {
999 // check if the participant is already registered
1000 if (!$this->_skipDupeRegistrationCheck) {
1001 $params['contact_id'] = self::checkRegistration($params, $this, FALSE, TRUE, TRUE);
1002 }
1003 }
1004
1005 if (!empty($params['image_URL'])) {
1006 CRM_Contact_BAO_Contact::processImageParams($params);
1007 }
1008
1009 //carry campaign to partcipants.
1010 if (array_key_exists('participant_campaign_id', $params)) {
1011 $params['campaign_id'] = $params['participant_campaign_id'];
1012 }
1013 else {
1014 $params['campaign_id'] = CRM_Utils_Array::value('campaign_id', $this->_values['event']);
1015 }
1016
1017 //hack to allow group to register w/ waiting
1018 $primaryParticipantCount = self::getParticipantCount($this, $params);
1019
1020 $totalParticipants = $primaryParticipantCount;
1021 if (!empty($params['additional_participants'])) {
1022 $totalParticipants += $params['additional_participants'];
1023 }
1024 if (!$this->_allowConfirmation && !empty($params['bypass_payment']) &&
1025 is_numeric($this->_availableRegistrations) &&
1026 $totalParticipants > $this->_availableRegistrations
1027 ) {
1028 $this->_allowWaitlist = TRUE;
1029 $this->set('allowWaitlist', TRUE);
1030 }
1031
1032 //carry participant id if pre-registered.
1033 if ($this->_allowConfirmation && $this->_participantId) {
1034 $params['participant_id'] = $this->_participantId;
1035 }
1036
1037 $params['defaultRole'] = 1;
1038 if (array_key_exists('participant_role', $params)) {
1039 $params['participant_role_id'] = $params['participant_role'];
1040 }
1041
1042 if (array_key_exists('participant_role_id', $params)) {
1043 $params['defaultRole'] = 0;
1044 }
1045 if (empty($params['participant_role_id']) &&
1046 $this->_values['event']['default_role_id']
1047 ) {
1048 $params['participant_role_id'] = $this->_values['event']['default_role_id'];
1049 }
1050
1051 $config = CRM_Core_Config::singleton();
1052 $params['currencyID'] = $config->defaultCurrency;
1053
1054 if ($this->_values['event']['is_monetary']) {
1055 // we first reset the confirm page so it accepts new values
1056 $this->controller->resetPage('Confirm');
1057
1058 //added for discount
1059 $discountId = CRM_Core_BAO_Discount::findSet($this->_eventId, 'civicrm_event');
1060
1061 if (!empty($this->_values['discount'][$discountId])) {
1062 $params['discount_id'] = $discountId;
1063 $params['amount_level'] = $this->_values['discount'][$discountId][$params['amount']]['label'];
1064
1065 $params['amount'] = $this->_values['discount'][$discountId][$params['amount']]['value'];
1066 }
1067 elseif (empty($params['priceSetId'])) {
1068 if (!empty($params['amount'])) {
1069 $params['amount_level'] = $this->_values['fee'][$params['amount']]['label'];
1070 $params['amount'] = $this->_values['fee'][$params['amount']]['value'];
1071 }
1072 else {
1073 $params['amount_level'] = $params['amount'] = '';
1074 }
1075 }
1076 else {
1077 $lineItem = array();
1078 CRM_Price_BAO_PriceSet::processAmount($this->_values['fee'], $params, $lineItem);
1079 if ($params['tax_amount']) {
1080 $this->set('tax_amount', $params['tax_amount']);
1081 }
1082 $submittedLineItems = $this->get('lineItem');
1083 if (!empty($submittedLineItems) && is_array($submittedLineItems)) {
1084 $submittedLineItems[0] = $lineItem;
1085 }
1086 else {
1087 $submittedLineItems = array($lineItem);
1088 }
1089 $this->set('lineItem', $submittedLineItems);
1090 $this->set('lineItemParticipantsCount', array($primaryParticipantCount));
1091 }
1092
1093 $this->set('amount', $params['amount']);
1094 $this->set('amount_level', $params['amount_level']);
1095
1096 // generate and set an invoiceID for this transaction
1097 $invoiceID = md5(uniqid(rand(), TRUE));
1098 $this->set('invoiceID', $invoiceID);
1099
1100 if (is_array($this->_paymentProcessor)) {
1101 $payment = $this->_paymentProcessor['object'];
1102 }
1103 // default mode is direct
1104 $this->set('contributeMode', 'direct');
1105
1106 if (isset($params["state_province_id-{$this->_bltID}"]) &&
1107 $params["state_province_id-{$this->_bltID}"]
1108 ) {
1109 $params["state_province-{$this->_bltID}"] = CRM_Core_PseudoConstant::stateProvinceAbbreviation($params["state_province_id-{$this->_bltID}"]);
1110 }
1111
1112 if (isset($params["country_id-{$this->_bltID}"]) &&
1113 $params["country_id-{$this->_bltID}"]
1114 ) {
1115 $params["country-{$this->_bltID}"] = CRM_Core_PseudoConstant::countryIsoCode($params["country_id-{$this->_bltID}"]);
1116 }
1117 if (isset($params['credit_card_exp_date'])) {
1118 $params['year'] = CRM_Core_Payment_Form::getCreditCardExpirationYear($params);
1119 $params['month'] = CRM_Core_Payment_Form::getCreditCardExpirationMonth($params);
1120 }
1121 if ($this->_values['event']['is_monetary']) {
1122 $params['ip_address'] = CRM_Utils_System::ipAddress();
1123 $params['currencyID'] = $config->defaultCurrency;
1124 $params['invoiceID'] = $invoiceID;
1125 }
1126 $this->_params = $this->get('params');
1127 if (!empty($this->_params) && is_array($this->_params)) {
1128 $this->_params[0] = $params;
1129 }
1130 else {
1131 $this->_params = array();
1132 $this->_params[] = $params;
1133 }
1134 $this->set('params', $this->_params);
1135
1136 if ($this->_paymentProcessor &&
1137 $this->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON
1138 ) {
1139 //get the button name
1140 $buttonName = $this->controller->getButtonName();
1141 if (in_array($buttonName,
1142 array(
1143 $this->_expressButtonName,
1144 $this->_expressButtonName . '_x',
1145 $this->_expressButtonName . '_y',
1146 )
1147 ) && empty($params['is_pay_later']) &&
1148 !$this->_allowWaitlist &&
1149 !$this->_requireApproval
1150 ) {
1151 $this->set('contributeMode', 'express');
1152
1153 // Send Event Name & Id in Params
1154 $params['eventName'] = $this->_values['event']['title'];
1155 $params['eventId'] = $this->_values['event']['id'];
1156
1157 $params['cancelURL'] = CRM_Utils_System::url('civicrm/event/register',
1158 "_qf_Register_display=1&qfKey={$this->controller->_key}",
1159 TRUE, NULL, FALSE
1160 );
1161 if (CRM_Utils_Array::value('additional_participants', $params, FALSE)) {
1162 $urlArgs = "_qf_Participant_1_display=1&rfp=1&qfKey={$this->controller->_key}";
1163 }
1164 else {
1165 $urlArgs = "_qf_Confirm_display=1&rfp=1&qfKey={$this->controller->_key}";
1166 }
1167 $params['returnURL'] = CRM_Utils_System::url('civicrm/event/register',
1168 $urlArgs,
1169 TRUE, NULL, FALSE
1170 );
1171 $params['invoiceID'] = $invoiceID;
1172
1173 $params['component'] = 'event';
1174 $token = $payment->doPreApproval($params);
1175 if (is_a($token, 'CRM_Core_Error')) {
1176 CRM_Core_Error::displaySessionError($token);
1177 CRM_Utils_System::redirect($params['cancelURL']);
1178 }
1179
1180 $this->set('token', $token);
1181
1182 $paymentURL = $this->_paymentProcessor['url_site'] . "/cgi-bin/webscr?cmd=_express-checkout&token=$token";
1183
1184 CRM_Utils_System::redirect($paymentURL);
1185 }
1186 }
1187 elseif ($this->_paymentProcessor &&
1188 $this->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_NOTIFY
1189 ) {
1190 $this->set('contributeMode', 'notify');
1191 }
1192 }
1193 else {
1194 $session = CRM_Core_Session::singleton();
1195 $params['description'] = ts('Online Event Registration') . ' ' . $this->_values['event']['title'];
1196
1197 $this->_params = array();
1198 $this->_params[] = $params;
1199 $this->set('params', $this->_params);
1200
1201 if (
1202 empty($params['additional_participants'])
1203 && !$this->_values['event']['is_confirm_enabled'] // CRM-11182 - Optional confirmation screen
1204 ) {
1205 self::processRegistration($this->_params);
1206 }
1207 }
1208
1209 // If registering > 1 participant, give status message
1210 if (CRM_Utils_Array::value('additional_participants', $params, FALSE)) {
1211 $statusMsg = ts('Registration information for participant 1 has been saved.');
1212 CRM_Core_Session::setStatus($statusMsg, ts('Saved'), 'success');
1213 }
1214 }
1215
1216 /**
1217 * Process Registration of free event.
1218 *
1219 * @param array $params
1220 * Form values.
1221 * @param int $contactID
1222 *
1223 * @return void
1224 */
1225 public function processRegistration($params, $contactID = NULL) {
1226 $session = CRM_Core_Session::singleton();
1227 $this->_participantInfo = array();
1228
1229 // CRM-4320, lets build array of cancelled additional participant ids
1230 // those are drop or skip by primary at the time of confirmation.
1231 // get all in and then unset those are confirmed.
1232 $cancelledIds = $this->_additionalParticipantIds;
1233
1234 $participantCount = array();
1235 foreach ($params as $participantNum => $record) {
1236 if ($record == 'skip') {
1237 $participantCount[$participantNum] = 'skip';
1238 }
1239 elseif ($participantNum) {
1240 $participantCount[$participantNum] = 'participant';
1241 }
1242 }
1243
1244 $registerByID = NULL;
1245 foreach ($params as $key => $value) {
1246 if ($value != 'skip') {
1247 $fields = NULL;
1248
1249 // setting register by Id and unset contactId.
1250 if (empty($value['is_primary'])) {
1251 $contactID = NULL;
1252 $registerByID = $this->get('registerByID');
1253 if ($registerByID) {
1254 $value['registered_by_id'] = $registerByID;
1255 }
1256 // get an email if one exists for the participant
1257 $participantEmail = '';
1258 foreach (array_keys($value) as $valueName) {
1259 if (substr($valueName, 0, 6) == 'email-') {
1260 $participantEmail = $value[$valueName];
1261 }
1262 }
1263 if ($participantEmail) {
1264 $this->_participantInfo[] = $participantEmail;
1265 }
1266 else {
1267 $this->_participantInfo[] = $value['first_name'] . ' ' . $value['last_name'];
1268 }
1269 }
1270 elseif (!empty($value['contact_id'])) {
1271 $contactID = $value['contact_id'];
1272 }
1273 else {
1274 $contactID = $this->getContactID();
1275 }
1276
1277 CRM_Event_Form_Registration_Confirm::fixLocationFields($value, $fields, $this);
1278 //for free event or additional participant, dont create billing email address.
1279 if (empty($value['is_primary']) || !$this->_values['event']['is_monetary']) {
1280 unset($value["email-{$this->_bltID}"]);
1281 }
1282
1283 $contactID = CRM_Event_Form_Registration_Confirm::updateContactFields($contactID, $value, $fields, $this);
1284
1285 // lets store the contactID in the session
1286 // we dont store in userID in case the user is doing multiple
1287 // transactions etc
1288 // for things like tell a friend
1289 if (!$this->getContactID() && !empty($value['is_primary'])) {
1290 $session->set('transaction.userID', $contactID);
1291 }
1292
1293 //lets get the status if require approval or waiting.
1294
1295 $waitingStatuses = CRM_Event_PseudoConstant::participantStatus(NULL, "class = 'Waiting'");
1296 if ($this->_allowWaitlist && !$this->_allowConfirmation) {
1297 $value['participant_status_id'] = $value['participant_status'] = array_search('On waitlist', $waitingStatuses);
1298 }
1299 elseif ($this->_requireApproval && !$this->_allowConfirmation) {
1300 $value['participant_status_id'] = $value['participant_status'] = array_search('Awaiting approval', $waitingStatuses);
1301 }
1302
1303 $this->set('value', $value);
1304 $this->confirmPostProcess($contactID, NULL, NULL);
1305
1306 //lets get additional participant id to cancel.
1307 if ($this->_allowConfirmation && is_array($cancelledIds)) {
1308 $additonalId = CRM_Utils_Array::value('participant_id', $value);
1309 if ($additonalId && $key = array_search($additonalId, $cancelledIds)) {
1310 unset($cancelledIds[$key]);
1311 }
1312 }
1313 }
1314 }
1315
1316 // update status and send mail to cancelled additional participants, CRM-4320
1317 if ($this->_allowConfirmation && is_array($cancelledIds) && !empty($cancelledIds)) {
1318 $cancelledId = array_search('Cancelled',
1319 CRM_Event_PseudoConstant::participantStatus(NULL, "class = 'Negative'")
1320 );
1321 CRM_Event_BAO_Participant::transitionParticipants($cancelledIds, $cancelledId);
1322 }
1323
1324 //set information about additional participants if exists
1325 if (count($this->_participantInfo)) {
1326 $this->set('participantInfo', $this->_participantInfo);
1327 }
1328
1329 //send mail Confirmation/Receipt
1330 if ($this->_contributeMode != 'checkout' ||
1331 $this->_contributeMode != 'notify'
1332 ) {
1333 $isTest = FALSE;
1334 if ($this->_action & CRM_Core_Action::PREVIEW) {
1335 $isTest = TRUE;
1336 }
1337
1338 //handle if no additional participant.
1339 if (!$registerByID) {
1340 $registerByID = $this->get('registerByID');
1341 }
1342 $primaryContactId = $this->get('primaryContactId');
1343
1344 //build an array of custom profile and assigning it to template.
1345 $additionalIDs = CRM_Event_BAO_Event::buildCustomProfile($registerByID, NULL,
1346 $primaryContactId, $isTest, TRUE
1347 );
1348
1349 //lets carry all paticipant params w/ values.
1350 foreach ($additionalIDs as $participantID => $contactId) {
1351 $participantNum = NULL;
1352 if ($participantID == $registerByID) {
1353 $participantNum = 0;
1354 }
1355 else {
1356 if ($participantNum = array_search('participant', $participantCount)) {
1357 unset($participantCount[$participantNum]);
1358 }
1359 }
1360
1361 if ($participantNum === NULL) {
1362 break;
1363 }
1364
1365 //carry the participant submitted values.
1366 $this->_values['params'][$participantID] = $params[$participantNum];
1367 }
1368
1369 //lets send mails to all with meanigful text, CRM-4320.
1370 $this->assign('isOnWaitlist', $this->_allowWaitlist);
1371 $this->assign('isRequireApproval', $this->_requireApproval);
1372
1373 foreach ($additionalIDs as $participantID => $contactId) {
1374 if ($participantID == $registerByID) {
1375 //set as Primary Participant
1376 $this->assign('isPrimary', 1);
1377
1378 $customProfile = CRM_Event_BAO_Event::buildCustomProfile($participantID, $this->_values, NULL, $isTest);
1379
1380 if (count($customProfile)) {
1381 $this->assign('customProfile', $customProfile);
1382 $this->set('customProfile', $customProfile);
1383 }
1384 }
1385 else {
1386 $this->assign('isPrimary', 0);
1387 $this->assign('customProfile', NULL);
1388 }
1389
1390 //send Confirmation mail to Primary & additional Participants if exists
1391 CRM_Event_BAO_Event::sendMail($contactId, $this->_values, $participantID, $isTest);
1392 }
1393 }
1394 }
1395
1396 /**
1397 * Method to check if the user is already registered for the event.
1398 * and if result found redirect to the event info page
1399 *
1400 * @param array $fields
1401 * The input form values(anonymous user).
1402 * @param array $self
1403 * Event data.
1404 * @param bool $isAdditional
1405 * Treat isAdditional participants a bit differently.
1406 * @param bool $returnContactId
1407 * Just find and return the contactID match to use.
1408 * @param bool $useDedupeRules
1409 * Force usage of dedupe rules.
1410 *
1411 * @return void
1412 */
1413 public static function checkRegistration($fields, &$self, $isAdditional = FALSE, $returnContactId = FALSE, $useDedupeRules = FALSE) {
1414 // CRM-3907, skip check for preview registrations
1415 // CRM-4320 participant need to walk wizard
1416 if (!$returnContactId &&
1417 ($self->_mode == 'test' || $self->_allowConfirmation)
1418 ) {
1419 return FALSE;
1420 }
1421
1422 $contactID = NULL;
1423 $session = CRM_Core_Session::singleton();
1424 if (!$isAdditional) {
1425 $contactID = $self->getContactID();
1426 }
1427
1428 if (!$contactID && is_array($fields) && $fields) {
1429
1430 //CRM-14134 use Unsupervised rule for everyone
1431 $dedupeParams = CRM_Dedupe_Finder::formatParams($fields, 'Individual');
1432
1433 // disable permission based on cache since event registration is public page/feature.
1434 $dedupeParams['check_permission'] = FALSE;
1435
1436 // find event dedupe rule
1437 if (CRM_Utils_Array::value('dedupe_rule_group_id', $self->_values['event'], 0) > 0) {
1438 $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Individual', 'Unsupervised', array(), $self->_values['event']['dedupe_rule_group_id']);
1439 }
1440 else {
1441 $ids = CRM_Dedupe_Finder::dupesByParams($dedupeParams, 'Individual', 'Unsupervised');
1442 }
1443 $contactID = CRM_Utils_Array::value(0, $ids);
1444
1445 }
1446
1447 if ($returnContactId) {
1448 // CRM-7377
1449 // return contactID if contact already exists
1450 return $contactID;
1451 }
1452
1453 if ($contactID) {
1454 $participant = new CRM_Event_BAO_Participant();
1455 $participant->contact_id = $contactID;
1456 $participant->event_id = $self->_values['event']['id'];
1457 if (!empty($fields['participant_role']) && is_numeric($fields['participant_role'])) {
1458 $participant->role_id = $fields['participant_role'];
1459 }
1460 else {
1461 $participant->role_id = $self->_values['event']['default_role_id'];
1462 }
1463 $participant->is_test = 0;
1464 $participant->find();
1465 $statusTypes = CRM_Event_PseudoConstant::participantStatus(NULL, 'is_counted = 1');
1466 while ($participant->fetch()) {
1467 if (array_key_exists($participant->status_id, $statusTypes)) {
1468 if (!$isAdditional && !$self->_values['event']['allow_same_participant_emails']) {
1469 $registerUrl = CRM_Utils_System::url('civicrm/event/register',
1470 "reset=1&id={$self->_values['event']['id']}&cid=0"
1471 );
1472 if ($self->_pcpId) {
1473 $registerUrl .= '&pcpId=' . $self->_pcpId;
1474 }
1475
1476 $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 gotten this message in error, please contact the site administrator.") . ' ' . ts('You can also <a href="%1">register another participant</a>.', array(1 => $registerUrl));
1477 $session->setStatus($status, ts('Oops.'), 'alert');
1478 $url = CRM_Utils_System::url('civicrm/event/info',
1479 "reset=1&id={$self->_values['event']['id']}&noFullMsg=true"
1480 );
1481 if ($self->_action & CRM_Core_Action::PREVIEW) {
1482 $url .= '&action=preview';
1483 }
1484
1485 if ($self->_pcpId) {
1486 $url .= '&pcpId=' . $self->_pcpId;
1487 }
1488
1489 CRM_Utils_System::redirect($url);
1490 }
1491
1492 if ($isAdditional) {
1493 $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 gotten this message in error, please contact the site administrator.");
1494 $session->setStatus($status, ts('Oops.'), 'alert');
1495 return $participant->id;
1496 }
1497 }
1498 }
1499 }
1500 }
1501
1502 }