Completed
Branch BUG/11294/expired-cart-ticket-... (be84f1)
by
unknown
13:41 queued 13s
created

ProcessTicketSelector::maxAttendeesViolation()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 16
nc 1
nop 1
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
1
<?php
2
3
namespace EventEspresso\modules\ticket_selector;
4
5
use EE_Cart;
6
use EE_Core_Config;
7
use EE_Error;
8
use EE_Request;
9
use EE_Session;
10
use EE_Ticket;
11
use EEH_Event_View;
12
use EEM_Ticket;
13
use EventEspresso\core\domain\services\factories\CartFactory;
14
use EventEspresso\core\exceptions\InvalidDataTypeException;
15
use EventEspresso\core\exceptions\InvalidInterfaceException;
16
use EventEspresso\core\services\loaders\LoaderFactory;
17
use InvalidArgumentException;
18
use ReflectionException;
19
20
defined('EVENT_ESPRESSO_VERSION') || exit('No direct script access allowed');
21
22
23
24
/**
25
 * Class ProcessTicketSelector
26
 * Description
27
 *
28
 * @package       Event Espresso
29
 * @subpackage    core
30
 * @author        Brent Christensen
31
 * @since         4.9.0
32
 */
33
class ProcessTicketSelector
34
{
35
36
    /**
37
     * @var EE_Cart $cart
38
     */
39
    private $cart;
40
41
    /**
42
     * @var EE_Core_Config $core_config
43
     */
44
    private $core_config;
45
46
    /**
47
     * @var EE_Request $request
48
     */
49
    private $request;
50
51
    /**
52
     * @var EE_Session $session
53
     */
54
    private $session;
55
56
    /**
57
     * @var EEM_Ticket $ticket_model
58
     */
59
    private $ticket_model;
60
61
    /**
62
     * @var TicketDatetimeAvailabilityTracker $tracker
63
     */
64
    private $tracker;
65
66
67
    /**
68
     * ProcessTicketSelector constructor.
69
     * NOTE: PLZ use the Loader to instantiate this class if need be
70
     * so that all dependencies get injected correctly (which will happen automatically)
71
     * Null values for parameters are only for backwards compatibility but will be removed later on.
72
     *
73
     * @param EE_Core_Config                    $core_config
74
     * @param EE_Request                        $request
75
     * @param EE_Session                        $session
76
     * @param EEM_Ticket                        $ticket_model
77
     * @param TicketDatetimeAvailabilityTracker $tracker
78
     * @throws EE_Error
79
     * @throws InvalidArgumentException
80
     * @throws InvalidInterfaceException
81
     * @throws InvalidDataTypeException
82
     * @throws InvalidArgumentException
83
     * @throws ReflectionException
84
     */
85
    public function __construct(
86
        EE_Core_Config $core_config = null,
87
        EE_Request $request = null,
88
        EE_Session $session = null,
89
        EEM_Ticket $ticket_model = null,
90
        TicketDatetimeAvailabilityTracker $tracker = null
91
    ) {
92
        $loader = LoaderFactory::getLoader();
93
        $this->core_config  = $core_config instanceof EE_Core_Config
94
            ? $core_config
95
            : $loader->getShared('EE_Core_Config');
96
        $this->request      = $request instanceof EE_Request
97
            ? $request
98
            : $loader->getShared('EE_Request');
99
        $this->session      = $session instanceof EE_Session
100
            ? $session
101
            : $loader->getShared('EE_Session');
102
        $this->ticket_model = $ticket_model instanceof EEM_Ticket
103
            ? $ticket_model
104
            : $loader->getShared('EEM_Ticket');
105
        $this->tracker      = $tracker instanceof TicketDatetimeAvailabilityTracker
106
            ? $tracker
107
            : $loader->getShared('EventEspresso\modules\ticket_selector\TicketDatetimeAvailabilityTracker');
108
    }
109
110
111
    /**
112
     * cancelTicketSelections
113
     *
114
     * @return        string
115
     * @throws EE_Error
116
     * @throws InvalidArgumentException
117
     * @throws InvalidInterfaceException
118
     * @throws InvalidDataTypeException
119
     */
120
    public function cancelTicketSelections()
121
    {
122
        // check nonce
123
        if (! $this->processTicketSelectorNonce('cancel_ticket_selections')) {
124
            return false;
125
        }
126
        $this->session->clear_session(__CLASS__, __FUNCTION__);
127
        if ($this->request->is_set('event_id')) {
128
            wp_safe_redirect(
129
                EEH_Event_View::event_link_url(
130
                    $this->request->get('event_id')
131
                )
132
            );
133
        } else {
134
            wp_safe_redirect(
135
                site_url('/' . $this->core_config->event_cpt_slug . '/')
136
            );
137
        }
138
        exit();
139
    }
140
141
142
    /**
143
     * processTicketSelectorNonce
144
     *
145
     * @param  string $nonce_name
146
     * @param string  $id
147
     * @return bool
148
     * @throws InvalidArgumentException
149
     * @throws InvalidInterfaceException
150
     * @throws InvalidDataTypeException
151
     */
152
    private function processTicketSelectorNonce($nonce_name, $id = '')
153
    {
154
        $nonce_name_with_id = ! empty($id) ? "{$nonce_name}_nonce_{$id}" : "{$nonce_name}_nonce";
155
        if (
156
            ! $this->request->isAdmin()
157
            && (
158
                ! $this->request->is_set($nonce_name_with_id)
159
                || ! wp_verify_nonce(
160
                    $this->request->get($nonce_name_with_id),
161
                    $nonce_name
162
                )
163
            )
164
        ) {
165
            EE_Error::add_error(
166
                sprintf(
167
                    esc_html__(
168
                        'We\'re sorry but your request failed to pass a security check.%sPlease click the back button on your browser and try again.',
169
                        'event_espresso'
170
                    ),
171
                    '<br/>'
172
                ),
173
                __FILE__,
174
                __FUNCTION__,
175
                __LINE__
176
            );
177
            return false;
178
        }
179
        return true;
180
    }
181
182
183
    /**
184
     * process_ticket_selections
185
     *
186
     * @return array|bool
187
     * @throws ReflectionException
188
     * @throws InvalidArgumentException
189
     * @throws InvalidInterfaceException
190
     * @throws InvalidDataTypeException
191
     * @throws EE_Error
192
     */
193
    public function processTicketSelections()
194
    {
195
        do_action('EED_Ticket_Selector__process_ticket_selections__before');
196
        // unless otherwise requested, clear the session
197
        if (apply_filters('FHEE__EE_Ticket_Selector__process_ticket_selections__clear_session', true)) {
198
            $this->session->clear_session(__CLASS__, __FUNCTION__);
199
        }// do we have an event id?
200
        $id = $this->getEventId();
201
        // validate/sanitize/filter data
202
        $valid = apply_filters(
203
            'FHEE__EED_Ticket_Selector__process_ticket_selections__valid_post_data',
204
            $this->validatePostData($id)
205
        );
206
        //check total tickets ordered vs max number of attendees that can register
207
        if ($valid['total_tickets'] > $valid['max_atndz']) {
208
            $this->maxAttendeesViolation($valid);
209
        } else {
210
            // all data appears to be valid
211
            if ($this->processSuccessfulCart($this->addTicketsToCart($valid))) {
212
                return true;
213
            }
214
        }
215
        // die(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< KILL BEFORE REDIRECT
216
        // at this point, just return if registration is being made from admin
217
        if ($this->request->isAdmin() || $this->request->isFrontAjax()) {
218
            return false;
219
        }
220
        if ($valid['return_url']) {
221
            EE_Error::get_notices(false, true);
222
            wp_safe_redirect($valid['return_url']);
223
            exit();
224
        }
225
        if ($id) {
226
            EE_Error::get_notices(false, true);
227
            wp_safe_redirect(get_permalink($id));
228
            exit();
229
        }
230
        echo EE_Error::get_notices();
231
        return false;
232
    }
233
234
235
    /**
236
     * @return int
237
     */
238
    private function getEventId()
239
    {
240
        if (! $this->request->is_set('tkt-slctr-event-id')) {
241
            // $_POST['tkt-slctr-event-id'] was not set ?!?!?!?
242
            EE_Error::add_error(
243
                sprintf(
244
                    esc_html__(
245
                        'An event id was not provided or was not received.%sPlease click the back button on your browser and try again.',
246
                        'event_espresso'
247
                    ),
248
                    '<br/>'
249
                ),
250
                __FILE__,
251
                __FUNCTION__,
252
                __LINE__
253
            );
254
        }
255
        //if event id is valid
256
        return absint($this->request->get('tkt-slctr-event-id'));
257
    }
258
259
260
    /**
261
     * validate_post_data
262
     *
263
     * @param int $id
264
     * @return array|FALSE
265
     * @throws ReflectionException
266
     * @throws InvalidArgumentException
267
     * @throws InvalidInterfaceException
268
     * @throws InvalidDataTypeException
269
     * @throws EE_Error
270
     */
271
    private function validatePostData($id = 0)
272
    {
273
        if (! $id) {
274
            EE_Error::add_error(
275
                esc_html__('The event id provided was not valid.', 'event_espresso'),
276
                __FILE__,
277
                __FUNCTION__,
278
                __LINE__
279
            );
280
            return false;
281
        }
282
        // start with an empty array()
283
        $valid_data = array();
284
        // grab valid id
285
        $valid_data['id'] = $id;
286
        // array of other form names
287
        $inputs_to_clean = array(
288
            'event_id'   => 'tkt-slctr-event-id',
289
            'max_atndz'  => 'tkt-slctr-max-atndz-',
290
            'rows'       => 'tkt-slctr-rows-',
291
            'qty'        => 'tkt-slctr-qty-',
292
            'ticket_id'  => 'tkt-slctr-ticket-id-',
293
            'return_url' => 'tkt-slctr-return-url-',
294
        );
295
        // let's track the total number of tickets ordered.'
296
        $valid_data['total_tickets'] = 0;
297
        // cycle through $inputs_to_clean array
298
        foreach ($inputs_to_clean as $what => $input_to_clean) {
299
            // check for POST data
300
            if ($this->request->is_set($input_to_clean . $id)) {
301
                // grab value
302
                $input_value = $this->request->get($input_to_clean . $id);
303
                switch ($what) {
304
                    // integers
305
                    case 'event_id':
306
                        $valid_data[ $what ] = absint($input_value);
307
                        // get event via the event id we put in the form
308
                        break;
309
                    case 'rows':
310
                    case 'max_atndz':
311
                        $valid_data[ $what ] = absint($input_value);
312
                        break;
313
                    // arrays of integers
314
                    case 'qty':
315
                        /** @var array $row_qty */
316
                        $row_qty = $input_value;
317
                        // if qty is coming from a radio button input, then we need to assemble an array of rows
318
                        if (! is_array($row_qty)) {
319
                            // get number of rows
320
                            $rows = $this->request->is_set('tkt-slctr-rows-' . $id)
321
                                ? absint($this->request->get('tkt-slctr-rows-' . $id))
322
                                : 1;
323
                            // explode integers by the dash
324
                            $row_qty = explode('-', $row_qty);
325
                            $row     = isset($row_qty[0]) ? absint($row_qty[0]) : 1;
326
                            $qty     = isset($row_qty[1]) ? absint($row_qty[1]) : 0;
327
                            $row_qty = array($row => $qty);
328
                            for ($x = 1; $x <= $rows; $x++) {
329
                                if (! isset($row_qty[ $x ])) {
330
                                    $row_qty[ $x ] = 0;
331
                                }
332
                            }
333
                        }
334
                        ksort($row_qty);
335
                        // cycle thru values
336
                        foreach ($row_qty as $qty) {
337
                            $qty = absint($qty);
338
                            // sanitize as integers
339
                            $valid_data[ $what ][]       = $qty;
340
                            $valid_data['total_tickets'] += $qty;
341
                        }
342
                        break;
343
                    // array of integers
344
                    case 'ticket_id':
345
                        // cycle thru values
346
                        foreach ((array) $input_value as $key => $value) {
347
                            // allow only integers
348
                            $valid_data[ $what ][ $key ] = absint($value);
349
                        }
350
                        break;
351
                    case 'return_url' :
352
                        // grab and sanitize return-url
353
                        $input_value = esc_url_raw($input_value);
354
                        // was the request coming from an iframe ? if so, then:
355
                        if (strpos($input_value, 'event_list=iframe')) {
356
                            // get anchor fragment
357
                            $input_value = explode('#', $input_value);
358
                            $input_value = end($input_value);
359
                            // use event list url instead, but append anchor
360
                            $input_value = EEH_Event_View::event_archive_url() . '#' . $input_value;
361
                        }
362
                        $valid_data[ $what ] = $input_value;
363
                        break;
364
                }    // end switch $what
365
            }
366
        }    // end foreach $inputs_to_clean
367
        return $valid_data;
368
    }
369
370
371
    /**
372
     * @param array $valid
373
     */
374
    private function maxAttendeesViolation(array $valid)
375
    {
376
        // ordering too many tickets !!!
377
        $total_tickets_string = esc_html(
378
            _n(
379
                'You have attempted to purchase %s ticket.',
380
                'You have attempted to purchase %s tickets.',
381
                $valid['total_tickets'],
382
                'event_espresso'
383
            )
384
        );
385
        $limit_error_1        = sprintf($total_tickets_string, $valid['total_tickets']);
386
        // dev only message
387
        $max_attendees_string = esc_html(
388
            _n(
389
                'The registration limit for this event is %s ticket per registration, therefore the total number of tickets you may purchase at a time can not exceed %s.',
390
                'The registration limit for this event is %s tickets per registration, therefore the total number of tickets you may purchase at a time can not exceed %s.',
391
                $valid['max_atndz'],
392
                'event_espresso'
393
            )
394
        );
395
        $limit_error_2    = sprintf($max_attendees_string, $valid['max_atndz'], $valid['max_atndz']);
396
        EE_Error::add_error($limit_error_1 . '<br/>' . $limit_error_2, __FILE__, __FUNCTION__, __LINE__);
397
    }
398
399
400
    /**
401
     * @param array $valid
402
     * @return int|TRUE
403
     * @throws EE_Error
404
     * @throws InvalidArgumentException
405
     * @throws InvalidDataTypeException
406
     * @throws InvalidInterfaceException
407
     * @throws ReflectionException
408
     */
409
    private function addTicketsToCart(array $valid)
410
    {
411
        $tickets_added = 0;
412
        $tickets_selected = false;
413
        if($valid['total_tickets'] > 0){
414
            // load cart using factory because we don't want to do so until actually needed
415
            $this->cart = CartFactory::getCart();
416
            // cycle thru the number of data rows sent from the event listing
417
            for ($x = 0; $x < $valid['rows']; $x++) {
418
                // does this row actually contain a ticket quantity?
419
                if (isset($valid['qty'][ $x ]) && $valid['qty'][ $x ] > 0) {
420
                    // YES we have a ticket quantity
421
                    $tickets_selected = true;
422
                    $valid_ticket     = false;
423
                    // \EEH_Debug_Tools::printr(
424
                    //     $valid['ticket_id'][ $x ],
425
                    //     '$valid[\'ticket_id\'][ $x ]',
426
                    //     __FILE__, __LINE__
427
                    // );
428
                    if (isset($valid['ticket_id'][ $x ])) {
429
                        // get ticket via the ticket id we put in the form
430
                        $ticket = $this->ticket_model->get_one_by_ID($valid['ticket_id'][ $x ]);
431
                        if ($ticket instanceof EE_Ticket) {
432
                            $valid_ticket  = true;
433
                            $tickets_added += $this->addTicketToCart(
434
                                $ticket,
435
                                $valid['qty'][ $x ]
436
                            );
437
                        }
438
                    }
439 View Code Duplication
                    if ($valid_ticket !== true) {
440
                        // nothing added to cart retrieved
441
                        EE_Error::add_error(
442
                            sprintf(
443
                                esc_html__(
444
                                    'A valid ticket could not be retrieved for the event.%sPlease click the back button on your browser and try again.',
445
                                    'event_espresso'
446
                                ),
447
                                '<br/>'
448
                            ),
449
                            __FILE__, __FUNCTION__, __LINE__
450
                        );
451
                    }
452
                    if (EE_Error::has_error()) {
453
                        break;
454
                    }
455
                }
456
            }
457
        }
458
        do_action(
459
            'AHEE__EE_Ticket_Selector__process_ticket_selections__after_tickets_added_to_cart',
460
            $this->cart,
461
            $this
462
        );
463
        if (! apply_filters('FHEE__EED_Ticket_Selector__process_ticket_selections__tckts_slctd', $tickets_selected)) {
464
            // no ticket quantities were selected
465
            EE_Error::add_error(
466
                esc_html__('You need to select a ticket quantity before you can proceed.', 'event_espresso'),
467
                __FILE__, __FUNCTION__, __LINE__
468
            );
469
        }
470
        return $tickets_added;
471
    }
472
473
474
475
    /**
476
     * adds a ticket to the cart
477
     *
478
     * @param EE_Ticket $ticket
479
     * @param int        $qty
480
     * @return TRUE on success, FALSE on fail
481
     * @throws InvalidArgumentException
482
     * @throws InvalidInterfaceException
483
     * @throws InvalidDataTypeException
484
     * @throws EE_Error
485
     */
486
    private function addTicketToCart(EE_Ticket $ticket, $qty = 1)
487
    {
488
        // get the number of spaces left for this datetime ticket
489
        $available_spaces = $this->tracker->ticketDatetimeAvailability($ticket);
490
        // compare available spaces against the number of tickets being purchased
491
        if ($available_spaces >= $qty) {
492
            // allow addons to prevent a ticket from being added to cart
493
            if (
494
                ! apply_filters(
495
                    'FHEE__EE_Ticket_Selector___add_ticket_to_cart__allow_add_to_cart',
496
                    true,
497
                    $ticket,
498
                    $qty,
499
                    $available_spaces
500
                )
501
            ) {
502
                return false;
503
            }
504
            $qty = absint(apply_filters('FHEE__EE_Ticket_Selector___add_ticket_to_cart__ticket_qty', $qty, $ticket));
505
            // add event to cart
506
            if ($this->cart->add_ticket_to_cart($ticket, $qty)) {
507
                $this->tracker->recalculateTicketDatetimeAvailability($ticket, $qty);
508
                return true;
509
            }
510
            return false;
511
        }
512
        $this->tracker->processAvailabilityError($ticket, $qty, $this->cart->all_ticket_quantity_count());
513
        return false;
514
    }
515
516
517
    /**
518
     * @param $tickets_added
519
     * @return bool
520
     * @throws InvalidInterfaceException
521
     * @throws InvalidDataTypeException
522
     * @throws EE_Error
523
     * @throws InvalidArgumentException
524
     */
525
    private function processSuccessfulCart($tickets_added)
526
    {
527
        // die(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< KILL REDIRECT HERE BEFORE CART UPDATE
528
        if (apply_filters('FHEE__EED_Ticket_Selector__process_ticket_selections__success', $tickets_added)) {
529
            // make sure cart is loaded
530
            if(! $this->cart  instanceof EE_Cart){
531
                $this->cart = CartFactory::getCart();
532
            }
533
            do_action(
534
                'FHEE__EE_Ticket_Selector__process_ticket_selections__before_redirecting_to_checkout',
535
                $this->cart,
536
                $this
537
            );
538
            $this->cart->recalculate_all_cart_totals();
539
            $this->cart->save_cart(false);
540
            // exit('KILL REDIRECT AFTER CART UPDATE'); // <<<<<<<<  OR HERE TO KILL REDIRECT AFTER CART UPDATE
541
            // just return TRUE for registrations being made from admin
542
            if ($this->request->isAdmin() || $this->request->isFrontAjax()) {
543
                return true;
544
            }
545
            EE_Error::get_notices(false, true);
546
            wp_safe_redirect(
547
                apply_filters(
548
                    'FHEE__EE_Ticket_Selector__process_ticket_selections__success_redirect_url',
549
                    $this->core_config->reg_page_url()
550
                )
551
            );
552
            exit();
553
        }
554
        if (! EE_Error::has_error() && ! EE_Error::has_error(true, 'attention')) {
555
            // nothing added to cart
556
            EE_Error::add_attention(
557
                esc_html__('No tickets were added for the event', 'event_espresso'),
558
                __FILE__, __FUNCTION__, __LINE__
559
            );
560
        }
561
        return false;
562
    }
563
}
564
// End of file ProcessTicketSelector.php
565
// Location: /ProcessTicketSelector.php
566