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