Completed
Branch FET/Gutenberg/11400/block-mana... (1c7938)
by
unknown
13:59 queued 16s
created
core/helpers/EEH_Line_Item.helper.php 2 patches
Indentation   +1698 added lines, -1698 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if (!defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 
5 5
 /**
@@ -23,1703 +23,1703 @@  discard block
 block discarded – undo
23 23
 class EEH_Line_Item
24 24
 {
25 25
 
26
-    //other functions: cancel ticket purchase
27
-    //delete ticket purchase
28
-    //add promotion
29
-
30
-
31
-    /**
32
-     * Adds a simple item (unrelated to any other model object) to the provided PARENT line item.
33
-     * Does NOT automatically re-calculate the line item totals or update the related transaction.
34
-     * You should call recalculate_total_including_taxes() on the grant total line item after this
35
-     * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
36
-     * to keep the registration final prices in-sync with the transaction's total.
37
-     *
38
-     * @param EE_Line_Item $parent_line_item
39
-     * @param string $name
40
-     * @param float $unit_price
41
-     * @param string $description
42
-     * @param int $quantity
43
-     * @param boolean $taxable
44
-     * @param boolean $code if set to a value, ensures there is only one line item with that code
45
-     * @return boolean success
46
-     * @throws \EE_Error
47
-     */
48
-    public static function add_unrelated_item(EE_Line_Item $parent_line_item, $name, $unit_price, $description = '', $quantity = 1, $taxable = FALSE, $code = NULL)
49
-    {
50
-        $items_subtotal = self::get_pre_tax_subtotal($parent_line_item);
51
-        $line_item = EE_Line_Item::new_instance(array(
52
-            'LIN_name' => $name,
53
-            'LIN_desc' => $description,
54
-            'LIN_unit_price' => $unit_price,
55
-            'LIN_quantity' => $quantity,
56
-            'LIN_percent' => null,
57
-            'LIN_is_taxable' => $taxable,
58
-            'LIN_order' => $items_subtotal instanceof EE_Line_Item ? count($items_subtotal->children()) : 0,
59
-            'LIN_total' => (float)$unit_price * (int)$quantity,
60
-            'LIN_type' => EEM_Line_Item::type_line_item,
61
-            'LIN_code' => $code,
62
-        ));
63
-        $line_item = apply_filters(
64
-            'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
65
-            $line_item,
66
-            $parent_line_item
67
-        );
68
-        return self::add_item($parent_line_item, $line_item);
69
-    }
70
-
71
-
72
-    /**
73
-     * Adds a simple item ( unrelated to any other model object) to the total line item,
74
-     * in the correct spot in the line item tree. Automatically
75
-     * re-calculates the line item totals and updates the related transaction. But
76
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
77
-     * should probably change because of this).
78
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
79
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
80
-     *
81
-     * @param EE_Line_Item $parent_line_item
82
-     * @param string $name
83
-     * @param float $percentage_amount
84
-     * @param string $description
85
-     * @param boolean $taxable
86
-     * @return boolean success
87
-     * @throws \EE_Error
88
-     */
89
-    public static function add_percentage_based_item(EE_Line_Item $parent_line_item, $name, $percentage_amount, $description = '', $taxable = FALSE)
90
-    {
91
-        $line_item = EE_Line_Item::new_instance(array(
92
-            'LIN_name' => $name,
93
-            'LIN_desc' => $description,
94
-            'LIN_unit_price' => 0,
95
-            'LIN_percent' => $percentage_amount,
96
-            'LIN_quantity' => 1,
97
-            'LIN_is_taxable' => $taxable,
98
-            'LIN_total' => (float)($percentage_amount * ($parent_line_item->total() / 100)),
99
-            'LIN_type' => EEM_Line_Item::type_line_item,
100
-            'LIN_parent' => $parent_line_item->ID()
101
-        ));
102
-        $line_item = apply_filters(
103
-            'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
104
-            $line_item
105
-        );
106
-        return $parent_line_item->add_child_line_item($line_item, false);
107
-    }
108
-
109
-
110
-    /**
111
-     * Returns the new line item created by adding a purchase of the ticket
112
-     * ensures that ticket line item is saved, and that cart total has been recalculated.
113
-     * If this ticket has already been purchased, just increments its count.
114
-     * Automatically re-calculates the line item totals and updates the related transaction. But
115
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
116
-     * should probably change because of this).
117
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
118
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
119
-     *
120
-     * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total
121
-     * @param EE_Ticket $ticket
122
-     * @param int $qty
123
-     * @return \EE_Line_Item
124
-     * @throws \EE_Error
125
-     */
126
-    public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
127
-    {
128
-        if (!$total_line_item instanceof EE_Line_Item || !$total_line_item->is_total()) {
129
-            throw new EE_Error(sprintf(__('A valid line item total is required in order to add tickets. A line item of type "%s" was passed.', 'event_espresso'), $ticket->ID(), $total_line_item->ID()));
130
-        }
131
-        // either increment the qty for an existing ticket
132
-        $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
133
-        // or add a new one
134
-        if (!$line_item instanceof EE_Line_Item) {
135
-            $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
136
-        }
137
-        $total_line_item->recalculate_total_including_taxes();
138
-        return $line_item;
139
-    }
140
-
141
-
142
-    /**
143
-     * Returns the new line item created by adding a purchase of the ticket
144
-     * @param \EE_Line_Item $total_line_item
145
-     * @param EE_Ticket $ticket
146
-     * @param int $qty
147
-     * @return \EE_Line_Item
148
-     * @throws \EE_Error
149
-     */
150
-    public static function increment_ticket_qty_if_already_in_cart(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
151
-    {
152
-        $line_item = null;
153
-        if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
154
-            $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
155
-            foreach ((array)$ticket_line_items as $ticket_line_item) {
156
-                if (
157
-                    $ticket_line_item instanceof EE_Line_Item
158
-                    && (int)$ticket_line_item->OBJ_ID() === (int)$ticket->ID()
159
-                ) {
160
-                    $line_item = $ticket_line_item;
161
-                    break;
162
-                }
163
-            }
164
-        }
165
-        if ($line_item instanceof EE_Line_Item) {
166
-            EEH_Line_Item::increment_quantity($line_item, $qty);
167
-            return $line_item;
168
-        }
169
-        return null;
170
-    }
171
-
172
-
173
-    /**
174
-     * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected).
175
-     * Does NOT save or recalculate other line items totals
176
-     *
177
-     * @param EE_Line_Item $line_item
178
-     * @param int $qty
179
-     * @return void
180
-     * @throws \EE_Error
181
-     */
182
-    public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
183
-    {
184
-        if (!$line_item->is_percent()) {
185
-            $qty += $line_item->quantity();
186
-            $line_item->set_quantity($qty);
187
-            $line_item->set_total($line_item->unit_price() * $qty);
188
-            $line_item->save();
189
-        }
190
-        foreach ($line_item->children() as $child) {
191
-            if ($child->is_sub_line_item()) {
192
-                EEH_Line_Item::update_quantity($child, $qty);
193
-            }
194
-        }
195
-    }
196
-
197
-
198
-    /**
199
-     * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected).
200
-     * Does NOT save or recalculate other line items totals
201
-     *
202
-     * @param EE_Line_Item $line_item
203
-     * @param int $qty
204
-     * @return void
205
-     * @throws \EE_Error
206
-     */
207
-    public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
208
-    {
209
-        if (!$line_item->is_percent()) {
210
-            $qty = $line_item->quantity() - $qty;
211
-            $qty = max($qty, 0);
212
-            $line_item->set_quantity($qty);
213
-            $line_item->set_total($line_item->unit_price() * $qty);
214
-            $line_item->save();
215
-        }
216
-        foreach ($line_item->children() as $child) {
217
-            if ($child->is_sub_line_item()) {
218
-                EEH_Line_Item::update_quantity($child, $qty);
219
-            }
220
-        }
221
-    }
222
-
223
-
224
-    /**
225
-     * Updates the line item and its children's quantities to the specified number.
226
-     * Does NOT save them or recalculate totals.
227
-     *
228
-     * @param EE_Line_Item $line_item
229
-     * @param int $new_quantity
230
-     * @throws \EE_Error
231
-     */
232
-    public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
233
-    {
234
-        if (!$line_item->is_percent()) {
235
-            $line_item->set_quantity($new_quantity);
236
-            $line_item->set_total($line_item->unit_price() * $new_quantity);
237
-            $line_item->save();
238
-        }
239
-        foreach ($line_item->children() as $child) {
240
-            if ($child->is_sub_line_item()) {
241
-                EEH_Line_Item::update_quantity($child, $new_quantity);
242
-            }
243
-        }
244
-    }
245
-
246
-
247
-    /**
248
-     * Returns the new line item created by adding a purchase of the ticket
249
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
250
-     * @param EE_Ticket $ticket
251
-     * @param int $qty
252
-     * @return \EE_Line_Item
253
-     * @throws \EE_Error
254
-     */
255
-    public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
256
-    {
257
-        $datetimes = $ticket->datetimes();
258
-        $first_datetime = reset($datetimes);
259
-        if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) {
260
-            $first_datetime_name = $first_datetime->event()->name();
261
-        } else {
262
-            $first_datetime_name = __('Event', 'event_espresso');
263
-        }
264
-        $event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name);
265
-        // get event subtotal line
266
-        $events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket);
267
-        // add $ticket to cart
268
-        $line_item = EE_Line_Item::new_instance(array(
269
-            'LIN_name' => $ticket->name(),
270
-            'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
271
-            'LIN_unit_price' => $ticket->price(),
272
-            'LIN_quantity' => $qty,
273
-            'LIN_is_taxable' => $ticket->taxable(),
274
-            'LIN_order' => count($events_sub_total->children()),
275
-            'LIN_total' => $ticket->price() * $qty,
276
-            'LIN_type' => EEM_Line_Item::type_line_item,
277
-            'OBJ_ID' => $ticket->ID(),
278
-            'OBJ_type' => 'Ticket'
279
-        ));
280
-        $line_item = apply_filters(
281
-            'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
282
-            $line_item
283
-        );
284
-        $events_sub_total->add_child_line_item($line_item);
285
-        //now add the sub-line items
286
-        $running_total_for_ticket = 0;
287
-        foreach ($ticket->prices(array('order_by' => array('PRC_order' => 'ASC'))) as $price) {
288
-            $sign = $price->is_discount() ? -1 : 1;
289
-            $price_total = $price->is_percent()
290
-                ? $running_total_for_ticket * $price->amount() / 100
291
-                : $price->amount() * $qty;
292
-            $sub_line_item = EE_Line_Item::new_instance(array(
293
-                'LIN_name' => $price->name(),
294
-                'LIN_desc' => $price->desc(),
295
-                'LIN_quantity' => $price->is_percent() ? null : $qty,
296
-                'LIN_is_taxable' => false,
297
-                'LIN_order' => $price->order(),
298
-                'LIN_total' => $sign * $price_total,
299
-                'LIN_type' => EEM_Line_Item::type_sub_line_item,
300
-                'OBJ_ID' => $price->ID(),
301
-                'OBJ_type' => 'Price'
302
-            ));
303
-            $sub_line_item = apply_filters(
304
-                'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item',
305
-                $sub_line_item
306
-            );
307
-            if ($price->is_percent()) {
308
-                $sub_line_item->set_percent($sign * $price->amount());
309
-            } else {
310
-                $sub_line_item->set_unit_price($sign * $price->amount());
311
-            }
312
-            $running_total_for_ticket += $price_total;
313
-            $line_item->add_child_line_item($sub_line_item);
314
-        }
315
-        return $line_item;
316
-    }
317
-
318
-
319
-    /**
320
-     * Adds the specified item under the pre-tax-sub-total line item. Automatically
321
-     * re-calculates the line item totals and updates the related transaction. But
322
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
323
-     * should probably change because of this).
324
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
325
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
326
-     *
327
-     * @param EE_Line_Item $total_line_item
328
-     * @param EE_Line_Item $item to be added
329
-     * @return boolean
330
-     * @throws \EE_Error
331
-     */
332
-    public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item)
333
-    {
334
-        $pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item);
335
-        if ($pre_tax_subtotal instanceof EE_Line_Item) {
336
-            $success = $pre_tax_subtotal->add_child_line_item($item);
337
-        } else {
338
-            return FALSE;
339
-        }
340
-        $total_line_item->recalculate_total_including_taxes();
341
-        return $success;
342
-    }
343
-
344
-
345
-    /**
346
-     * cancels an existing ticket line item,
347
-     * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item.
348
-     * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
349
-     *
350
-     * @param EE_Line_Item $ticket_line_item
351
-     * @param int $qty
352
-     * @return bool success
353
-     * @throws \EE_Error
354
-     */
355
-    public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
356
-    {
357
-        // validate incoming line_item
358
-        if ($ticket_line_item->OBJ_type() !== 'Ticket') {
359
-            throw new EE_Error(
360
-                sprintf(
361
-                    __('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
362
-                    $ticket_line_item->type()
363
-                )
364
-            );
365
-        }
366
-        if ($ticket_line_item->quantity() < $qty) {
367
-            throw new EE_Error(
368
-                sprintf(
369
-                    __('Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.', 'event_espresso'),
370
-                    $qty,
371
-                    $ticket_line_item->quantity()
372
-                )
373
-            );
374
-        }
375
-        // decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this
376
-        $ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty);
377
-        foreach ($ticket_line_item->children() as $child_line_item) {
378
-            if (
379
-                $child_line_item->is_sub_line_item()
380
-                && !$child_line_item->is_percent()
381
-                && !$child_line_item->is_cancellation()
382
-            ) {
383
-                $child_line_item->set_quantity($child_line_item->quantity() - $qty);
384
-            }
385
-        }
386
-        // get cancellation sub line item
387
-        $cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
388
-            $ticket_line_item,
389
-            EEM_Line_Item::type_cancellation
390
-        );
391
-        $cancellation_line_item = reset($cancellation_line_item);
392
-        // verify that this ticket was indeed previously cancelled
393
-        if ($cancellation_line_item instanceof EE_Line_Item) {
394
-            // increment cancelled quantity
395
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty);
396
-        } else {
397
-            // create cancellation sub line item
398
-            $cancellation_line_item = EE_Line_Item::new_instance(array(
399
-                'LIN_name' => __('Cancellation', 'event_espresso'),
400
-                'LIN_desc' => sprintf(
401
-                    _x('Cancelled %1$s : %2$s', 'Cancelled Ticket Name : 2015-01-01 11:11', 'event_espresso'),
402
-                    $ticket_line_item->name(),
403
-                    current_time(get_option('date_format') . ' ' . get_option('time_format'))
404
-                ),
405
-                'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
406
-                'LIN_quantity' => $qty,
407
-                'LIN_is_taxable' => $ticket_line_item->is_taxable(),
408
-                'LIN_order' => count($ticket_line_item->children()),
409
-                'LIN_total' => 0, // $ticket_line_item->unit_price()
410
-                'LIN_type' => EEM_Line_Item::type_cancellation,
411
-            ));
412
-            $ticket_line_item->add_child_line_item($cancellation_line_item);
413
-        }
414
-        if ($ticket_line_item->save_this_and_descendants() > 0) {
415
-            // decrement parent line item quantity
416
-            $event_line_item = $ticket_line_item->parent();
417
-            if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
418
-                $event_line_item->set_quantity($event_line_item->quantity() - $qty);
419
-                $event_line_item->save();
420
-            }
421
-            EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
422
-            return true;
423
-        }
424
-        return false;
425
-    }
426
-
427
-
428
-    /**
429
-     * reinstates (un-cancels?) a previously canceled ticket line item,
430
-     * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item.
431
-     * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
432
-     *
433
-     * @param EE_Line_Item $ticket_line_item
434
-     * @param int $qty
435
-     * @return bool success
436
-     * @throws \EE_Error
437
-     */
438
-    public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
439
-    {
440
-        // validate incoming line_item
441
-        if ($ticket_line_item->OBJ_type() !== 'Ticket') {
442
-            throw new EE_Error(
443
-                sprintf(
444
-                    __('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
445
-                    $ticket_line_item->type()
446
-                )
447
-            );
448
-        }
449
-        // get cancellation sub line item
450
-        $cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
451
-            $ticket_line_item,
452
-            EEM_Line_Item::type_cancellation
453
-        );
454
-        $cancellation_line_item = reset($cancellation_line_item);
455
-        // verify that this ticket was indeed previously cancelled
456
-        if (!$cancellation_line_item instanceof EE_Line_Item) {
457
-            return false;
458
-        }
459
-        if ($cancellation_line_item->quantity() > $qty) {
460
-            // decrement cancelled quantity
461
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
462
-        } else if ($cancellation_line_item->quantity() == $qty) {
463
-            // decrement cancelled quantity in case anyone still has the object kicking around
464
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
465
-            // delete because quantity will end up as 0
466
-            $cancellation_line_item->delete();
467
-            // and attempt to destroy the object,
468
-            // even though PHP won't actually destroy it until it needs the memory
469
-            unset($cancellation_line_item);
470
-        } else {
471
-            // what ?!?! negative quantity ?!?!
472
-            throw new EE_Error(
473
-                sprintf(
474
-                    __('Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.',
475
-                        'event_espresso'),
476
-                    $qty,
477
-                    $cancellation_line_item->quantity()
478
-                )
479
-            );
480
-        }
481
-        // increment ticket quantity
482
-        $ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty);
483
-        if ($ticket_line_item->save_this_and_descendants() > 0) {
484
-            // increment parent line item quantity
485
-            $event_line_item = $ticket_line_item->parent();
486
-            if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
487
-                $event_line_item->set_quantity($event_line_item->quantity() + $qty);
488
-            }
489
-            EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
490
-            return true;
491
-        }
492
-        return false;
493
-    }
494
-
495
-
496
-    /**
497
-     * calls EEH_Line_Item::find_transaction_grand_total_for_line_item()
498
-     * then EE_Line_Item::recalculate_total_including_taxes() on the result
499
-     *
500
-     * @param EE_Line_Item $line_item
501
-     * @return \EE_Line_Item
502
-     */
503
-    public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item)
504
-    {
505
-        $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item);
506
-        return $grand_total_line_item->recalculate_total_including_taxes();
507
-    }
508
-
509
-
510
-    /**
511
-     * Gets the line item which contains the subtotal of all the items
512
-     *
513
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
514
-     * @return \EE_Line_Item
515
-     * @throws \EE_Error
516
-     */
517
-    public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item)
518
-    {
519
-        $pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal');
520
-        return $pre_tax_subtotal instanceof EE_Line_Item
521
-            ? $pre_tax_subtotal
522
-            : self::create_pre_tax_subtotal($total_line_item);
523
-    }
524
-
525
-
526
-    /**
527
-     * Gets the line item for the taxes subtotal
528
-     *
529
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
530
-     * @return \EE_Line_Item
531
-     * @throws \EE_Error
532
-     */
533
-    public static function get_taxes_subtotal(EE_Line_Item $total_line_item)
534
-    {
535
-        $taxes = $total_line_item->get_child_line_item('taxes');
536
-        return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item);
537
-    }
538
-
539
-
540
-    /**
541
-     * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object
542
-     *
543
-     * @param EE_Line_Item $line_item
544
-     * @param EE_Transaction $transaction
545
-     * @return void
546
-     * @throws \EE_Error
547
-     */
548
-    public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = NULL)
549
-    {
550
-        if ($transaction) {
551
-            /** @type EEM_Transaction $EEM_Transaction */
552
-            $EEM_Transaction = EE_Registry::instance()->load_model('Transaction');
553
-            $TXN_ID = $EEM_Transaction->ensure_is_ID($transaction);
554
-            $line_item->set_TXN_ID($TXN_ID);
555
-        }
556
-    }
557
-
558
-
559
-    /**
560
-     * Creates a new default total line item for the transaction,
561
-     * and its tickets subtotal and taxes subtotal line items (and adds the
562
-     * existing taxes as children of the taxes subtotal line item)
563
-     *
564
-     * @param EE_Transaction $transaction
565
-     * @return \EE_Line_Item of type total
566
-     * @throws \EE_Error
567
-     */
568
-    public static function create_total_line_item($transaction = NULL)
569
-    {
570
-        $total_line_item = EE_Line_Item::new_instance(array(
571
-            'LIN_code' => 'total',
572
-            'LIN_name' => __('Grand Total', 'event_espresso'),
573
-            'LIN_type' => EEM_Line_Item::type_total,
574
-            'OBJ_type' => 'Transaction'
575
-        ));
576
-        $total_line_item = apply_filters(
577
-            'FHEE__EEH_Line_Item__create_total_line_item__total_line_item',
578
-            $total_line_item
579
-        );
580
-        self::set_TXN_ID($total_line_item, $transaction);
581
-        self::create_pre_tax_subtotal($total_line_item, $transaction);
582
-        self::create_taxes_subtotal($total_line_item, $transaction);
583
-        return $total_line_item;
584
-    }
585
-
586
-
587
-    /**
588
-     * Creates a default items subtotal line item
589
-     *
590
-     * @param EE_Line_Item $total_line_item
591
-     * @param EE_Transaction $transaction
592
-     * @return EE_Line_Item
593
-     * @throws \EE_Error
594
-     */
595
-    protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
596
-    {
597
-        $pre_tax_line_item = EE_Line_Item::new_instance(array(
598
-            'LIN_code' => 'pre-tax-subtotal',
599
-            'LIN_name' => __('Pre-Tax Subtotal', 'event_espresso'),
600
-            'LIN_type' => EEM_Line_Item::type_sub_total
601
-        ));
602
-        $pre_tax_line_item = apply_filters(
603
-            'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item',
604
-            $pre_tax_line_item
605
-        );
606
-        self::set_TXN_ID($pre_tax_line_item, $transaction);
607
-        $total_line_item->add_child_line_item($pre_tax_line_item);
608
-        self::create_event_subtotal($pre_tax_line_item, $transaction);
609
-        return $pre_tax_line_item;
610
-    }
611
-
612
-
613
-    /**
614
-     * Creates a line item for the taxes subtotal and finds all the tax prices
615
-     * and applies taxes to it
616
-     *
617
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
618
-     * @param EE_Transaction $transaction
619
-     * @return EE_Line_Item
620
-     * @throws \EE_Error
621
-     */
622
-    protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
623
-    {
624
-        $tax_line_item = EE_Line_Item::new_instance(array(
625
-            'LIN_code' => 'taxes',
626
-            'LIN_name' => __('Taxes', 'event_espresso'),
627
-            'LIN_type' => EEM_Line_Item::type_tax_sub_total,
628
-            'LIN_order' => 1000,//this should always come last
629
-        ));
630
-        $tax_line_item = apply_filters(
631
-            'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
632
-            $tax_line_item
633
-        );
634
-        self::set_TXN_ID($tax_line_item, $transaction);
635
-        $total_line_item->add_child_line_item($tax_line_item);
636
-        //and lastly, add the actual taxes
637
-        self::apply_taxes($total_line_item);
638
-        return $tax_line_item;
639
-    }
640
-
641
-
642
-    /**
643
-     * Creates a default items subtotal line item
644
-     *
645
-     * @param EE_Line_Item $pre_tax_line_item
646
-     * @param EE_Transaction $transaction
647
-     * @param EE_Event $event
648
-     * @return EE_Line_Item
649
-     * @throws \EE_Error
650
-     */
651
-    public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = NULL, $event = NULL)
652
-    {
653
-        $event_line_item = EE_Line_Item::new_instance(array(
654
-            'LIN_code' => self::get_event_code($event),
655
-            'LIN_name' => self::get_event_name($event),
656
-            'LIN_desc' => self::get_event_desc($event),
657
-            'LIN_type' => EEM_Line_Item::type_sub_total,
658
-            'OBJ_type' => 'Event',
659
-            'OBJ_ID' => $event instanceof EE_Event ? $event->ID() : 0
660
-        ));
661
-        $event_line_item = apply_filters(
662
-            'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item',
663
-            $event_line_item
664
-        );
665
-        self::set_TXN_ID($event_line_item, $transaction);
666
-        $pre_tax_line_item->add_child_line_item($event_line_item);
667
-        return $event_line_item;
668
-    }
669
-
670
-
671
-    /**
672
-     * Gets what the event ticket's code SHOULD be
673
-     *
674
-     * @param EE_Event $event
675
-     * @return string
676
-     * @throws \EE_Error
677
-     */
678
-    public static function get_event_code($event)
679
-    {
680
-        return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
681
-    }
682
-
683
-    /**
684
-     * Gets the event name
685
-     * @param EE_Event $event
686
-     * @return string
687
-     */
688
-    public static function get_event_name($event)
689
-    {
690
-        return $event instanceof EE_Event ? $event->name() : __('Event', 'event_espresso');
691
-    }
692
-
693
-    /**
694
-     * Gets the event excerpt
695
-     * @param EE_Event $event
696
-     * @return string
697
-     */
698
-    public static function get_event_desc($event)
699
-    {
700
-        return $event instanceof EE_Event ? $event->short_description() : '';
701
-    }
702
-
703
-    /**
704
-     * Given the grand total line item and a ticket, finds the event sub-total
705
-     * line item the ticket's purchase should be added onto
706
-     *
707
-     * @access public
708
-     * @param EE_Line_Item $grand_total the grand total line item
709
-     * @param EE_Ticket $ticket
710
-     * @throws \EE_Error
711
-     * @return EE_Line_Item
712
-     */
713
-    public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
714
-    {
715
-        $first_datetime = $ticket->first_datetime();
716
-        if (!$first_datetime instanceof EE_Datetime) {
717
-            throw new EE_Error(
718
-                sprintf(__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), $ticket->ID())
719
-            );
720
-        }
721
-        $event = $first_datetime->event();
722
-        if (!$event instanceof EE_Event) {
723
-            throw new EE_Error(
724
-                sprintf(
725
-                    __('The supplied ticket (ID %d) has no event data associated with it.', 'event_espresso'),
726
-                    $ticket->ID()
727
-                )
728
-            );
729
-        }
730
-        $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
731
-        if (!$events_sub_total instanceof EE_Line_Item) {
732
-            throw new EE_Error(
733
-                sprintf(
734
-                    __('There is no events sub-total for ticket %s on total line item %d', 'event_espresso'),
735
-                    $ticket->ID(),
736
-                    $grand_total->ID()
737
-                )
738
-            );
739
-        }
740
-        return $events_sub_total;
741
-    }
742
-
743
-
744
-    /**
745
-     * Gets the event line item
746
-     *
747
-     * @param EE_Line_Item $grand_total
748
-     * @param EE_Event $event
749
-     * @return EE_Line_Item for the event subtotal which is a child of $grand_total
750
-     * @throws \EE_Error
751
-     */
752
-    public static function get_event_line_item(EE_Line_Item $grand_total, $event)
753
-    {
754
-        /** @type EE_Event $event */
755
-        $event = EEM_Event::instance()->ensure_is_obj($event, true);
756
-        $event_line_item = NULL;
757
-        $found = false;
758
-        foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
759
-            // default event subtotal, we should only ever find this the first time this method is called
760
-            if (!$event_line_item->OBJ_ID()) {
761
-                // let's use this! but first... set the event details
762
-                EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
763
-                $found = true;
764
-                break;
765
-            } else if ($event_line_item->OBJ_ID() === $event->ID()) {
766
-                // found existing line item for this event in the cart, so break out of loop and use this one
767
-                $found = true;
768
-                break;
769
-            }
770
-        }
771
-        if (!$found) {
772
-            //there is no event sub-total yet, so add it
773
-            $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
774
-            // create a new "event" subtotal below that
775
-            $event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event);
776
-            // and set the event details
777
-            EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
778
-        }
779
-        return $event_line_item;
780
-    }
781
-
782
-
783
-    /**
784
-     * Creates a default items subtotal line item
785
-     *
786
-     * @param EE_Line_Item $event_line_item
787
-     * @param EE_Event $event
788
-     * @param EE_Transaction $transaction
789
-     * @return EE_Line_Item
790
-     * @throws \EE_Error
791
-     */
792
-    public static function set_event_subtotal_details(
793
-        EE_Line_Item $event_line_item,
794
-        EE_Event $event,
795
-        $transaction = null
796
-    )
797
-    {
798
-        if ($event instanceof EE_Event) {
799
-            $event_line_item->set_code(self::get_event_code($event));
800
-            $event_line_item->set_name(self::get_event_name($event));
801
-            $event_line_item->set_desc(self::get_event_desc($event));
802
-            $event_line_item->set_OBJ_ID($event->ID());
803
-        }
804
-        self::set_TXN_ID($event_line_item, $transaction);
805
-    }
806
-
807
-
808
-    /**
809
-     * Finds what taxes should apply, adds them as tax line items under the taxes sub-total,
810
-     * and recalculates the taxes sub-total and the grand total. Resets the taxes, so
811
-     * any old taxes are removed
812
-     *
813
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
814
-     * @throws \EE_Error
815
-     */
816
-    public static function apply_taxes(EE_Line_Item $total_line_item)
817
-    {
818
-        /** @type EEM_Price $EEM_Price */
819
-        $EEM_Price = EE_Registry::instance()->load_model('Price');
820
-        // get array of taxes via Price Model
821
-        $ordered_taxes = $EEM_Price->get_all_prices_that_are_taxes();
822
-        ksort($ordered_taxes);
823
-        $taxes_line_item = self::get_taxes_subtotal($total_line_item);
824
-        //just to be safe, remove its old tax line items
825
-        $taxes_line_item->delete_children_line_items();
826
-        //loop thru taxes
827
-        foreach ($ordered_taxes as $order => $taxes) {
828
-            foreach ($taxes as $tax) {
829
-                if ($tax instanceof EE_Price) {
830
-                    $tax_line_item = EE_Line_Item::new_instance(
831
-                        array(
832
-                            'LIN_name' => $tax->name(),
833
-                            'LIN_desc' => $tax->desc(),
834
-                            'LIN_percent' => $tax->amount(),
835
-                            'LIN_is_taxable' => false,
836
-                            'LIN_order' => $order,
837
-                            'LIN_total' => 0,
838
-                            'LIN_type' => EEM_Line_Item::type_tax,
839
-                            'OBJ_type' => 'Price',
840
-                            'OBJ_ID' => $tax->ID()
841
-                        )
842
-                    );
843
-                    $tax_line_item = apply_filters(
844
-                        'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
845
-                        $tax_line_item
846
-                    );
847
-                    $taxes_line_item->add_child_line_item($tax_line_item);
848
-                }
849
-            }
850
-        }
851
-        $total_line_item->recalculate_total_including_taxes();
852
-    }
853
-
854
-
855
-    /**
856
-     * Ensures that taxes have been applied to the order, if not applies them.
857
-     * Returns the total amount of tax
858
-     *
859
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
860
-     * @return float
861
-     * @throws \EE_Error
862
-     */
863
-    public static function ensure_taxes_applied($total_line_item)
864
-    {
865
-        $taxes_subtotal = self::get_taxes_subtotal($total_line_item);
866
-        if (!$taxes_subtotal->children()) {
867
-            self::apply_taxes($total_line_item);
868
-        }
869
-        return $taxes_subtotal->total();
870
-    }
871
-
872
-
873
-    /**
874
-     * Deletes ALL children of the passed line item
875
-     *
876
-     * @param EE_Line_Item $parent_line_item
877
-     * @return bool
878
-     * @throws \EE_Error
879
-     */
880
-    public static function delete_all_child_items(EE_Line_Item $parent_line_item)
881
-    {
882
-        $deleted = 0;
883
-        foreach ($parent_line_item->children() as $child_line_item) {
884
-            if ($child_line_item instanceof EE_Line_Item) {
885
-                $deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
886
-                if ($child_line_item->ID()) {
887
-                    $child_line_item->delete();
888
-                    unset($child_line_item);
889
-                } else {
890
-                    $parent_line_item->delete_child_line_item($child_line_item->code());
891
-                }
892
-                $deleted++;
893
-            }
894
-        }
895
-        return $deleted;
896
-    }
897
-
898
-
899
-    /**
900
-     * Deletes the line items as indicated by the line item code(s) provided,
901
-     * regardless of where they're found in the line item tree. Automatically
902
-     * re-calculates the line item totals and updates the related transaction. But
903
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
904
-     * should probably change because of this).
905
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
906
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
907
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
908
-     * @param array|bool|string $line_item_codes
909
-     * @return int number of items successfully removed
910
-     */
911
-    public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = FALSE)
912
-    {
913
-
914
-        if ($total_line_item->type() !== EEM_Line_Item::type_total) {
915
-            EE_Error::doing_it_wrong(
916
-                'EEH_Line_Item::delete_items',
917
-                __(
918
-                    'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly',
919
-                    'event_espresso'
920
-                ),
921
-                '4.6.18'
922
-            );
923
-        }
924
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
925
-
926
-        // check if only a single line_item_id was passed
927
-        if (!empty($line_item_codes) && !is_array($line_item_codes)) {
928
-            // place single line_item_id in an array to appear as multiple line_item_ids
929
-            $line_item_codes = array($line_item_codes);
930
-        }
931
-        $removals = 0;
932
-        // cycle thru line_item_ids
933
-        foreach ($line_item_codes as $line_item_id) {
934
-            $removals += $total_line_item->delete_child_line_item($line_item_id);
935
-        }
936
-
937
-        if ($removals > 0) {
938
-            $total_line_item->recalculate_taxes_and_tax_total();
939
-            return $removals;
940
-        } else {
941
-            return FALSE;
942
-        }
943
-    }
944
-
945
-
946
-    /**
947
-     * Overwrites the previous tax by clearing out the old taxes, and creates a new
948
-     * tax and updates the total line item accordingly
949
-     *
950
-     * @param EE_Line_Item $total_line_item
951
-     * @param float $amount
952
-     * @param string $name
953
-     * @param string $description
954
-     * @param string $code
955
-     * @param boolean $add_to_existing_line_item
956
-     *                          if true, and a duplicate line item with the same code is found,
957
-     *                          $amount will be added onto it; otherwise will simply set the taxes to match $amount
958
-     * @return EE_Line_Item the new tax line item created
959
-     * @throws \EE_Error
960
-     */
961
-    public static function set_total_tax_to(
962
-        EE_Line_Item $total_line_item,
963
-        $amount,
964
-        $name = null,
965
-        $description = null,
966
-        $code = null,
967
-        $add_to_existing_line_item = false
968
-    )
969
-    {
970
-        $tax_subtotal = self::get_taxes_subtotal($total_line_item);
971
-        $taxable_total = $total_line_item->taxable_total();
972
-
973
-        if ($add_to_existing_line_item) {
974
-            $new_tax = $tax_subtotal->get_child_line_item($code);
975
-            EEM_Line_Item::instance()->delete(
976
-                array(array('LIN_code' => array('!=', $code), 'LIN_parent' => $tax_subtotal->ID()))
977
-            );
978
-        } else {
979
-            $new_tax = null;
980
-            $tax_subtotal->delete_children_line_items();
981
-        }
982
-        if ($new_tax) {
983
-            $new_tax->set_total($new_tax->total() + $amount);
984
-            $new_tax->set_percent($taxable_total ? $new_tax->total() / $taxable_total * 100 : 0);
985
-        } else {
986
-            //no existing tax item. Create it
987
-            $new_tax = EE_Line_Item::new_instance(array(
988
-                'TXN_ID' => $total_line_item->TXN_ID(),
989
-                'LIN_name' => $name ? $name : __('Tax', 'event_espresso'),
990
-                'LIN_desc' => $description ? $description : '',
991
-                'LIN_percent' => $taxable_total ? ($amount / $taxable_total * 100) : 0,
992
-                'LIN_total' => $amount,
993
-                'LIN_parent' => $tax_subtotal->ID(),
994
-                'LIN_type' => EEM_Line_Item::type_tax,
995
-                'LIN_code' => $code
996
-            ));
997
-        }
998
-
999
-        $new_tax = apply_filters(
1000
-            'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal',
1001
-            $new_tax,
1002
-            $total_line_item
1003
-        );
1004
-        $new_tax->save();
1005
-        $tax_subtotal->set_total($new_tax->total());
1006
-        $tax_subtotal->save();
1007
-        $total_line_item->recalculate_total_including_taxes();
1008
-        return $new_tax;
1009
-    }
1010
-
1011
-
1012
-    /**
1013
-     * Makes all the line items which are children of $line_item taxable (or not).
1014
-     * Does NOT save the line items
1015
-     * @param EE_Line_Item $line_item
1016
-     * @param string $code_substring_for_whitelist if this string is part of the line item's code
1017
-     *  it will be whitelisted (ie, except from becoming taxable)
1018
-     * @param boolean $taxable
1019
-     */
1020
-    public static function set_line_items_taxable(
1021
-        EE_Line_Item $line_item,
1022
-        $taxable = true,
1023
-        $code_substring_for_whitelist = null
1024
-    )
1025
-    {
1026
-        $whitelisted = false;
1027
-        if ($code_substring_for_whitelist !== null) {
1028
-            $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false ? true : false;
1029
-        }
1030
-        if (!$whitelisted && $line_item->is_line_item()) {
1031
-            $line_item->set_is_taxable($taxable);
1032
-        }
1033
-        foreach ($line_item->children() as $child_line_item) {
1034
-            EEH_Line_Item::set_line_items_taxable($child_line_item, $taxable, $code_substring_for_whitelist);
1035
-        }
1036
-    }
1037
-
1038
-
1039
-    /**
1040
-     * Gets all descendants that are event subtotals
1041
-     *
1042
-     * @uses  EEH_Line_Item::get_subtotals_of_object_type()
1043
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1044
-     * @return EE_Line_Item[]
1045
-     */
1046
-    public static function get_event_subtotals(EE_Line_Item $parent_line_item)
1047
-    {
1048
-        return self::get_subtotals_of_object_type($parent_line_item, 'Event');
1049
-    }
1050
-
1051
-
1052
-    /**
1053
-     * Gets all descendants subtotals that match the supplied object type
1054
-     *
1055
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1056
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1057
-     * @param string $obj_type
1058
-     * @return EE_Line_Item[]
1059
-     */
1060
-    public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1061
-    {
1062
-        return self::_get_descendants_by_type_and_object_type(
1063
-            $parent_line_item,
1064
-            EEM_Line_Item::type_sub_total,
1065
-            $obj_type
1066
-        );
1067
-    }
1068
-
1069
-
1070
-    /**
1071
-     * Gets all descendants that are tickets
1072
-     *
1073
-     * @uses  EEH_Line_Item::get_line_items_of_object_type()
1074
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1075
-     * @return EE_Line_Item[]
1076
-     */
1077
-    public static function get_ticket_line_items(EE_Line_Item $parent_line_item)
1078
-    {
1079
-        return self::get_line_items_of_object_type($parent_line_item, 'Ticket');
1080
-    }
1081
-
1082
-
1083
-    /**
1084
-     * Gets all descendants subtotals that match the supplied object type
1085
-     *
1086
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1087
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1088
-     * @param string $obj_type
1089
-     * @return EE_Line_Item[]
1090
-     */
1091
-    public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1092
-    {
1093
-        return self::_get_descendants_by_type_and_object_type($parent_line_item, EEM_Line_Item::type_line_item, $obj_type);
1094
-    }
1095
-
1096
-
1097
-    /**
1098
-     * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax'
1099
-     * @uses  EEH_Line_Item::get_descendants_of_type()
1100
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1101
-     * @return EE_Line_Item[]
1102
-     */
1103
-    public static function get_tax_descendants(EE_Line_Item $parent_line_item)
1104
-    {
1105
-        return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_tax);
1106
-    }
1107
-
1108
-
1109
-    /**
1110
-     * Gets all the real items purchased which are children of this item
1111
-     * @uses  EEH_Line_Item::get_descendants_of_type()
1112
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1113
-     * @return EE_Line_Item[]
1114
-     */
1115
-    public static function get_line_item_descendants(EE_Line_Item $parent_line_item)
1116
-    {
1117
-        return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_line_item);
1118
-    }
1119
-
1120
-
1121
-    /**
1122
-     * Gets all descendants of supplied line item that match the supplied line item type
1123
-     *
1124
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1125
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1126
-     * @param string $line_item_type one of the EEM_Line_Item constants
1127
-     * @return EE_Line_Item[]
1128
-     */
1129
-    public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type)
1130
-    {
1131
-        return self::_get_descendants_by_type_and_object_type($parent_line_item, $line_item_type, NULL);
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1137
-     *
1138
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1139
-     * @param string $line_item_type one of the EEM_Line_Item constants
1140
-     * @param string | NULL $obj_type object model class name (minus prefix) or NULL to ignore object type when searching
1141
-     * @return EE_Line_Item[]
1142
-     */
1143
-    protected static function _get_descendants_by_type_and_object_type(
1144
-        EE_Line_Item $parent_line_item,
1145
-        $line_item_type,
1146
-        $obj_type = null
1147
-    )
1148
-    {
1149
-        $objects = array();
1150
-        foreach ($parent_line_item->children() as $child_line_item) {
1151
-            if ($child_line_item instanceof EE_Line_Item) {
1152
-                if (
1153
-                    $child_line_item->type() === $line_item_type
1154
-                    && (
1155
-                        $child_line_item->OBJ_type() === $obj_type || $obj_type === null
1156
-                    )
1157
-                ) {
1158
-                    $objects[] = $child_line_item;
1159
-                } else {
1160
-                    //go-through-all-its children looking for more matches
1161
-                    $objects = array_merge(
1162
-                        $objects,
1163
-                        self::_get_descendants_by_type_and_object_type(
1164
-                            $child_line_item,
1165
-                            $line_item_type,
1166
-                            $obj_type
1167
-                        )
1168
-                    );
1169
-                }
1170
-            }
1171
-        }
1172
-        return $objects;
1173
-    }
1174
-
1175
-
1176
-    /**
1177
-     * Gets all descendants subtotals that match the supplied object type
1178
-     *
1179
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1180
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1181
-     * @param string $OBJ_type object type (like Event)
1182
-     * @param array $OBJ_IDs array of OBJ_IDs
1183
-     * @return EE_Line_Item[]
1184
-     */
1185
-    public static function get_line_items_by_object_type_and_IDs(
1186
-        EE_Line_Item $parent_line_item,
1187
-        $OBJ_type = '',
1188
-        $OBJ_IDs = array()
1189
-    )
1190
-    {
1191
-        return self::_get_descendants_by_object_type_and_object_ID($parent_line_item, $OBJ_type, $OBJ_IDs);
1192
-    }
1193
-
1194
-
1195
-    /**
1196
-     * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1197
-     *
1198
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1199
-     * @param string $OBJ_type object type (like Event)
1200
-     * @param array $OBJ_IDs array of OBJ_IDs
1201
-     * @return EE_Line_Item[]
1202
-     */
1203
-    protected static function _get_descendants_by_object_type_and_object_ID(
1204
-        EE_Line_Item $parent_line_item,
1205
-        $OBJ_type,
1206
-        $OBJ_IDs
1207
-    )
1208
-    {
1209
-        $objects = array();
1210
-        foreach ($parent_line_item->children() as $child_line_item) {
1211
-            if ($child_line_item instanceof EE_Line_Item) {
1212
-                if (
1213
-                    $child_line_item->OBJ_type() === $OBJ_type
1214
-                    && is_array($OBJ_IDs)
1215
-                    && in_array($child_line_item->OBJ_ID(), $OBJ_IDs)
1216
-                ) {
1217
-                    $objects[] = $child_line_item;
1218
-                } else {
1219
-                    //go-through-all-its children looking for more matches
1220
-                    $objects = array_merge(
1221
-                        $objects,
1222
-                        self::_get_descendants_by_object_type_and_object_ID(
1223
-                            $child_line_item,
1224
-                            $OBJ_type,
1225
-                            $OBJ_IDs
1226
-                        )
1227
-                    );
1228
-                }
1229
-            }
1230
-        }
1231
-        return $objects;
1232
-    }
1233
-
1234
-
1235
-    /**
1236
-     * Uses a breadth-first-search in order to find the nearest descendant of
1237
-     * the specified type and returns it, else NULL
1238
-     *
1239
-     * @uses  EEH_Line_Item::_get_nearest_descendant()
1240
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1241
-     * @param string $type like one of the EEM_Line_Item::type_*
1242
-     * @return EE_Line_Item
1243
-     */
1244
-    public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type)
1245
-    {
1246
-        return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type);
1247
-    }
1248
-
1249
-
1250
-    /**
1251
-     * Uses a breadth-first-search in order to find the nearest descendant
1252
-     * having the specified LIN_code and returns it, else NULL
1253
-     *
1254
-     * @uses  EEH_Line_Item::_get_nearest_descendant()
1255
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1256
-     * @param string $code any value used for LIN_code
1257
-     * @return EE_Line_Item
1258
-     */
1259
-    public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code)
1260
-    {
1261
-        return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code);
1262
-    }
1263
-
1264
-
1265
-    /**
1266
-     * Uses a breadth-first-search in order to find the nearest descendant
1267
-     * having the specified LIN_code and returns it, else NULL
1268
-     *
1269
-     * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1270
-     * @param string $search_field name of EE_Line_Item property
1271
-     * @param string $value any value stored in $search_field
1272
-     * @return EE_Line_Item
1273
-     */
1274
-    protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value)
1275
-    {
1276
-        foreach ($parent_line_item->children() as $child) {
1277
-            if ($child->get($search_field) == $value) {
1278
-                return $child;
1279
-            }
1280
-        }
1281
-        foreach ($parent_line_item->children() as $child) {
1282
-            $descendant_found = self::_get_nearest_descendant($child, $search_field, $value);
1283
-            if ($descendant_found) {
1284
-                return $descendant_found;
1285
-            }
1286
-        }
1287
-        return NULL;
1288
-    }
1289
-
1290
-
1291
-    /**
1292
-     * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction,
1293
-     * else recursively walks up the line item tree until a parent of type total is found,
1294
-     *
1295
-     * @param EE_Line_Item $line_item
1296
-     * @return \EE_Line_Item
1297
-     * @throws \EE_Error
1298
-     */
1299
-    public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item)
1300
-    {
1301
-        if ($line_item->TXN_ID()) {
1302
-            $total_line_item = $line_item->transaction()->total_line_item(false);
1303
-            if ($total_line_item instanceof EE_Line_Item) {
1304
-                return $total_line_item;
1305
-            }
1306
-        } else {
1307
-            $line_item_parent = $line_item->parent();
1308
-            if ($line_item_parent instanceof EE_Line_Item) {
1309
-                if ($line_item_parent->is_total()) {
1310
-                    return $line_item_parent;
1311
-                }
1312
-                return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent);
1313
-            }
1314
-        }
1315
-        throw new EE_Error(
1316
-            sprintf(
1317
-                __('A valid grand total for line item %1$d was not found.', 'event_espresso'),
1318
-                $line_item->ID()
1319
-            )
1320
-        );
1321
-    }
1322
-
1323
-
1324
-    /**
1325
-     * Prints out a representation of the line item tree
1326
-     *
1327
-     * @param EE_Line_Item $line_item
1328
-     * @param int $indentation
1329
-     * @return void
1330
-     * @throws \EE_Error
1331
-     */
1332
-    public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1333
-    {
1334
-        echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1335
-        if (!$indentation) {
1336
-            echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1337
-        }
1338
-        for ($i = 0; $i < $indentation; $i++) {
1339
-            echo ". ";
1340
-        }
1341
-        $breakdown = '';
1342
-        if ($line_item->is_line_item()) {
1343
-            if ($line_item->is_percent()) {
1344
-                $breakdown = "{$line_item->percent()}%";
1345
-            } else {
1346
-                $breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1347
-            }
1348
-        }
1349
-        echo $line_item->name() . " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : " . '$' . "{$line_item->total()}";
1350
-        if ($breakdown) {
1351
-            echo " ( {$breakdown} )";
1352
-        }
1353
-        if ($line_item->is_taxable()) {
1354
-            echo "  * taxable";
1355
-        }
1356
-        if ($line_item->children()) {
1357
-            foreach ($line_item->children() as $child) {
1358
-                self::visualize($child, $indentation + 1);
1359
-            }
1360
-        }
1361
-    }
1362
-
1363
-
1364
-    /**
1365
-     * Calculates the registration's final price, taking into account that they
1366
-     * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes,
1367
-     * and receive a portion of any transaction-wide discounts.
1368
-     * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount
1369
-     * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get
1370
-     * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50
1371
-     * and brent's final price should be $5.50.
1372
-     *
1373
-     * In order to do this, we basically need to traverse the line item tree calculating
1374
-     * the running totals (just as if we were recalculating the total), but when we identify
1375
-     * regular line items, we need to keep track of their share of the grand total.
1376
-     * Also, we need to keep track of the TAXABLE total for each ticket purchase, so
1377
-     * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total"
1378
-     * when there are non-taxable items; otherwise they would be the same)
1379
-     *
1380
-     * @param EE_Line_Item $line_item
1381
-     * @param array $billable_ticket_quantities array of EE_Ticket IDs and their corresponding quantity that
1382
-     *                                                                            can be included in price calculations at this moment
1383
-     * @return array        keys are line items for tickets IDs and values are their share of the running total,
1384
-     *                                          plus the key 'total', and 'taxable' which also has keys of all the ticket IDs. Eg
1385
-     *                                          array(
1386
-     *                                          12 => 4.3
1387
-     *                                          23 => 8.0
1388
-     *                                          'total' => 16.6,
1389
-     *                                          'taxable' => array(
1390
-     *                                          12 => 10,
1391
-     *                                          23 => 4
1392
-     *                                          ).
1393
-     *                                          So to find which registrations have which final price, we need to find which line item
1394
-     *                                          is theirs, which can be done with
1395
-     *                                          `EEM_Line_Item::instance()->get_line_item_for_registration( $registration );`
1396
-     */
1397
-    public static function calculate_reg_final_prices_per_line_item(EE_Line_Item $line_item, $billable_ticket_quantities = array())
1398
-    {
1399
-        //init running grand total if not already
1400
-        if (!isset($running_totals['total'])) {
1401
-            $running_totals['total'] = 0;
1402
-        }
1403
-        if (!isset($running_totals['taxable'])) {
1404
-            $running_totals['taxable'] = array('total' => 0);
1405
-        }
1406
-        foreach ($line_item->children() as $child_line_item) {
1407
-            switch ($child_line_item->type()) {
1408
-
1409
-                case EEM_Line_Item::type_sub_total :
1410
-                    $running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item($child_line_item, $billable_ticket_quantities);
1411
-                    //combine arrays but preserve numeric keys
1412
-                    $running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals);
1413
-                    $running_totals['total'] += $running_totals_from_subtotal['total'];
1414
-                    $running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total'];
1415
-                    break;
1416
-
1417
-                case EEM_Line_Item::type_tax_sub_total :
1418
-
1419
-                    //find how much the taxes percentage is
1420
-                    if ($child_line_item->percent() !== 0) {
1421
-                        $tax_percent_decimal = $child_line_item->percent() / 100;
1422
-                    } else {
1423
-                        $tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100;
1424
-                    }
1425
-                    //and apply to all the taxable totals, and add to the pretax totals
1426
-                    foreach ($running_totals as $line_item_id => $this_running_total) {
1427
-                        //"total" and "taxable" array key is an exception
1428
-                        if ($line_item_id === 'taxable') {
1429
-                            continue;
1430
-                        }
1431
-                        $taxable_total = $running_totals['taxable'][$line_item_id];
1432
-                        $running_totals[$line_item_id] += ($taxable_total * $tax_percent_decimal);
1433
-                    }
1434
-                    break;
1435
-
1436
-                case EEM_Line_Item::type_line_item :
1437
-
1438
-                    // ticket line items or ????
1439
-                    if ($child_line_item->OBJ_type() === 'Ticket') {
1440
-                        // kk it's a ticket
1441
-                        if (isset($running_totals[$child_line_item->ID()])) {
1442
-                            //huh? that shouldn't happen.
1443
-                            $running_totals['total'] += $child_line_item->total();
1444
-                        } else {
1445
-                            //its not in our running totals yet. great.
1446
-                            if ($child_line_item->is_taxable()) {
1447
-                                $taxable_amount = $child_line_item->unit_price();
1448
-                            } else {
1449
-                                $taxable_amount = 0;
1450
-                            }
1451
-                            // are we only calculating totals for some tickets?
1452
-                            if (isset($billable_ticket_quantities[$child_line_item->OBJ_ID()])) {
1453
-                                $quantity = $billable_ticket_quantities[$child_line_item->OBJ_ID()];
1454
-                                $running_totals[$child_line_item->ID()] = $quantity
1455
-                                    ? $child_line_item->unit_price()
1456
-                                    : 0;
1457
-                                $running_totals['taxable'][$child_line_item->ID()] = $quantity
1458
-                                    ? $taxable_amount
1459
-                                    : 0;
1460
-                            } else {
1461
-                                $quantity = $child_line_item->quantity();
1462
-                                $running_totals[$child_line_item->ID()] = $child_line_item->unit_price();
1463
-                                $running_totals['taxable'][$child_line_item->ID()] = $taxable_amount;
1464
-                            }
1465
-                            $running_totals['taxable']['total'] += $taxable_amount * $quantity;
1466
-                            $running_totals['total'] += $child_line_item->unit_price() * $quantity;
1467
-                        }
1468
-                    } else {
1469
-                        // it's some other type of item added to the cart
1470
-                        // it should affect the running totals
1471
-                        // basically we want to convert it into a PERCENT modifier. Because
1472
-                        // more clearly affect all registration's final price equally
1473
-                        $line_items_percent_of_running_total = $running_totals['total'] > 0
1474
-                            ? ($child_line_item->total() / $running_totals['total']) + 1
1475
-                            : 1;
1476
-                        foreach ($running_totals as $line_item_id => $this_running_total) {
1477
-                            //the "taxable" array key is an exception
1478
-                            if ($line_item_id === 'taxable') {
1479
-                                continue;
1480
-                            }
1481
-                            // update the running totals
1482
-                            // yes this actually even works for the running grand total!
1483
-                            $running_totals[$line_item_id] =
1484
-                                $line_items_percent_of_running_total * $this_running_total;
1485
-
1486
-                            if ($child_line_item->is_taxable()) {
1487
-                                $running_totals['taxable'][$line_item_id] =
1488
-                                    $line_items_percent_of_running_total * $running_totals['taxable'][$line_item_id];
1489
-                            }
1490
-                        }
1491
-                    }
1492
-                    break;
1493
-            }
1494
-        }
1495
-        return $running_totals;
1496
-    }
1497
-
1498
-
1499
-    /**
1500
-     * @param \EE_Line_Item $total_line_item
1501
-     * @param \EE_Line_Item $ticket_line_item
1502
-     * @return float | null
1503
-     * @throws \OutOfRangeException
1504
-     */
1505
-    public static function calculate_final_price_for_ticket_line_item(\EE_Line_Item $total_line_item, \EE_Line_Item $ticket_line_item)
1506
-    {
1507
-        static $final_prices_per_ticket_line_item = array();
1508
-        if (empty($final_prices_per_ticket_line_item)) {
1509
-            $final_prices_per_ticket_line_item = \EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1510
-                $total_line_item
1511
-            );
1512
-        }
1513
-        //ok now find this new registration's final price
1514
-        if (isset($final_prices_per_ticket_line_item[$ticket_line_item->ID()])) {
1515
-            return $final_prices_per_ticket_line_item[$ticket_line_item->ID()];
1516
-        }
1517
-        $message = sprintf(
1518
-            __(
1519
-                'The final price for the ticket line item (ID:%1$d) could not be calculated.',
1520
-                'event_espresso'
1521
-            ),
1522
-            $ticket_line_item->ID()
1523
-        );
1524
-        if (WP_DEBUG) {
1525
-            $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1526
-            throw new \OutOfRangeException($message);
1527
-        } else {
1528
-            EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
1529
-        }
1530
-        return null;
1531
-    }
1532
-
1533
-
1534
-    /**
1535
-     * Creates a duplicate of the line item tree, except only includes billable items
1536
-     * and the portion of line items attributed to billable things
1537
-     *
1538
-     * @param EE_Line_Item $line_item
1539
-     * @param EE_Registration[] $registrations
1540
-     * @return \EE_Line_Item
1541
-     * @throws \EE_Error
1542
-     */
1543
-    public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations)
1544
-    {
1545
-        $copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations);
1546
-        foreach ($line_item->children() as $child_li) {
1547
-            $copy_li->add_child_line_item(EEH_Line_Item::billable_line_item_tree($child_li, $registrations));
1548
-        }
1549
-        //if this is the grand total line item, make sure the totals all add up
1550
-        //(we could have duplicated this logic AS we copied the line items, but
1551
-        //it seems DRYer this way)
1552
-        if ($copy_li->type() === EEM_Line_Item::type_total) {
1553
-            $copy_li->recalculate_total_including_taxes();
1554
-        }
1555
-        return $copy_li;
1556
-    }
1557
-
1558
-
1559
-    /**
1560
-     * Creates a new, unsaved line item from $line_item that factors in the
1561
-     * number of billable registrations on $registrations.
1562
-     *
1563
-     * @param EE_Line_Item $line_item
1564
-     * @return EE_Line_Item
1565
-     * @throws \EE_Error
1566
-     * @param EE_Registration[] $registrations
1567
-     */
1568
-    public static function billable_line_item(EE_Line_Item $line_item, $registrations)
1569
-    {
1570
-        $new_li_fields = $line_item->model_field_array();
1571
-        if ($line_item->type() === EEM_Line_Item::type_line_item &&
1572
-            $line_item->OBJ_type() === 'Ticket'
1573
-        ) {
1574
-            $count = 0;
1575
-            foreach ($registrations as $registration) {
1576
-                if ($line_item->OBJ_ID() === $registration->ticket_ID() &&
1577
-                    in_array($registration->status_ID(), EEM_Registration::reg_statuses_that_allow_payment())
1578
-                ) {
1579
-                    $count++;
1580
-                }
1581
-            }
1582
-            $new_li_fields['LIN_quantity'] = $count;
1583
-        }
1584
-        //don't set the total. We'll leave that up to the code that calculates it
1585
-        unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']);
1586
-        return EE_Line_Item::new_instance($new_li_fields);
1587
-    }
1588
-
1589
-
1590
-    /**
1591
-     * Returns a modified line item tree where all the subtotals which have a total of 0
1592
-     * are removed, and line items with a quantity of 0
1593
-     *
1594
-     * @param EE_Line_Item $line_item |null
1595
-     * @return \EE_Line_Item|null
1596
-     * @throws \EE_Error
1597
-     */
1598
-    public static function non_empty_line_items(EE_Line_Item $line_item)
1599
-    {
1600
-        $copied_li = EEH_Line_Item::non_empty_line_item($line_item);
1601
-        if ($copied_li === null) {
1602
-            return null;
1603
-        }
1604
-        //if this is an event subtotal, we want to only include it if it
1605
-        //has a non-zero total and at least one ticket line item child
1606
-        $ticket_children = 0;
1607
-        foreach ($line_item->children() as $child_li) {
1608
-            $child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
1609
-            if ($child_li_copy !== null) {
1610
-                $copied_li->add_child_line_item($child_li_copy);
1611
-                if ($child_li_copy->type() === EEM_Line_Item::type_line_item &&
1612
-                    $child_li_copy->OBJ_type() === 'Ticket'
1613
-                ) {
1614
-                    $ticket_children++;
1615
-                }
1616
-            }
1617
-        }
1618
-        //if this is an event subtotal with NO ticket children
1619
-        //we basically want to ignore it
1620
-        if (
1621
-            $ticket_children === 0
1622
-            && $line_item->type() === EEM_Line_Item::type_sub_total
1623
-            && $line_item->OBJ_type() === 'Event'
1624
-            && $line_item->total() === 0
1625
-        ) {
1626
-            return null;
1627
-        }
1628
-        return $copied_li;
1629
-    }
1630
-
1631
-
1632
-    /**
1633
-     * Creates a new, unsaved line item, but if it's a ticket line item
1634
-     * with a total of 0, or a subtotal of 0, returns null instead
1635
-     *
1636
-     * @param EE_Line_Item $line_item
1637
-     * @return EE_Line_Item
1638
-     * @throws \EE_Error
1639
-     */
1640
-    public static function non_empty_line_item(EE_Line_Item $line_item)
1641
-    {
1642
-        if ($line_item->type() === EEM_Line_Item::type_line_item &&
1643
-            $line_item->OBJ_type() === 'Ticket' &&
1644
-            $line_item->quantity() === 0
1645
-        ) {
1646
-            return null;
1647
-        }
1648
-        $new_li_fields = $line_item->model_field_array();
1649
-        //don't set the total. We'll leave that up to the code that calculates it
1650
-        unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']);
1651
-        return EE_Line_Item::new_instance($new_li_fields);
1652
-    }
1653
-
1654
-
1655
-
1656
-    /**************************************** @DEPRECATED METHODS *************************************** */
1657
-    /**
1658
-     * @deprecated
1659
-     * @param EE_Line_Item $total_line_item
1660
-     * @return \EE_Line_Item
1661
-     * @throws \EE_Error
1662
-     */
1663
-    public static function get_items_subtotal(EE_Line_Item $total_line_item)
1664
-    {
1665
-        EE_Error::doing_it_wrong('EEH_Line_Item::get_items_subtotal()', __('Method replaced with EEH_Line_Item::get_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1666
-        return self::get_pre_tax_subtotal($total_line_item);
1667
-    }
1668
-
1669
-
1670
-    /**
1671
-     * @deprecated
1672
-     * @param EE_Transaction $transaction
1673
-     * @return \EE_Line_Item
1674
-     * @throws \EE_Error
1675
-     */
1676
-    public static function create_default_total_line_item($transaction = NULL)
1677
-    {
1678
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_total_line_item()', __('Method replaced with EEH_Line_Item::create_total_line_item()', 'event_espresso'), '4.6.0');
1679
-        return self::create_total_line_item($transaction);
1680
-    }
1681
-
1682
-
1683
-    /**
1684
-     * @deprecated
1685
-     * @param EE_Line_Item $total_line_item
1686
-     * @param EE_Transaction $transaction
1687
-     * @return \EE_Line_Item
1688
-     * @throws \EE_Error
1689
-     */
1690
-    public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1691
-    {
1692
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_tickets_subtotal()', __('Method replaced with EEH_Line_Item::create_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1693
-        return self::create_pre_tax_subtotal($total_line_item, $transaction);
1694
-    }
1695
-
1696
-
1697
-    /**
1698
-     * @deprecated
1699
-     * @param EE_Line_Item $total_line_item
1700
-     * @param EE_Transaction $transaction
1701
-     * @return \EE_Line_Item
1702
-     * @throws \EE_Error
1703
-     */
1704
-    public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1705
-    {
1706
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_taxes_subtotal()', __('Method replaced with EEH_Line_Item::create_taxes_subtotal()', 'event_espresso'), '4.6.0');
1707
-        return self::create_taxes_subtotal($total_line_item, $transaction);
1708
-    }
1709
-
1710
-
1711
-    /**
1712
-     * @deprecated
1713
-     * @param EE_Line_Item $total_line_item
1714
-     * @param EE_Transaction $transaction
1715
-     * @return \EE_Line_Item
1716
-     * @throws \EE_Error
1717
-     */
1718
-    public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1719
-    {
1720
-        EE_Error::doing_it_wrong('EEH_Line_Item::create_default_event_subtotal()', __('Method replaced with EEH_Line_Item::create_event_subtotal()', 'event_espresso'), '4.6.0');
1721
-        return self::create_event_subtotal($total_line_item, $transaction);
1722
-    }
26
+	//other functions: cancel ticket purchase
27
+	//delete ticket purchase
28
+	//add promotion
29
+
30
+
31
+	/**
32
+	 * Adds a simple item (unrelated to any other model object) to the provided PARENT line item.
33
+	 * Does NOT automatically re-calculate the line item totals or update the related transaction.
34
+	 * You should call recalculate_total_including_taxes() on the grant total line item after this
35
+	 * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
36
+	 * to keep the registration final prices in-sync with the transaction's total.
37
+	 *
38
+	 * @param EE_Line_Item $parent_line_item
39
+	 * @param string $name
40
+	 * @param float $unit_price
41
+	 * @param string $description
42
+	 * @param int $quantity
43
+	 * @param boolean $taxable
44
+	 * @param boolean $code if set to a value, ensures there is only one line item with that code
45
+	 * @return boolean success
46
+	 * @throws \EE_Error
47
+	 */
48
+	public static function add_unrelated_item(EE_Line_Item $parent_line_item, $name, $unit_price, $description = '', $quantity = 1, $taxable = FALSE, $code = NULL)
49
+	{
50
+		$items_subtotal = self::get_pre_tax_subtotal($parent_line_item);
51
+		$line_item = EE_Line_Item::new_instance(array(
52
+			'LIN_name' => $name,
53
+			'LIN_desc' => $description,
54
+			'LIN_unit_price' => $unit_price,
55
+			'LIN_quantity' => $quantity,
56
+			'LIN_percent' => null,
57
+			'LIN_is_taxable' => $taxable,
58
+			'LIN_order' => $items_subtotal instanceof EE_Line_Item ? count($items_subtotal->children()) : 0,
59
+			'LIN_total' => (float)$unit_price * (int)$quantity,
60
+			'LIN_type' => EEM_Line_Item::type_line_item,
61
+			'LIN_code' => $code,
62
+		));
63
+		$line_item = apply_filters(
64
+			'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
65
+			$line_item,
66
+			$parent_line_item
67
+		);
68
+		return self::add_item($parent_line_item, $line_item);
69
+	}
70
+
71
+
72
+	/**
73
+	 * Adds a simple item ( unrelated to any other model object) to the total line item,
74
+	 * in the correct spot in the line item tree. Automatically
75
+	 * re-calculates the line item totals and updates the related transaction. But
76
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
77
+	 * should probably change because of this).
78
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
79
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
80
+	 *
81
+	 * @param EE_Line_Item $parent_line_item
82
+	 * @param string $name
83
+	 * @param float $percentage_amount
84
+	 * @param string $description
85
+	 * @param boolean $taxable
86
+	 * @return boolean success
87
+	 * @throws \EE_Error
88
+	 */
89
+	public static function add_percentage_based_item(EE_Line_Item $parent_line_item, $name, $percentage_amount, $description = '', $taxable = FALSE)
90
+	{
91
+		$line_item = EE_Line_Item::new_instance(array(
92
+			'LIN_name' => $name,
93
+			'LIN_desc' => $description,
94
+			'LIN_unit_price' => 0,
95
+			'LIN_percent' => $percentage_amount,
96
+			'LIN_quantity' => 1,
97
+			'LIN_is_taxable' => $taxable,
98
+			'LIN_total' => (float)($percentage_amount * ($parent_line_item->total() / 100)),
99
+			'LIN_type' => EEM_Line_Item::type_line_item,
100
+			'LIN_parent' => $parent_line_item->ID()
101
+		));
102
+		$line_item = apply_filters(
103
+			'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
104
+			$line_item
105
+		);
106
+		return $parent_line_item->add_child_line_item($line_item, false);
107
+	}
108
+
109
+
110
+	/**
111
+	 * Returns the new line item created by adding a purchase of the ticket
112
+	 * ensures that ticket line item is saved, and that cart total has been recalculated.
113
+	 * If this ticket has already been purchased, just increments its count.
114
+	 * Automatically re-calculates the line item totals and updates the related transaction. But
115
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
116
+	 * should probably change because of this).
117
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
118
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
119
+	 *
120
+	 * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total
121
+	 * @param EE_Ticket $ticket
122
+	 * @param int $qty
123
+	 * @return \EE_Line_Item
124
+	 * @throws \EE_Error
125
+	 */
126
+	public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
127
+	{
128
+		if (!$total_line_item instanceof EE_Line_Item || !$total_line_item->is_total()) {
129
+			throw new EE_Error(sprintf(__('A valid line item total is required in order to add tickets. A line item of type "%s" was passed.', 'event_espresso'), $ticket->ID(), $total_line_item->ID()));
130
+		}
131
+		// either increment the qty for an existing ticket
132
+		$line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
133
+		// or add a new one
134
+		if (!$line_item instanceof EE_Line_Item) {
135
+			$line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
136
+		}
137
+		$total_line_item->recalculate_total_including_taxes();
138
+		return $line_item;
139
+	}
140
+
141
+
142
+	/**
143
+	 * Returns the new line item created by adding a purchase of the ticket
144
+	 * @param \EE_Line_Item $total_line_item
145
+	 * @param EE_Ticket $ticket
146
+	 * @param int $qty
147
+	 * @return \EE_Line_Item
148
+	 * @throws \EE_Error
149
+	 */
150
+	public static function increment_ticket_qty_if_already_in_cart(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
151
+	{
152
+		$line_item = null;
153
+		if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
154
+			$ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
155
+			foreach ((array)$ticket_line_items as $ticket_line_item) {
156
+				if (
157
+					$ticket_line_item instanceof EE_Line_Item
158
+					&& (int)$ticket_line_item->OBJ_ID() === (int)$ticket->ID()
159
+				) {
160
+					$line_item = $ticket_line_item;
161
+					break;
162
+				}
163
+			}
164
+		}
165
+		if ($line_item instanceof EE_Line_Item) {
166
+			EEH_Line_Item::increment_quantity($line_item, $qty);
167
+			return $line_item;
168
+		}
169
+		return null;
170
+	}
171
+
172
+
173
+	/**
174
+	 * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected).
175
+	 * Does NOT save or recalculate other line items totals
176
+	 *
177
+	 * @param EE_Line_Item $line_item
178
+	 * @param int $qty
179
+	 * @return void
180
+	 * @throws \EE_Error
181
+	 */
182
+	public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
183
+	{
184
+		if (!$line_item->is_percent()) {
185
+			$qty += $line_item->quantity();
186
+			$line_item->set_quantity($qty);
187
+			$line_item->set_total($line_item->unit_price() * $qty);
188
+			$line_item->save();
189
+		}
190
+		foreach ($line_item->children() as $child) {
191
+			if ($child->is_sub_line_item()) {
192
+				EEH_Line_Item::update_quantity($child, $qty);
193
+			}
194
+		}
195
+	}
196
+
197
+
198
+	/**
199
+	 * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected).
200
+	 * Does NOT save or recalculate other line items totals
201
+	 *
202
+	 * @param EE_Line_Item $line_item
203
+	 * @param int $qty
204
+	 * @return void
205
+	 * @throws \EE_Error
206
+	 */
207
+	public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
208
+	{
209
+		if (!$line_item->is_percent()) {
210
+			$qty = $line_item->quantity() - $qty;
211
+			$qty = max($qty, 0);
212
+			$line_item->set_quantity($qty);
213
+			$line_item->set_total($line_item->unit_price() * $qty);
214
+			$line_item->save();
215
+		}
216
+		foreach ($line_item->children() as $child) {
217
+			if ($child->is_sub_line_item()) {
218
+				EEH_Line_Item::update_quantity($child, $qty);
219
+			}
220
+		}
221
+	}
222
+
223
+
224
+	/**
225
+	 * Updates the line item and its children's quantities to the specified number.
226
+	 * Does NOT save them or recalculate totals.
227
+	 *
228
+	 * @param EE_Line_Item $line_item
229
+	 * @param int $new_quantity
230
+	 * @throws \EE_Error
231
+	 */
232
+	public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
233
+	{
234
+		if (!$line_item->is_percent()) {
235
+			$line_item->set_quantity($new_quantity);
236
+			$line_item->set_total($line_item->unit_price() * $new_quantity);
237
+			$line_item->save();
238
+		}
239
+		foreach ($line_item->children() as $child) {
240
+			if ($child->is_sub_line_item()) {
241
+				EEH_Line_Item::update_quantity($child, $new_quantity);
242
+			}
243
+		}
244
+	}
245
+
246
+
247
+	/**
248
+	 * Returns the new line item created by adding a purchase of the ticket
249
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
250
+	 * @param EE_Ticket $ticket
251
+	 * @param int $qty
252
+	 * @return \EE_Line_Item
253
+	 * @throws \EE_Error
254
+	 */
255
+	public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
256
+	{
257
+		$datetimes = $ticket->datetimes();
258
+		$first_datetime = reset($datetimes);
259
+		if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) {
260
+			$first_datetime_name = $first_datetime->event()->name();
261
+		} else {
262
+			$first_datetime_name = __('Event', 'event_espresso');
263
+		}
264
+		$event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name);
265
+		// get event subtotal line
266
+		$events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket);
267
+		// add $ticket to cart
268
+		$line_item = EE_Line_Item::new_instance(array(
269
+			'LIN_name' => $ticket->name(),
270
+			'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
271
+			'LIN_unit_price' => $ticket->price(),
272
+			'LIN_quantity' => $qty,
273
+			'LIN_is_taxable' => $ticket->taxable(),
274
+			'LIN_order' => count($events_sub_total->children()),
275
+			'LIN_total' => $ticket->price() * $qty,
276
+			'LIN_type' => EEM_Line_Item::type_line_item,
277
+			'OBJ_ID' => $ticket->ID(),
278
+			'OBJ_type' => 'Ticket'
279
+		));
280
+		$line_item = apply_filters(
281
+			'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
282
+			$line_item
283
+		);
284
+		$events_sub_total->add_child_line_item($line_item);
285
+		//now add the sub-line items
286
+		$running_total_for_ticket = 0;
287
+		foreach ($ticket->prices(array('order_by' => array('PRC_order' => 'ASC'))) as $price) {
288
+			$sign = $price->is_discount() ? -1 : 1;
289
+			$price_total = $price->is_percent()
290
+				? $running_total_for_ticket * $price->amount() / 100
291
+				: $price->amount() * $qty;
292
+			$sub_line_item = EE_Line_Item::new_instance(array(
293
+				'LIN_name' => $price->name(),
294
+				'LIN_desc' => $price->desc(),
295
+				'LIN_quantity' => $price->is_percent() ? null : $qty,
296
+				'LIN_is_taxable' => false,
297
+				'LIN_order' => $price->order(),
298
+				'LIN_total' => $sign * $price_total,
299
+				'LIN_type' => EEM_Line_Item::type_sub_line_item,
300
+				'OBJ_ID' => $price->ID(),
301
+				'OBJ_type' => 'Price'
302
+			));
303
+			$sub_line_item = apply_filters(
304
+				'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item',
305
+				$sub_line_item
306
+			);
307
+			if ($price->is_percent()) {
308
+				$sub_line_item->set_percent($sign * $price->amount());
309
+			} else {
310
+				$sub_line_item->set_unit_price($sign * $price->amount());
311
+			}
312
+			$running_total_for_ticket += $price_total;
313
+			$line_item->add_child_line_item($sub_line_item);
314
+		}
315
+		return $line_item;
316
+	}
317
+
318
+
319
+	/**
320
+	 * Adds the specified item under the pre-tax-sub-total line item. Automatically
321
+	 * re-calculates the line item totals and updates the related transaction. But
322
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
323
+	 * should probably change because of this).
324
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
325
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
326
+	 *
327
+	 * @param EE_Line_Item $total_line_item
328
+	 * @param EE_Line_Item $item to be added
329
+	 * @return boolean
330
+	 * @throws \EE_Error
331
+	 */
332
+	public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item)
333
+	{
334
+		$pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item);
335
+		if ($pre_tax_subtotal instanceof EE_Line_Item) {
336
+			$success = $pre_tax_subtotal->add_child_line_item($item);
337
+		} else {
338
+			return FALSE;
339
+		}
340
+		$total_line_item->recalculate_total_including_taxes();
341
+		return $success;
342
+	}
343
+
344
+
345
+	/**
346
+	 * cancels an existing ticket line item,
347
+	 * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item.
348
+	 * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
349
+	 *
350
+	 * @param EE_Line_Item $ticket_line_item
351
+	 * @param int $qty
352
+	 * @return bool success
353
+	 * @throws \EE_Error
354
+	 */
355
+	public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
356
+	{
357
+		// validate incoming line_item
358
+		if ($ticket_line_item->OBJ_type() !== 'Ticket') {
359
+			throw new EE_Error(
360
+				sprintf(
361
+					__('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
362
+					$ticket_line_item->type()
363
+				)
364
+			);
365
+		}
366
+		if ($ticket_line_item->quantity() < $qty) {
367
+			throw new EE_Error(
368
+				sprintf(
369
+					__('Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.', 'event_espresso'),
370
+					$qty,
371
+					$ticket_line_item->quantity()
372
+				)
373
+			);
374
+		}
375
+		// decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this
376
+		$ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty);
377
+		foreach ($ticket_line_item->children() as $child_line_item) {
378
+			if (
379
+				$child_line_item->is_sub_line_item()
380
+				&& !$child_line_item->is_percent()
381
+				&& !$child_line_item->is_cancellation()
382
+			) {
383
+				$child_line_item->set_quantity($child_line_item->quantity() - $qty);
384
+			}
385
+		}
386
+		// get cancellation sub line item
387
+		$cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
388
+			$ticket_line_item,
389
+			EEM_Line_Item::type_cancellation
390
+		);
391
+		$cancellation_line_item = reset($cancellation_line_item);
392
+		// verify that this ticket was indeed previously cancelled
393
+		if ($cancellation_line_item instanceof EE_Line_Item) {
394
+			// increment cancelled quantity
395
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty);
396
+		} else {
397
+			// create cancellation sub line item
398
+			$cancellation_line_item = EE_Line_Item::new_instance(array(
399
+				'LIN_name' => __('Cancellation', 'event_espresso'),
400
+				'LIN_desc' => sprintf(
401
+					_x('Cancelled %1$s : %2$s', 'Cancelled Ticket Name : 2015-01-01 11:11', 'event_espresso'),
402
+					$ticket_line_item->name(),
403
+					current_time(get_option('date_format') . ' ' . get_option('time_format'))
404
+				),
405
+				'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
406
+				'LIN_quantity' => $qty,
407
+				'LIN_is_taxable' => $ticket_line_item->is_taxable(),
408
+				'LIN_order' => count($ticket_line_item->children()),
409
+				'LIN_total' => 0, // $ticket_line_item->unit_price()
410
+				'LIN_type' => EEM_Line_Item::type_cancellation,
411
+			));
412
+			$ticket_line_item->add_child_line_item($cancellation_line_item);
413
+		}
414
+		if ($ticket_line_item->save_this_and_descendants() > 0) {
415
+			// decrement parent line item quantity
416
+			$event_line_item = $ticket_line_item->parent();
417
+			if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
418
+				$event_line_item->set_quantity($event_line_item->quantity() - $qty);
419
+				$event_line_item->save();
420
+			}
421
+			EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
422
+			return true;
423
+		}
424
+		return false;
425
+	}
426
+
427
+
428
+	/**
429
+	 * reinstates (un-cancels?) a previously canceled ticket line item,
430
+	 * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item.
431
+	 * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
432
+	 *
433
+	 * @param EE_Line_Item $ticket_line_item
434
+	 * @param int $qty
435
+	 * @return bool success
436
+	 * @throws \EE_Error
437
+	 */
438
+	public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
439
+	{
440
+		// validate incoming line_item
441
+		if ($ticket_line_item->OBJ_type() !== 'Ticket') {
442
+			throw new EE_Error(
443
+				sprintf(
444
+					__('The supplied line item must have an Object Type of "Ticket", not %1$s.', 'event_espresso'),
445
+					$ticket_line_item->type()
446
+				)
447
+			);
448
+		}
449
+		// get cancellation sub line item
450
+		$cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
451
+			$ticket_line_item,
452
+			EEM_Line_Item::type_cancellation
453
+		);
454
+		$cancellation_line_item = reset($cancellation_line_item);
455
+		// verify that this ticket was indeed previously cancelled
456
+		if (!$cancellation_line_item instanceof EE_Line_Item) {
457
+			return false;
458
+		}
459
+		if ($cancellation_line_item->quantity() > $qty) {
460
+			// decrement cancelled quantity
461
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
462
+		} else if ($cancellation_line_item->quantity() == $qty) {
463
+			// decrement cancelled quantity in case anyone still has the object kicking around
464
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
465
+			// delete because quantity will end up as 0
466
+			$cancellation_line_item->delete();
467
+			// and attempt to destroy the object,
468
+			// even though PHP won't actually destroy it until it needs the memory
469
+			unset($cancellation_line_item);
470
+		} else {
471
+			// what ?!?! negative quantity ?!?!
472
+			throw new EE_Error(
473
+				sprintf(
474
+					__('Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.',
475
+						'event_espresso'),
476
+					$qty,
477
+					$cancellation_line_item->quantity()
478
+				)
479
+			);
480
+		}
481
+		// increment ticket quantity
482
+		$ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty);
483
+		if ($ticket_line_item->save_this_and_descendants() > 0) {
484
+			// increment parent line item quantity
485
+			$event_line_item = $ticket_line_item->parent();
486
+			if ($event_line_item instanceof EE_Line_Item && $event_line_item->OBJ_type() === 'Event') {
487
+				$event_line_item->set_quantity($event_line_item->quantity() + $qty);
488
+			}
489
+			EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
490
+			return true;
491
+		}
492
+		return false;
493
+	}
494
+
495
+
496
+	/**
497
+	 * calls EEH_Line_Item::find_transaction_grand_total_for_line_item()
498
+	 * then EE_Line_Item::recalculate_total_including_taxes() on the result
499
+	 *
500
+	 * @param EE_Line_Item $line_item
501
+	 * @return \EE_Line_Item
502
+	 */
503
+	public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item)
504
+	{
505
+		$grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item);
506
+		return $grand_total_line_item->recalculate_total_including_taxes();
507
+	}
508
+
509
+
510
+	/**
511
+	 * Gets the line item which contains the subtotal of all the items
512
+	 *
513
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
514
+	 * @return \EE_Line_Item
515
+	 * @throws \EE_Error
516
+	 */
517
+	public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item)
518
+	{
519
+		$pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal');
520
+		return $pre_tax_subtotal instanceof EE_Line_Item
521
+			? $pre_tax_subtotal
522
+			: self::create_pre_tax_subtotal($total_line_item);
523
+	}
524
+
525
+
526
+	/**
527
+	 * Gets the line item for the taxes subtotal
528
+	 *
529
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
530
+	 * @return \EE_Line_Item
531
+	 * @throws \EE_Error
532
+	 */
533
+	public static function get_taxes_subtotal(EE_Line_Item $total_line_item)
534
+	{
535
+		$taxes = $total_line_item->get_child_line_item('taxes');
536
+		return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item);
537
+	}
538
+
539
+
540
+	/**
541
+	 * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object
542
+	 *
543
+	 * @param EE_Line_Item $line_item
544
+	 * @param EE_Transaction $transaction
545
+	 * @return void
546
+	 * @throws \EE_Error
547
+	 */
548
+	public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = NULL)
549
+	{
550
+		if ($transaction) {
551
+			/** @type EEM_Transaction $EEM_Transaction */
552
+			$EEM_Transaction = EE_Registry::instance()->load_model('Transaction');
553
+			$TXN_ID = $EEM_Transaction->ensure_is_ID($transaction);
554
+			$line_item->set_TXN_ID($TXN_ID);
555
+		}
556
+	}
557
+
558
+
559
+	/**
560
+	 * Creates a new default total line item for the transaction,
561
+	 * and its tickets subtotal and taxes subtotal line items (and adds the
562
+	 * existing taxes as children of the taxes subtotal line item)
563
+	 *
564
+	 * @param EE_Transaction $transaction
565
+	 * @return \EE_Line_Item of type total
566
+	 * @throws \EE_Error
567
+	 */
568
+	public static function create_total_line_item($transaction = NULL)
569
+	{
570
+		$total_line_item = EE_Line_Item::new_instance(array(
571
+			'LIN_code' => 'total',
572
+			'LIN_name' => __('Grand Total', 'event_espresso'),
573
+			'LIN_type' => EEM_Line_Item::type_total,
574
+			'OBJ_type' => 'Transaction'
575
+		));
576
+		$total_line_item = apply_filters(
577
+			'FHEE__EEH_Line_Item__create_total_line_item__total_line_item',
578
+			$total_line_item
579
+		);
580
+		self::set_TXN_ID($total_line_item, $transaction);
581
+		self::create_pre_tax_subtotal($total_line_item, $transaction);
582
+		self::create_taxes_subtotal($total_line_item, $transaction);
583
+		return $total_line_item;
584
+	}
585
+
586
+
587
+	/**
588
+	 * Creates a default items subtotal line item
589
+	 *
590
+	 * @param EE_Line_Item $total_line_item
591
+	 * @param EE_Transaction $transaction
592
+	 * @return EE_Line_Item
593
+	 * @throws \EE_Error
594
+	 */
595
+	protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
596
+	{
597
+		$pre_tax_line_item = EE_Line_Item::new_instance(array(
598
+			'LIN_code' => 'pre-tax-subtotal',
599
+			'LIN_name' => __('Pre-Tax Subtotal', 'event_espresso'),
600
+			'LIN_type' => EEM_Line_Item::type_sub_total
601
+		));
602
+		$pre_tax_line_item = apply_filters(
603
+			'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item',
604
+			$pre_tax_line_item
605
+		);
606
+		self::set_TXN_ID($pre_tax_line_item, $transaction);
607
+		$total_line_item->add_child_line_item($pre_tax_line_item);
608
+		self::create_event_subtotal($pre_tax_line_item, $transaction);
609
+		return $pre_tax_line_item;
610
+	}
611
+
612
+
613
+	/**
614
+	 * Creates a line item for the taxes subtotal and finds all the tax prices
615
+	 * and applies taxes to it
616
+	 *
617
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
618
+	 * @param EE_Transaction $transaction
619
+	 * @return EE_Line_Item
620
+	 * @throws \EE_Error
621
+	 */
622
+	protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
623
+	{
624
+		$tax_line_item = EE_Line_Item::new_instance(array(
625
+			'LIN_code' => 'taxes',
626
+			'LIN_name' => __('Taxes', 'event_espresso'),
627
+			'LIN_type' => EEM_Line_Item::type_tax_sub_total,
628
+			'LIN_order' => 1000,//this should always come last
629
+		));
630
+		$tax_line_item = apply_filters(
631
+			'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
632
+			$tax_line_item
633
+		);
634
+		self::set_TXN_ID($tax_line_item, $transaction);
635
+		$total_line_item->add_child_line_item($tax_line_item);
636
+		//and lastly, add the actual taxes
637
+		self::apply_taxes($total_line_item);
638
+		return $tax_line_item;
639
+	}
640
+
641
+
642
+	/**
643
+	 * Creates a default items subtotal line item
644
+	 *
645
+	 * @param EE_Line_Item $pre_tax_line_item
646
+	 * @param EE_Transaction $transaction
647
+	 * @param EE_Event $event
648
+	 * @return EE_Line_Item
649
+	 * @throws \EE_Error
650
+	 */
651
+	public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = NULL, $event = NULL)
652
+	{
653
+		$event_line_item = EE_Line_Item::new_instance(array(
654
+			'LIN_code' => self::get_event_code($event),
655
+			'LIN_name' => self::get_event_name($event),
656
+			'LIN_desc' => self::get_event_desc($event),
657
+			'LIN_type' => EEM_Line_Item::type_sub_total,
658
+			'OBJ_type' => 'Event',
659
+			'OBJ_ID' => $event instanceof EE_Event ? $event->ID() : 0
660
+		));
661
+		$event_line_item = apply_filters(
662
+			'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item',
663
+			$event_line_item
664
+		);
665
+		self::set_TXN_ID($event_line_item, $transaction);
666
+		$pre_tax_line_item->add_child_line_item($event_line_item);
667
+		return $event_line_item;
668
+	}
669
+
670
+
671
+	/**
672
+	 * Gets what the event ticket's code SHOULD be
673
+	 *
674
+	 * @param EE_Event $event
675
+	 * @return string
676
+	 * @throws \EE_Error
677
+	 */
678
+	public static function get_event_code($event)
679
+	{
680
+		return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
681
+	}
682
+
683
+	/**
684
+	 * Gets the event name
685
+	 * @param EE_Event $event
686
+	 * @return string
687
+	 */
688
+	public static function get_event_name($event)
689
+	{
690
+		return $event instanceof EE_Event ? $event->name() : __('Event', 'event_espresso');
691
+	}
692
+
693
+	/**
694
+	 * Gets the event excerpt
695
+	 * @param EE_Event $event
696
+	 * @return string
697
+	 */
698
+	public static function get_event_desc($event)
699
+	{
700
+		return $event instanceof EE_Event ? $event->short_description() : '';
701
+	}
702
+
703
+	/**
704
+	 * Given the grand total line item and a ticket, finds the event sub-total
705
+	 * line item the ticket's purchase should be added onto
706
+	 *
707
+	 * @access public
708
+	 * @param EE_Line_Item $grand_total the grand total line item
709
+	 * @param EE_Ticket $ticket
710
+	 * @throws \EE_Error
711
+	 * @return EE_Line_Item
712
+	 */
713
+	public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
714
+	{
715
+		$first_datetime = $ticket->first_datetime();
716
+		if (!$first_datetime instanceof EE_Datetime) {
717
+			throw new EE_Error(
718
+				sprintf(__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), $ticket->ID())
719
+			);
720
+		}
721
+		$event = $first_datetime->event();
722
+		if (!$event instanceof EE_Event) {
723
+			throw new EE_Error(
724
+				sprintf(
725
+					__('The supplied ticket (ID %d) has no event data associated with it.', 'event_espresso'),
726
+					$ticket->ID()
727
+				)
728
+			);
729
+		}
730
+		$events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
731
+		if (!$events_sub_total instanceof EE_Line_Item) {
732
+			throw new EE_Error(
733
+				sprintf(
734
+					__('There is no events sub-total for ticket %s on total line item %d', 'event_espresso'),
735
+					$ticket->ID(),
736
+					$grand_total->ID()
737
+				)
738
+			);
739
+		}
740
+		return $events_sub_total;
741
+	}
742
+
743
+
744
+	/**
745
+	 * Gets the event line item
746
+	 *
747
+	 * @param EE_Line_Item $grand_total
748
+	 * @param EE_Event $event
749
+	 * @return EE_Line_Item for the event subtotal which is a child of $grand_total
750
+	 * @throws \EE_Error
751
+	 */
752
+	public static function get_event_line_item(EE_Line_Item $grand_total, $event)
753
+	{
754
+		/** @type EE_Event $event */
755
+		$event = EEM_Event::instance()->ensure_is_obj($event, true);
756
+		$event_line_item = NULL;
757
+		$found = false;
758
+		foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
759
+			// default event subtotal, we should only ever find this the first time this method is called
760
+			if (!$event_line_item->OBJ_ID()) {
761
+				// let's use this! but first... set the event details
762
+				EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
763
+				$found = true;
764
+				break;
765
+			} else if ($event_line_item->OBJ_ID() === $event->ID()) {
766
+				// found existing line item for this event in the cart, so break out of loop and use this one
767
+				$found = true;
768
+				break;
769
+			}
770
+		}
771
+		if (!$found) {
772
+			//there is no event sub-total yet, so add it
773
+			$pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
774
+			// create a new "event" subtotal below that
775
+			$event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event);
776
+			// and set the event details
777
+			EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
778
+		}
779
+		return $event_line_item;
780
+	}
781
+
782
+
783
+	/**
784
+	 * Creates a default items subtotal line item
785
+	 *
786
+	 * @param EE_Line_Item $event_line_item
787
+	 * @param EE_Event $event
788
+	 * @param EE_Transaction $transaction
789
+	 * @return EE_Line_Item
790
+	 * @throws \EE_Error
791
+	 */
792
+	public static function set_event_subtotal_details(
793
+		EE_Line_Item $event_line_item,
794
+		EE_Event $event,
795
+		$transaction = null
796
+	)
797
+	{
798
+		if ($event instanceof EE_Event) {
799
+			$event_line_item->set_code(self::get_event_code($event));
800
+			$event_line_item->set_name(self::get_event_name($event));
801
+			$event_line_item->set_desc(self::get_event_desc($event));
802
+			$event_line_item->set_OBJ_ID($event->ID());
803
+		}
804
+		self::set_TXN_ID($event_line_item, $transaction);
805
+	}
806
+
807
+
808
+	/**
809
+	 * Finds what taxes should apply, adds them as tax line items under the taxes sub-total,
810
+	 * and recalculates the taxes sub-total and the grand total. Resets the taxes, so
811
+	 * any old taxes are removed
812
+	 *
813
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
814
+	 * @throws \EE_Error
815
+	 */
816
+	public static function apply_taxes(EE_Line_Item $total_line_item)
817
+	{
818
+		/** @type EEM_Price $EEM_Price */
819
+		$EEM_Price = EE_Registry::instance()->load_model('Price');
820
+		// get array of taxes via Price Model
821
+		$ordered_taxes = $EEM_Price->get_all_prices_that_are_taxes();
822
+		ksort($ordered_taxes);
823
+		$taxes_line_item = self::get_taxes_subtotal($total_line_item);
824
+		//just to be safe, remove its old tax line items
825
+		$taxes_line_item->delete_children_line_items();
826
+		//loop thru taxes
827
+		foreach ($ordered_taxes as $order => $taxes) {
828
+			foreach ($taxes as $tax) {
829
+				if ($tax instanceof EE_Price) {
830
+					$tax_line_item = EE_Line_Item::new_instance(
831
+						array(
832
+							'LIN_name' => $tax->name(),
833
+							'LIN_desc' => $tax->desc(),
834
+							'LIN_percent' => $tax->amount(),
835
+							'LIN_is_taxable' => false,
836
+							'LIN_order' => $order,
837
+							'LIN_total' => 0,
838
+							'LIN_type' => EEM_Line_Item::type_tax,
839
+							'OBJ_type' => 'Price',
840
+							'OBJ_ID' => $tax->ID()
841
+						)
842
+					);
843
+					$tax_line_item = apply_filters(
844
+						'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
845
+						$tax_line_item
846
+					);
847
+					$taxes_line_item->add_child_line_item($tax_line_item);
848
+				}
849
+			}
850
+		}
851
+		$total_line_item->recalculate_total_including_taxes();
852
+	}
853
+
854
+
855
+	/**
856
+	 * Ensures that taxes have been applied to the order, if not applies them.
857
+	 * Returns the total amount of tax
858
+	 *
859
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
860
+	 * @return float
861
+	 * @throws \EE_Error
862
+	 */
863
+	public static function ensure_taxes_applied($total_line_item)
864
+	{
865
+		$taxes_subtotal = self::get_taxes_subtotal($total_line_item);
866
+		if (!$taxes_subtotal->children()) {
867
+			self::apply_taxes($total_line_item);
868
+		}
869
+		return $taxes_subtotal->total();
870
+	}
871
+
872
+
873
+	/**
874
+	 * Deletes ALL children of the passed line item
875
+	 *
876
+	 * @param EE_Line_Item $parent_line_item
877
+	 * @return bool
878
+	 * @throws \EE_Error
879
+	 */
880
+	public static function delete_all_child_items(EE_Line_Item $parent_line_item)
881
+	{
882
+		$deleted = 0;
883
+		foreach ($parent_line_item->children() as $child_line_item) {
884
+			if ($child_line_item instanceof EE_Line_Item) {
885
+				$deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
886
+				if ($child_line_item->ID()) {
887
+					$child_line_item->delete();
888
+					unset($child_line_item);
889
+				} else {
890
+					$parent_line_item->delete_child_line_item($child_line_item->code());
891
+				}
892
+				$deleted++;
893
+			}
894
+		}
895
+		return $deleted;
896
+	}
897
+
898
+
899
+	/**
900
+	 * Deletes the line items as indicated by the line item code(s) provided,
901
+	 * regardless of where they're found in the line item tree. Automatically
902
+	 * re-calculates the line item totals and updates the related transaction. But
903
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
904
+	 * should probably change because of this).
905
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
906
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
907
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
908
+	 * @param array|bool|string $line_item_codes
909
+	 * @return int number of items successfully removed
910
+	 */
911
+	public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = FALSE)
912
+	{
913
+
914
+		if ($total_line_item->type() !== EEM_Line_Item::type_total) {
915
+			EE_Error::doing_it_wrong(
916
+				'EEH_Line_Item::delete_items',
917
+				__(
918
+					'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly',
919
+					'event_espresso'
920
+				),
921
+				'4.6.18'
922
+			);
923
+		}
924
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
925
+
926
+		// check if only a single line_item_id was passed
927
+		if (!empty($line_item_codes) && !is_array($line_item_codes)) {
928
+			// place single line_item_id in an array to appear as multiple line_item_ids
929
+			$line_item_codes = array($line_item_codes);
930
+		}
931
+		$removals = 0;
932
+		// cycle thru line_item_ids
933
+		foreach ($line_item_codes as $line_item_id) {
934
+			$removals += $total_line_item->delete_child_line_item($line_item_id);
935
+		}
936
+
937
+		if ($removals > 0) {
938
+			$total_line_item->recalculate_taxes_and_tax_total();
939
+			return $removals;
940
+		} else {
941
+			return FALSE;
942
+		}
943
+	}
944
+
945
+
946
+	/**
947
+	 * Overwrites the previous tax by clearing out the old taxes, and creates a new
948
+	 * tax and updates the total line item accordingly
949
+	 *
950
+	 * @param EE_Line_Item $total_line_item
951
+	 * @param float $amount
952
+	 * @param string $name
953
+	 * @param string $description
954
+	 * @param string $code
955
+	 * @param boolean $add_to_existing_line_item
956
+	 *                          if true, and a duplicate line item with the same code is found,
957
+	 *                          $amount will be added onto it; otherwise will simply set the taxes to match $amount
958
+	 * @return EE_Line_Item the new tax line item created
959
+	 * @throws \EE_Error
960
+	 */
961
+	public static function set_total_tax_to(
962
+		EE_Line_Item $total_line_item,
963
+		$amount,
964
+		$name = null,
965
+		$description = null,
966
+		$code = null,
967
+		$add_to_existing_line_item = false
968
+	)
969
+	{
970
+		$tax_subtotal = self::get_taxes_subtotal($total_line_item);
971
+		$taxable_total = $total_line_item->taxable_total();
972
+
973
+		if ($add_to_existing_line_item) {
974
+			$new_tax = $tax_subtotal->get_child_line_item($code);
975
+			EEM_Line_Item::instance()->delete(
976
+				array(array('LIN_code' => array('!=', $code), 'LIN_parent' => $tax_subtotal->ID()))
977
+			);
978
+		} else {
979
+			$new_tax = null;
980
+			$tax_subtotal->delete_children_line_items();
981
+		}
982
+		if ($new_tax) {
983
+			$new_tax->set_total($new_tax->total() + $amount);
984
+			$new_tax->set_percent($taxable_total ? $new_tax->total() / $taxable_total * 100 : 0);
985
+		} else {
986
+			//no existing tax item. Create it
987
+			$new_tax = EE_Line_Item::new_instance(array(
988
+				'TXN_ID' => $total_line_item->TXN_ID(),
989
+				'LIN_name' => $name ? $name : __('Tax', 'event_espresso'),
990
+				'LIN_desc' => $description ? $description : '',
991
+				'LIN_percent' => $taxable_total ? ($amount / $taxable_total * 100) : 0,
992
+				'LIN_total' => $amount,
993
+				'LIN_parent' => $tax_subtotal->ID(),
994
+				'LIN_type' => EEM_Line_Item::type_tax,
995
+				'LIN_code' => $code
996
+			));
997
+		}
998
+
999
+		$new_tax = apply_filters(
1000
+			'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal',
1001
+			$new_tax,
1002
+			$total_line_item
1003
+		);
1004
+		$new_tax->save();
1005
+		$tax_subtotal->set_total($new_tax->total());
1006
+		$tax_subtotal->save();
1007
+		$total_line_item->recalculate_total_including_taxes();
1008
+		return $new_tax;
1009
+	}
1010
+
1011
+
1012
+	/**
1013
+	 * Makes all the line items which are children of $line_item taxable (or not).
1014
+	 * Does NOT save the line items
1015
+	 * @param EE_Line_Item $line_item
1016
+	 * @param string $code_substring_for_whitelist if this string is part of the line item's code
1017
+	 *  it will be whitelisted (ie, except from becoming taxable)
1018
+	 * @param boolean $taxable
1019
+	 */
1020
+	public static function set_line_items_taxable(
1021
+		EE_Line_Item $line_item,
1022
+		$taxable = true,
1023
+		$code_substring_for_whitelist = null
1024
+	)
1025
+	{
1026
+		$whitelisted = false;
1027
+		if ($code_substring_for_whitelist !== null) {
1028
+			$whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false ? true : false;
1029
+		}
1030
+		if (!$whitelisted && $line_item->is_line_item()) {
1031
+			$line_item->set_is_taxable($taxable);
1032
+		}
1033
+		foreach ($line_item->children() as $child_line_item) {
1034
+			EEH_Line_Item::set_line_items_taxable($child_line_item, $taxable, $code_substring_for_whitelist);
1035
+		}
1036
+	}
1037
+
1038
+
1039
+	/**
1040
+	 * Gets all descendants that are event subtotals
1041
+	 *
1042
+	 * @uses  EEH_Line_Item::get_subtotals_of_object_type()
1043
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1044
+	 * @return EE_Line_Item[]
1045
+	 */
1046
+	public static function get_event_subtotals(EE_Line_Item $parent_line_item)
1047
+	{
1048
+		return self::get_subtotals_of_object_type($parent_line_item, 'Event');
1049
+	}
1050
+
1051
+
1052
+	/**
1053
+	 * Gets all descendants subtotals that match the supplied object type
1054
+	 *
1055
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1056
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1057
+	 * @param string $obj_type
1058
+	 * @return EE_Line_Item[]
1059
+	 */
1060
+	public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1061
+	{
1062
+		return self::_get_descendants_by_type_and_object_type(
1063
+			$parent_line_item,
1064
+			EEM_Line_Item::type_sub_total,
1065
+			$obj_type
1066
+		);
1067
+	}
1068
+
1069
+
1070
+	/**
1071
+	 * Gets all descendants that are tickets
1072
+	 *
1073
+	 * @uses  EEH_Line_Item::get_line_items_of_object_type()
1074
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1075
+	 * @return EE_Line_Item[]
1076
+	 */
1077
+	public static function get_ticket_line_items(EE_Line_Item $parent_line_item)
1078
+	{
1079
+		return self::get_line_items_of_object_type($parent_line_item, 'Ticket');
1080
+	}
1081
+
1082
+
1083
+	/**
1084
+	 * Gets all descendants subtotals that match the supplied object type
1085
+	 *
1086
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1087
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1088
+	 * @param string $obj_type
1089
+	 * @return EE_Line_Item[]
1090
+	 */
1091
+	public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1092
+	{
1093
+		return self::_get_descendants_by_type_and_object_type($parent_line_item, EEM_Line_Item::type_line_item, $obj_type);
1094
+	}
1095
+
1096
+
1097
+	/**
1098
+	 * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax'
1099
+	 * @uses  EEH_Line_Item::get_descendants_of_type()
1100
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1101
+	 * @return EE_Line_Item[]
1102
+	 */
1103
+	public static function get_tax_descendants(EE_Line_Item $parent_line_item)
1104
+	{
1105
+		return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_tax);
1106
+	}
1107
+
1108
+
1109
+	/**
1110
+	 * Gets all the real items purchased which are children of this item
1111
+	 * @uses  EEH_Line_Item::get_descendants_of_type()
1112
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1113
+	 * @return EE_Line_Item[]
1114
+	 */
1115
+	public static function get_line_item_descendants(EE_Line_Item $parent_line_item)
1116
+	{
1117
+		return EEH_Line_Item::get_descendants_of_type($parent_line_item, EEM_Line_Item::type_line_item);
1118
+	}
1119
+
1120
+
1121
+	/**
1122
+	 * Gets all descendants of supplied line item that match the supplied line item type
1123
+	 *
1124
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1125
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1126
+	 * @param string $line_item_type one of the EEM_Line_Item constants
1127
+	 * @return EE_Line_Item[]
1128
+	 */
1129
+	public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type)
1130
+	{
1131
+		return self::_get_descendants_by_type_and_object_type($parent_line_item, $line_item_type, NULL);
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1137
+	 *
1138
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1139
+	 * @param string $line_item_type one of the EEM_Line_Item constants
1140
+	 * @param string | NULL $obj_type object model class name (minus prefix) or NULL to ignore object type when searching
1141
+	 * @return EE_Line_Item[]
1142
+	 */
1143
+	protected static function _get_descendants_by_type_and_object_type(
1144
+		EE_Line_Item $parent_line_item,
1145
+		$line_item_type,
1146
+		$obj_type = null
1147
+	)
1148
+	{
1149
+		$objects = array();
1150
+		foreach ($parent_line_item->children() as $child_line_item) {
1151
+			if ($child_line_item instanceof EE_Line_Item) {
1152
+				if (
1153
+					$child_line_item->type() === $line_item_type
1154
+					&& (
1155
+						$child_line_item->OBJ_type() === $obj_type || $obj_type === null
1156
+					)
1157
+				) {
1158
+					$objects[] = $child_line_item;
1159
+				} else {
1160
+					//go-through-all-its children looking for more matches
1161
+					$objects = array_merge(
1162
+						$objects,
1163
+						self::_get_descendants_by_type_and_object_type(
1164
+							$child_line_item,
1165
+							$line_item_type,
1166
+							$obj_type
1167
+						)
1168
+					);
1169
+				}
1170
+			}
1171
+		}
1172
+		return $objects;
1173
+	}
1174
+
1175
+
1176
+	/**
1177
+	 * Gets all descendants subtotals that match the supplied object type
1178
+	 *
1179
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1180
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1181
+	 * @param string $OBJ_type object type (like Event)
1182
+	 * @param array $OBJ_IDs array of OBJ_IDs
1183
+	 * @return EE_Line_Item[]
1184
+	 */
1185
+	public static function get_line_items_by_object_type_and_IDs(
1186
+		EE_Line_Item $parent_line_item,
1187
+		$OBJ_type = '',
1188
+		$OBJ_IDs = array()
1189
+	)
1190
+	{
1191
+		return self::_get_descendants_by_object_type_and_object_ID($parent_line_item, $OBJ_type, $OBJ_IDs);
1192
+	}
1193
+
1194
+
1195
+	/**
1196
+	 * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type as well
1197
+	 *
1198
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1199
+	 * @param string $OBJ_type object type (like Event)
1200
+	 * @param array $OBJ_IDs array of OBJ_IDs
1201
+	 * @return EE_Line_Item[]
1202
+	 */
1203
+	protected static function _get_descendants_by_object_type_and_object_ID(
1204
+		EE_Line_Item $parent_line_item,
1205
+		$OBJ_type,
1206
+		$OBJ_IDs
1207
+	)
1208
+	{
1209
+		$objects = array();
1210
+		foreach ($parent_line_item->children() as $child_line_item) {
1211
+			if ($child_line_item instanceof EE_Line_Item) {
1212
+				if (
1213
+					$child_line_item->OBJ_type() === $OBJ_type
1214
+					&& is_array($OBJ_IDs)
1215
+					&& in_array($child_line_item->OBJ_ID(), $OBJ_IDs)
1216
+				) {
1217
+					$objects[] = $child_line_item;
1218
+				} else {
1219
+					//go-through-all-its children looking for more matches
1220
+					$objects = array_merge(
1221
+						$objects,
1222
+						self::_get_descendants_by_object_type_and_object_ID(
1223
+							$child_line_item,
1224
+							$OBJ_type,
1225
+							$OBJ_IDs
1226
+						)
1227
+					);
1228
+				}
1229
+			}
1230
+		}
1231
+		return $objects;
1232
+	}
1233
+
1234
+
1235
+	/**
1236
+	 * Uses a breadth-first-search in order to find the nearest descendant of
1237
+	 * the specified type and returns it, else NULL
1238
+	 *
1239
+	 * @uses  EEH_Line_Item::_get_nearest_descendant()
1240
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1241
+	 * @param string $type like one of the EEM_Line_Item::type_*
1242
+	 * @return EE_Line_Item
1243
+	 */
1244
+	public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type)
1245
+	{
1246
+		return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type);
1247
+	}
1248
+
1249
+
1250
+	/**
1251
+	 * Uses a breadth-first-search in order to find the nearest descendant
1252
+	 * having the specified LIN_code and returns it, else NULL
1253
+	 *
1254
+	 * @uses  EEH_Line_Item::_get_nearest_descendant()
1255
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1256
+	 * @param string $code any value used for LIN_code
1257
+	 * @return EE_Line_Item
1258
+	 */
1259
+	public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code)
1260
+	{
1261
+		return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code);
1262
+	}
1263
+
1264
+
1265
+	/**
1266
+	 * Uses a breadth-first-search in order to find the nearest descendant
1267
+	 * having the specified LIN_code and returns it, else NULL
1268
+	 *
1269
+	 * @param \EE_Line_Item $parent_line_item - the line item to find descendants of
1270
+	 * @param string $search_field name of EE_Line_Item property
1271
+	 * @param string $value any value stored in $search_field
1272
+	 * @return EE_Line_Item
1273
+	 */
1274
+	protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value)
1275
+	{
1276
+		foreach ($parent_line_item->children() as $child) {
1277
+			if ($child->get($search_field) == $value) {
1278
+				return $child;
1279
+			}
1280
+		}
1281
+		foreach ($parent_line_item->children() as $child) {
1282
+			$descendant_found = self::_get_nearest_descendant($child, $search_field, $value);
1283
+			if ($descendant_found) {
1284
+				return $descendant_found;
1285
+			}
1286
+		}
1287
+		return NULL;
1288
+	}
1289
+
1290
+
1291
+	/**
1292
+	 * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction,
1293
+	 * else recursively walks up the line item tree until a parent of type total is found,
1294
+	 *
1295
+	 * @param EE_Line_Item $line_item
1296
+	 * @return \EE_Line_Item
1297
+	 * @throws \EE_Error
1298
+	 */
1299
+	public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item)
1300
+	{
1301
+		if ($line_item->TXN_ID()) {
1302
+			$total_line_item = $line_item->transaction()->total_line_item(false);
1303
+			if ($total_line_item instanceof EE_Line_Item) {
1304
+				return $total_line_item;
1305
+			}
1306
+		} else {
1307
+			$line_item_parent = $line_item->parent();
1308
+			if ($line_item_parent instanceof EE_Line_Item) {
1309
+				if ($line_item_parent->is_total()) {
1310
+					return $line_item_parent;
1311
+				}
1312
+				return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent);
1313
+			}
1314
+		}
1315
+		throw new EE_Error(
1316
+			sprintf(
1317
+				__('A valid grand total for line item %1$d was not found.', 'event_espresso'),
1318
+				$line_item->ID()
1319
+			)
1320
+		);
1321
+	}
1322
+
1323
+
1324
+	/**
1325
+	 * Prints out a representation of the line item tree
1326
+	 *
1327
+	 * @param EE_Line_Item $line_item
1328
+	 * @param int $indentation
1329
+	 * @return void
1330
+	 * @throws \EE_Error
1331
+	 */
1332
+	public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1333
+	{
1334
+		echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1335
+		if (!$indentation) {
1336
+			echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1337
+		}
1338
+		for ($i = 0; $i < $indentation; $i++) {
1339
+			echo ". ";
1340
+		}
1341
+		$breakdown = '';
1342
+		if ($line_item->is_line_item()) {
1343
+			if ($line_item->is_percent()) {
1344
+				$breakdown = "{$line_item->percent()}%";
1345
+			} else {
1346
+				$breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1347
+			}
1348
+		}
1349
+		echo $line_item->name() . " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : " . '$' . "{$line_item->total()}";
1350
+		if ($breakdown) {
1351
+			echo " ( {$breakdown} )";
1352
+		}
1353
+		if ($line_item->is_taxable()) {
1354
+			echo "  * taxable";
1355
+		}
1356
+		if ($line_item->children()) {
1357
+			foreach ($line_item->children() as $child) {
1358
+				self::visualize($child, $indentation + 1);
1359
+			}
1360
+		}
1361
+	}
1362
+
1363
+
1364
+	/**
1365
+	 * Calculates the registration's final price, taking into account that they
1366
+	 * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes,
1367
+	 * and receive a portion of any transaction-wide discounts.
1368
+	 * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount
1369
+	 * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get
1370
+	 * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50
1371
+	 * and brent's final price should be $5.50.
1372
+	 *
1373
+	 * In order to do this, we basically need to traverse the line item tree calculating
1374
+	 * the running totals (just as if we were recalculating the total), but when we identify
1375
+	 * regular line items, we need to keep track of their share of the grand total.
1376
+	 * Also, we need to keep track of the TAXABLE total for each ticket purchase, so
1377
+	 * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total"
1378
+	 * when there are non-taxable items; otherwise they would be the same)
1379
+	 *
1380
+	 * @param EE_Line_Item $line_item
1381
+	 * @param array $billable_ticket_quantities array of EE_Ticket IDs and their corresponding quantity that
1382
+	 *                                                                            can be included in price calculations at this moment
1383
+	 * @return array        keys are line items for tickets IDs and values are their share of the running total,
1384
+	 *                                          plus the key 'total', and 'taxable' which also has keys of all the ticket IDs. Eg
1385
+	 *                                          array(
1386
+	 *                                          12 => 4.3
1387
+	 *                                          23 => 8.0
1388
+	 *                                          'total' => 16.6,
1389
+	 *                                          'taxable' => array(
1390
+	 *                                          12 => 10,
1391
+	 *                                          23 => 4
1392
+	 *                                          ).
1393
+	 *                                          So to find which registrations have which final price, we need to find which line item
1394
+	 *                                          is theirs, which can be done with
1395
+	 *                                          `EEM_Line_Item::instance()->get_line_item_for_registration( $registration );`
1396
+	 */
1397
+	public static function calculate_reg_final_prices_per_line_item(EE_Line_Item $line_item, $billable_ticket_quantities = array())
1398
+	{
1399
+		//init running grand total if not already
1400
+		if (!isset($running_totals['total'])) {
1401
+			$running_totals['total'] = 0;
1402
+		}
1403
+		if (!isset($running_totals['taxable'])) {
1404
+			$running_totals['taxable'] = array('total' => 0);
1405
+		}
1406
+		foreach ($line_item->children() as $child_line_item) {
1407
+			switch ($child_line_item->type()) {
1408
+
1409
+				case EEM_Line_Item::type_sub_total :
1410
+					$running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item($child_line_item, $billable_ticket_quantities);
1411
+					//combine arrays but preserve numeric keys
1412
+					$running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals);
1413
+					$running_totals['total'] += $running_totals_from_subtotal['total'];
1414
+					$running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total'];
1415
+					break;
1416
+
1417
+				case EEM_Line_Item::type_tax_sub_total :
1418
+
1419
+					//find how much the taxes percentage is
1420
+					if ($child_line_item->percent() !== 0) {
1421
+						$tax_percent_decimal = $child_line_item->percent() / 100;
1422
+					} else {
1423
+						$tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100;
1424
+					}
1425
+					//and apply to all the taxable totals, and add to the pretax totals
1426
+					foreach ($running_totals as $line_item_id => $this_running_total) {
1427
+						//"total" and "taxable" array key is an exception
1428
+						if ($line_item_id === 'taxable') {
1429
+							continue;
1430
+						}
1431
+						$taxable_total = $running_totals['taxable'][$line_item_id];
1432
+						$running_totals[$line_item_id] += ($taxable_total * $tax_percent_decimal);
1433
+					}
1434
+					break;
1435
+
1436
+				case EEM_Line_Item::type_line_item :
1437
+
1438
+					// ticket line items or ????
1439
+					if ($child_line_item->OBJ_type() === 'Ticket') {
1440
+						// kk it's a ticket
1441
+						if (isset($running_totals[$child_line_item->ID()])) {
1442
+							//huh? that shouldn't happen.
1443
+							$running_totals['total'] += $child_line_item->total();
1444
+						} else {
1445
+							//its not in our running totals yet. great.
1446
+							if ($child_line_item->is_taxable()) {
1447
+								$taxable_amount = $child_line_item->unit_price();
1448
+							} else {
1449
+								$taxable_amount = 0;
1450
+							}
1451
+							// are we only calculating totals for some tickets?
1452
+							if (isset($billable_ticket_quantities[$child_line_item->OBJ_ID()])) {
1453
+								$quantity = $billable_ticket_quantities[$child_line_item->OBJ_ID()];
1454
+								$running_totals[$child_line_item->ID()] = $quantity
1455
+									? $child_line_item->unit_price()
1456
+									: 0;
1457
+								$running_totals['taxable'][$child_line_item->ID()] = $quantity
1458
+									? $taxable_amount
1459
+									: 0;
1460
+							} else {
1461
+								$quantity = $child_line_item->quantity();
1462
+								$running_totals[$child_line_item->ID()] = $child_line_item->unit_price();
1463
+								$running_totals['taxable'][$child_line_item->ID()] = $taxable_amount;
1464
+							}
1465
+							$running_totals['taxable']['total'] += $taxable_amount * $quantity;
1466
+							$running_totals['total'] += $child_line_item->unit_price() * $quantity;
1467
+						}
1468
+					} else {
1469
+						// it's some other type of item added to the cart
1470
+						// it should affect the running totals
1471
+						// basically we want to convert it into a PERCENT modifier. Because
1472
+						// more clearly affect all registration's final price equally
1473
+						$line_items_percent_of_running_total = $running_totals['total'] > 0
1474
+							? ($child_line_item->total() / $running_totals['total']) + 1
1475
+							: 1;
1476
+						foreach ($running_totals as $line_item_id => $this_running_total) {
1477
+							//the "taxable" array key is an exception
1478
+							if ($line_item_id === 'taxable') {
1479
+								continue;
1480
+							}
1481
+							// update the running totals
1482
+							// yes this actually even works for the running grand total!
1483
+							$running_totals[$line_item_id] =
1484
+								$line_items_percent_of_running_total * $this_running_total;
1485
+
1486
+							if ($child_line_item->is_taxable()) {
1487
+								$running_totals['taxable'][$line_item_id] =
1488
+									$line_items_percent_of_running_total * $running_totals['taxable'][$line_item_id];
1489
+							}
1490
+						}
1491
+					}
1492
+					break;
1493
+			}
1494
+		}
1495
+		return $running_totals;
1496
+	}
1497
+
1498
+
1499
+	/**
1500
+	 * @param \EE_Line_Item $total_line_item
1501
+	 * @param \EE_Line_Item $ticket_line_item
1502
+	 * @return float | null
1503
+	 * @throws \OutOfRangeException
1504
+	 */
1505
+	public static function calculate_final_price_for_ticket_line_item(\EE_Line_Item $total_line_item, \EE_Line_Item $ticket_line_item)
1506
+	{
1507
+		static $final_prices_per_ticket_line_item = array();
1508
+		if (empty($final_prices_per_ticket_line_item)) {
1509
+			$final_prices_per_ticket_line_item = \EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1510
+				$total_line_item
1511
+			);
1512
+		}
1513
+		//ok now find this new registration's final price
1514
+		if (isset($final_prices_per_ticket_line_item[$ticket_line_item->ID()])) {
1515
+			return $final_prices_per_ticket_line_item[$ticket_line_item->ID()];
1516
+		}
1517
+		$message = sprintf(
1518
+			__(
1519
+				'The final price for the ticket line item (ID:%1$d) could not be calculated.',
1520
+				'event_espresso'
1521
+			),
1522
+			$ticket_line_item->ID()
1523
+		);
1524
+		if (WP_DEBUG) {
1525
+			$message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1526
+			throw new \OutOfRangeException($message);
1527
+		} else {
1528
+			EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
1529
+		}
1530
+		return null;
1531
+	}
1532
+
1533
+
1534
+	/**
1535
+	 * Creates a duplicate of the line item tree, except only includes billable items
1536
+	 * and the portion of line items attributed to billable things
1537
+	 *
1538
+	 * @param EE_Line_Item $line_item
1539
+	 * @param EE_Registration[] $registrations
1540
+	 * @return \EE_Line_Item
1541
+	 * @throws \EE_Error
1542
+	 */
1543
+	public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations)
1544
+	{
1545
+		$copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations);
1546
+		foreach ($line_item->children() as $child_li) {
1547
+			$copy_li->add_child_line_item(EEH_Line_Item::billable_line_item_tree($child_li, $registrations));
1548
+		}
1549
+		//if this is the grand total line item, make sure the totals all add up
1550
+		//(we could have duplicated this logic AS we copied the line items, but
1551
+		//it seems DRYer this way)
1552
+		if ($copy_li->type() === EEM_Line_Item::type_total) {
1553
+			$copy_li->recalculate_total_including_taxes();
1554
+		}
1555
+		return $copy_li;
1556
+	}
1557
+
1558
+
1559
+	/**
1560
+	 * Creates a new, unsaved line item from $line_item that factors in the
1561
+	 * number of billable registrations on $registrations.
1562
+	 *
1563
+	 * @param EE_Line_Item $line_item
1564
+	 * @return EE_Line_Item
1565
+	 * @throws \EE_Error
1566
+	 * @param EE_Registration[] $registrations
1567
+	 */
1568
+	public static function billable_line_item(EE_Line_Item $line_item, $registrations)
1569
+	{
1570
+		$new_li_fields = $line_item->model_field_array();
1571
+		if ($line_item->type() === EEM_Line_Item::type_line_item &&
1572
+			$line_item->OBJ_type() === 'Ticket'
1573
+		) {
1574
+			$count = 0;
1575
+			foreach ($registrations as $registration) {
1576
+				if ($line_item->OBJ_ID() === $registration->ticket_ID() &&
1577
+					in_array($registration->status_ID(), EEM_Registration::reg_statuses_that_allow_payment())
1578
+				) {
1579
+					$count++;
1580
+				}
1581
+			}
1582
+			$new_li_fields['LIN_quantity'] = $count;
1583
+		}
1584
+		//don't set the total. We'll leave that up to the code that calculates it
1585
+		unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']);
1586
+		return EE_Line_Item::new_instance($new_li_fields);
1587
+	}
1588
+
1589
+
1590
+	/**
1591
+	 * Returns a modified line item tree where all the subtotals which have a total of 0
1592
+	 * are removed, and line items with a quantity of 0
1593
+	 *
1594
+	 * @param EE_Line_Item $line_item |null
1595
+	 * @return \EE_Line_Item|null
1596
+	 * @throws \EE_Error
1597
+	 */
1598
+	public static function non_empty_line_items(EE_Line_Item $line_item)
1599
+	{
1600
+		$copied_li = EEH_Line_Item::non_empty_line_item($line_item);
1601
+		if ($copied_li === null) {
1602
+			return null;
1603
+		}
1604
+		//if this is an event subtotal, we want to only include it if it
1605
+		//has a non-zero total and at least one ticket line item child
1606
+		$ticket_children = 0;
1607
+		foreach ($line_item->children() as $child_li) {
1608
+			$child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
1609
+			if ($child_li_copy !== null) {
1610
+				$copied_li->add_child_line_item($child_li_copy);
1611
+				if ($child_li_copy->type() === EEM_Line_Item::type_line_item &&
1612
+					$child_li_copy->OBJ_type() === 'Ticket'
1613
+				) {
1614
+					$ticket_children++;
1615
+				}
1616
+			}
1617
+		}
1618
+		//if this is an event subtotal with NO ticket children
1619
+		//we basically want to ignore it
1620
+		if (
1621
+			$ticket_children === 0
1622
+			&& $line_item->type() === EEM_Line_Item::type_sub_total
1623
+			&& $line_item->OBJ_type() === 'Event'
1624
+			&& $line_item->total() === 0
1625
+		) {
1626
+			return null;
1627
+		}
1628
+		return $copied_li;
1629
+	}
1630
+
1631
+
1632
+	/**
1633
+	 * Creates a new, unsaved line item, but if it's a ticket line item
1634
+	 * with a total of 0, or a subtotal of 0, returns null instead
1635
+	 *
1636
+	 * @param EE_Line_Item $line_item
1637
+	 * @return EE_Line_Item
1638
+	 * @throws \EE_Error
1639
+	 */
1640
+	public static function non_empty_line_item(EE_Line_Item $line_item)
1641
+	{
1642
+		if ($line_item->type() === EEM_Line_Item::type_line_item &&
1643
+			$line_item->OBJ_type() === 'Ticket' &&
1644
+			$line_item->quantity() === 0
1645
+		) {
1646
+			return null;
1647
+		}
1648
+		$new_li_fields = $line_item->model_field_array();
1649
+		//don't set the total. We'll leave that up to the code that calculates it
1650
+		unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']);
1651
+		return EE_Line_Item::new_instance($new_li_fields);
1652
+	}
1653
+
1654
+
1655
+
1656
+	/**************************************** @DEPRECATED METHODS *************************************** */
1657
+	/**
1658
+	 * @deprecated
1659
+	 * @param EE_Line_Item $total_line_item
1660
+	 * @return \EE_Line_Item
1661
+	 * @throws \EE_Error
1662
+	 */
1663
+	public static function get_items_subtotal(EE_Line_Item $total_line_item)
1664
+	{
1665
+		EE_Error::doing_it_wrong('EEH_Line_Item::get_items_subtotal()', __('Method replaced with EEH_Line_Item::get_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1666
+		return self::get_pre_tax_subtotal($total_line_item);
1667
+	}
1668
+
1669
+
1670
+	/**
1671
+	 * @deprecated
1672
+	 * @param EE_Transaction $transaction
1673
+	 * @return \EE_Line_Item
1674
+	 * @throws \EE_Error
1675
+	 */
1676
+	public static function create_default_total_line_item($transaction = NULL)
1677
+	{
1678
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_total_line_item()', __('Method replaced with EEH_Line_Item::create_total_line_item()', 'event_espresso'), '4.6.0');
1679
+		return self::create_total_line_item($transaction);
1680
+	}
1681
+
1682
+
1683
+	/**
1684
+	 * @deprecated
1685
+	 * @param EE_Line_Item $total_line_item
1686
+	 * @param EE_Transaction $transaction
1687
+	 * @return \EE_Line_Item
1688
+	 * @throws \EE_Error
1689
+	 */
1690
+	public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1691
+	{
1692
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_tickets_subtotal()', __('Method replaced with EEH_Line_Item::create_pre_tax_subtotal()', 'event_espresso'), '4.6.0');
1693
+		return self::create_pre_tax_subtotal($total_line_item, $transaction);
1694
+	}
1695
+
1696
+
1697
+	/**
1698
+	 * @deprecated
1699
+	 * @param EE_Line_Item $total_line_item
1700
+	 * @param EE_Transaction $transaction
1701
+	 * @return \EE_Line_Item
1702
+	 * @throws \EE_Error
1703
+	 */
1704
+	public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1705
+	{
1706
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_taxes_subtotal()', __('Method replaced with EEH_Line_Item::create_taxes_subtotal()', 'event_espresso'), '4.6.0');
1707
+		return self::create_taxes_subtotal($total_line_item, $transaction);
1708
+	}
1709
+
1710
+
1711
+	/**
1712
+	 * @deprecated
1713
+	 * @param EE_Line_Item $total_line_item
1714
+	 * @param EE_Transaction $transaction
1715
+	 * @return \EE_Line_Item
1716
+	 * @throws \EE_Error
1717
+	 */
1718
+	public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
1719
+	{
1720
+		EE_Error::doing_it_wrong('EEH_Line_Item::create_default_event_subtotal()', __('Method replaced with EEH_Line_Item::create_event_subtotal()', 'event_espresso'), '4.6.0');
1721
+		return self::create_event_subtotal($total_line_item, $transaction);
1722
+	}
1723 1723
 
1724 1724
 
1725 1725
 }
Please login to merge, or discard this patch.
Spacing   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -1,4 +1,4 @@  discard block
 block discarded – undo
1
-<?php if (!defined('EVENT_ESPRESSO_VERSION')) {
1
+<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2 2
     exit('No direct script access allowed');
3 3
 }
4 4
 
@@ -56,7 +56,7 @@  discard block
 block discarded – undo
56 56
             'LIN_percent' => null,
57 57
             'LIN_is_taxable' => $taxable,
58 58
             'LIN_order' => $items_subtotal instanceof EE_Line_Item ? count($items_subtotal->children()) : 0,
59
-            'LIN_total' => (float)$unit_price * (int)$quantity,
59
+            'LIN_total' => (float) $unit_price * (int) $quantity,
60 60
             'LIN_type' => EEM_Line_Item::type_line_item,
61 61
             'LIN_code' => $code,
62 62
         ));
@@ -95,7 +95,7 @@  discard block
 block discarded – undo
95 95
             'LIN_percent' => $percentage_amount,
96 96
             'LIN_quantity' => 1,
97 97
             'LIN_is_taxable' => $taxable,
98
-            'LIN_total' => (float)($percentage_amount * ($parent_line_item->total() / 100)),
98
+            'LIN_total' => (float) ($percentage_amount * ($parent_line_item->total() / 100)),
99 99
             'LIN_type' => EEM_Line_Item::type_line_item,
100 100
             'LIN_parent' => $parent_line_item->ID()
101 101
         ));
@@ -125,13 +125,13 @@  discard block
 block discarded – undo
125 125
      */
126 126
     public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
127 127
     {
128
-        if (!$total_line_item instanceof EE_Line_Item || !$total_line_item->is_total()) {
128
+        if ( ! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) {
129 129
             throw new EE_Error(sprintf(__('A valid line item total is required in order to add tickets. A line item of type "%s" was passed.', 'event_espresso'), $ticket->ID(), $total_line_item->ID()));
130 130
         }
131 131
         // either increment the qty for an existing ticket
132 132
         $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
133 133
         // or add a new one
134
-        if (!$line_item instanceof EE_Line_Item) {
134
+        if ( ! $line_item instanceof EE_Line_Item) {
135 135
             $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
136 136
         }
137 137
         $total_line_item->recalculate_total_including_taxes();
@@ -152,10 +152,10 @@  discard block
 block discarded – undo
152 152
         $line_item = null;
153 153
         if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
154 154
             $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
155
-            foreach ((array)$ticket_line_items as $ticket_line_item) {
155
+            foreach ((array) $ticket_line_items as $ticket_line_item) {
156 156
                 if (
157 157
                     $ticket_line_item instanceof EE_Line_Item
158
-                    && (int)$ticket_line_item->OBJ_ID() === (int)$ticket->ID()
158
+                    && (int) $ticket_line_item->OBJ_ID() === (int) $ticket->ID()
159 159
                 ) {
160 160
                     $line_item = $ticket_line_item;
161 161
                     break;
@@ -181,7 +181,7 @@  discard block
 block discarded – undo
181 181
      */
182 182
     public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
183 183
     {
184
-        if (!$line_item->is_percent()) {
184
+        if ( ! $line_item->is_percent()) {
185 185
             $qty += $line_item->quantity();
186 186
             $line_item->set_quantity($qty);
187 187
             $line_item->set_total($line_item->unit_price() * $qty);
@@ -206,7 +206,7 @@  discard block
 block discarded – undo
206 206
      */
207 207
     public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
208 208
     {
209
-        if (!$line_item->is_percent()) {
209
+        if ( ! $line_item->is_percent()) {
210 210
             $qty = $line_item->quantity() - $qty;
211 211
             $qty = max($qty, 0);
212 212
             $line_item->set_quantity($qty);
@@ -231,7 +231,7 @@  discard block
 block discarded – undo
231 231
      */
232 232
     public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
233 233
     {
234
-        if (!$line_item->is_percent()) {
234
+        if ( ! $line_item->is_percent()) {
235 235
             $line_item->set_quantity($new_quantity);
236 236
             $line_item->set_total($line_item->unit_price() * $new_quantity);
237 237
             $line_item->save();
@@ -267,7 +267,7 @@  discard block
 block discarded – undo
267 267
         // add $ticket to cart
268 268
         $line_item = EE_Line_Item::new_instance(array(
269 269
             'LIN_name' => $ticket->name(),
270
-            'LIN_desc' => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
270
+            'LIN_desc' => $ticket->description() !== '' ? $ticket->description().' '.$event : $event,
271 271
             'LIN_unit_price' => $ticket->price(),
272 272
             'LIN_quantity' => $qty,
273 273
             'LIN_is_taxable' => $ticket->taxable(),
@@ -377,8 +377,8 @@  discard block
 block discarded – undo
377 377
         foreach ($ticket_line_item->children() as $child_line_item) {
378 378
             if (
379 379
                 $child_line_item->is_sub_line_item()
380
-                && !$child_line_item->is_percent()
381
-                && !$child_line_item->is_cancellation()
380
+                && ! $child_line_item->is_percent()
381
+                && ! $child_line_item->is_cancellation()
382 382
             ) {
383 383
                 $child_line_item->set_quantity($child_line_item->quantity() - $qty);
384 384
             }
@@ -400,7 +400,7 @@  discard block
 block discarded – undo
400 400
                 'LIN_desc' => sprintf(
401 401
                     _x('Cancelled %1$s : %2$s', 'Cancelled Ticket Name : 2015-01-01 11:11', 'event_espresso'),
402 402
                     $ticket_line_item->name(),
403
-                    current_time(get_option('date_format') . ' ' . get_option('time_format'))
403
+                    current_time(get_option('date_format').' '.get_option('time_format'))
404 404
                 ),
405 405
                 'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
406 406
                 'LIN_quantity' => $qty,
@@ -453,7 +453,7 @@  discard block
 block discarded – undo
453 453
         );
454 454
         $cancellation_line_item = reset($cancellation_line_item);
455 455
         // verify that this ticket was indeed previously cancelled
456
-        if (!$cancellation_line_item instanceof EE_Line_Item) {
456
+        if ( ! $cancellation_line_item instanceof EE_Line_Item) {
457 457
             return false;
458 458
         }
459 459
         if ($cancellation_line_item->quantity() > $qty) {
@@ -625,7 +625,7 @@  discard block
 block discarded – undo
625 625
             'LIN_code' => 'taxes',
626 626
             'LIN_name' => __('Taxes', 'event_espresso'),
627 627
             'LIN_type' => EEM_Line_Item::type_tax_sub_total,
628
-            'LIN_order' => 1000,//this should always come last
628
+            'LIN_order' => 1000, //this should always come last
629 629
         ));
630 630
         $tax_line_item = apply_filters(
631 631
             'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
@@ -677,7 +677,7 @@  discard block
 block discarded – undo
677 677
      */
678 678
     public static function get_event_code($event)
679 679
     {
680
-        return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
680
+        return 'event-'.($event instanceof EE_Event ? $event->ID() : '0');
681 681
     }
682 682
 
683 683
     /**
@@ -713,13 +713,13 @@  discard block
 block discarded – undo
713 713
     public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
714 714
     {
715 715
         $first_datetime = $ticket->first_datetime();
716
-        if (!$first_datetime instanceof EE_Datetime) {
716
+        if ( ! $first_datetime instanceof EE_Datetime) {
717 717
             throw new EE_Error(
718 718
                 sprintf(__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), $ticket->ID())
719 719
             );
720 720
         }
721 721
         $event = $first_datetime->event();
722
-        if (!$event instanceof EE_Event) {
722
+        if ( ! $event instanceof EE_Event) {
723 723
             throw new EE_Error(
724 724
                 sprintf(
725 725
                     __('The supplied ticket (ID %d) has no event data associated with it.', 'event_espresso'),
@@ -728,7 +728,7 @@  discard block
 block discarded – undo
728 728
             );
729 729
         }
730 730
         $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
731
-        if (!$events_sub_total instanceof EE_Line_Item) {
731
+        if ( ! $events_sub_total instanceof EE_Line_Item) {
732 732
             throw new EE_Error(
733 733
                 sprintf(
734 734
                     __('There is no events sub-total for ticket %s on total line item %d', 'event_espresso'),
@@ -757,7 +757,7 @@  discard block
 block discarded – undo
757 757
         $found = false;
758 758
         foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
759 759
             // default event subtotal, we should only ever find this the first time this method is called
760
-            if (!$event_line_item->OBJ_ID()) {
760
+            if ( ! $event_line_item->OBJ_ID()) {
761 761
                 // let's use this! but first... set the event details
762 762
                 EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
763 763
                 $found = true;
@@ -768,7 +768,7 @@  discard block
 block discarded – undo
768 768
                 break;
769 769
             }
770 770
         }
771
-        if (!$found) {
771
+        if ( ! $found) {
772 772
             //there is no event sub-total yet, so add it
773 773
             $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
774 774
             // create a new "event" subtotal below that
@@ -863,7 +863,7 @@  discard block
 block discarded – undo
863 863
     public static function ensure_taxes_applied($total_line_item)
864 864
     {
865 865
         $taxes_subtotal = self::get_taxes_subtotal($total_line_item);
866
-        if (!$taxes_subtotal->children()) {
866
+        if ( ! $taxes_subtotal->children()) {
867 867
             self::apply_taxes($total_line_item);
868 868
         }
869 869
         return $taxes_subtotal->total();
@@ -924,7 +924,7 @@  discard block
 block discarded – undo
924 924
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
925 925
 
926 926
         // check if only a single line_item_id was passed
927
-        if (!empty($line_item_codes) && !is_array($line_item_codes)) {
927
+        if ( ! empty($line_item_codes) && ! is_array($line_item_codes)) {
928 928
             // place single line_item_id in an array to appear as multiple line_item_ids
929 929
             $line_item_codes = array($line_item_codes);
930 930
         }
@@ -1027,7 +1027,7 @@  discard block
 block discarded – undo
1027 1027
         if ($code_substring_for_whitelist !== null) {
1028 1028
             $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false ? true : false;
1029 1029
         }
1030
-        if (!$whitelisted && $line_item->is_line_item()) {
1030
+        if ( ! $whitelisted && $line_item->is_line_item()) {
1031 1031
             $line_item->set_is_taxable($taxable);
1032 1032
         }
1033 1033
         foreach ($line_item->children() as $child_line_item) {
@@ -1332,7 +1332,7 @@  discard block
 block discarded – undo
1332 1332
     public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1333 1333
     {
1334 1334
         echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1335
-        if (!$indentation) {
1335
+        if ( ! $indentation) {
1336 1336
             echo defined('EE_TESTS_DIR') ? "\n" : '<br />';
1337 1337
         }
1338 1338
         for ($i = 0; $i < $indentation; $i++) {
@@ -1343,10 +1343,10 @@  discard block
 block discarded – undo
1343 1343
             if ($line_item->is_percent()) {
1344 1344
                 $breakdown = "{$line_item->percent()}%";
1345 1345
             } else {
1346
-                $breakdown = '$' . "{$line_item->unit_price()} x {$line_item->quantity()}";
1346
+                $breakdown = '$'."{$line_item->unit_price()} x {$line_item->quantity()}";
1347 1347
             }
1348 1348
         }
1349
-        echo $line_item->name() . " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : " . '$' . "{$line_item->total()}";
1349
+        echo $line_item->name()." [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : ".'$'."{$line_item->total()}";
1350 1350
         if ($breakdown) {
1351 1351
             echo " ( {$breakdown} )";
1352 1352
         }
@@ -1397,10 +1397,10 @@  discard block
 block discarded – undo
1397 1397
     public static function calculate_reg_final_prices_per_line_item(EE_Line_Item $line_item, $billable_ticket_quantities = array())
1398 1398
     {
1399 1399
         //init running grand total if not already
1400
-        if (!isset($running_totals['total'])) {
1400
+        if ( ! isset($running_totals['total'])) {
1401 1401
             $running_totals['total'] = 0;
1402 1402
         }
1403
-        if (!isset($running_totals['taxable'])) {
1403
+        if ( ! isset($running_totals['taxable'])) {
1404 1404
             $running_totals['taxable'] = array('total' => 0);
1405 1405
         }
1406 1406
         foreach ($line_item->children() as $child_line_item) {
@@ -1522,7 +1522,7 @@  discard block
 block discarded – undo
1522 1522
             $ticket_line_item->ID()
1523 1523
         );
1524 1524
         if (WP_DEBUG) {
1525
-            $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1525
+            $message .= '<br>'.print_r($final_prices_per_ticket_line_item, true);
1526 1526
             throw new \OutOfRangeException($message);
1527 1527
         } else {
1528 1528
             EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
Please login to merge, or discard this patch.
core/db_classes/EE_Line_Item.class.php 2 patches
Indentation   +1426 added lines, -1426 removed lines patch added patch discarded remove patch
@@ -17,1432 +17,1432 @@
 block discarded – undo
17 17
 class EE_Line_Item extends EE_Base_Class implements EEI_Line_Item
18 18
 {
19 19
 
20
-    /**
21
-     * for children line items (currently not a normal relation)
22
-     *
23
-     * @type EE_Line_Item[]
24
-     */
25
-    protected $_children = array();
26
-
27
-    /**
28
-     * for the parent line item
29
-     *
30
-     * @var EE_Line_Item
31
-     */
32
-    protected $_parent;
33
-
34
-
35
-    /**
36
-     *
37
-     * @param array $props_n_values incoming values
38
-     * @param string $timezone incoming timezone (if not set the timezone set for the website will be
39
-     *                                        used.)
40
-     * @param array $date_formats incoming date_formats in an array where the first value is the
41
-     *                                        date_format and the second value is the time format
42
-     * @return EE_Line_Item
43
-     * @throws EE_Error
44
-     */
45
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
46
-    {
47
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
48
-        return $has_object
49
-            ? $has_object
50
-            : new self($props_n_values, false, $timezone);
51
-    }
52
-
53
-
54
-    /**
55
-     * @param array $props_n_values incoming values from the database
56
-     * @param string $timezone incoming timezone as set by the model.  If not set the timezone for
57
-     *                                the website will be used.
58
-     * @return EE_Line_Item
59
-     * @throws EE_Error
60
-     */
61
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
62
-    {
63
-        return new self($props_n_values, true, $timezone);
64
-    }
65
-
66
-
67
-    /**
68
-     * Adds some defaults if they're not specified
69
-     *
70
-     * @param array $fieldValues
71
-     * @param bool $bydb
72
-     * @param string $timezone
73
-     * @throws EE_Error
74
-     */
75
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
76
-    {
77
-        parent::__construct($fieldValues, $bydb, $timezone);
78
-        if (!$this->get('LIN_code')) {
79
-            $this->set_code($this->generate_code());
80
-        }
81
-    }
82
-
83
-
84
-    /**
85
-     * Gets ID
86
-     *
87
-     * @return int
88
-     * @throws EE_Error
89
-     */
90
-    public function ID()
91
-    {
92
-        return $this->get('LIN_ID');
93
-    }
94
-
95
-
96
-    /**
97
-     * Gets TXN_ID
98
-     *
99
-     * @return int
100
-     * @throws EE_Error
101
-     */
102
-    public function TXN_ID()
103
-    {
104
-        return $this->get('TXN_ID');
105
-    }
106
-
107
-
108
-    /**
109
-     * Sets TXN_ID
110
-     *
111
-     * @param int $TXN_ID
112
-     * @throws EE_Error
113
-     */
114
-    public function set_TXN_ID($TXN_ID)
115
-    {
116
-        $this->set('TXN_ID', $TXN_ID);
117
-    }
118
-
119
-
120
-    /**
121
-     * Gets name
122
-     *
123
-     * @return string
124
-     * @throws EE_Error
125
-     */
126
-    public function name()
127
-    {
128
-        $name = $this->get('LIN_name');
129
-        if (!$name) {
130
-            $name = ucwords(str_replace('-', ' ', $this->type()));
131
-        }
132
-        return $name;
133
-    }
134
-
135
-
136
-    /**
137
-     * Sets name
138
-     *
139
-     * @param string $name
140
-     * @throws EE_Error
141
-     */
142
-    public function set_name($name)
143
-    {
144
-        $this->set('LIN_name', $name);
145
-    }
146
-
147
-
148
-    /**
149
-     * Gets desc
150
-     *
151
-     * @return string
152
-     * @throws EE_Error
153
-     */
154
-    public function desc()
155
-    {
156
-        return $this->get('LIN_desc');
157
-    }
158
-
159
-
160
-    /**
161
-     * Sets desc
162
-     *
163
-     * @param string $desc
164
-     * @throws EE_Error
165
-     */
166
-    public function set_desc($desc)
167
-    {
168
-        $this->set('LIN_desc', $desc);
169
-    }
170
-
171
-
172
-    /**
173
-     * Gets quantity
174
-     *
175
-     * @return int
176
-     * @throws EE_Error
177
-     */
178
-    public function quantity()
179
-    {
180
-        return $this->get('LIN_quantity');
181
-    }
182
-
183
-
184
-    /**
185
-     * Sets quantity
186
-     *
187
-     * @param int $quantity
188
-     * @throws EE_Error
189
-     */
190
-    public function set_quantity($quantity)
191
-    {
192
-        $this->set('LIN_quantity', max($quantity, 0));
193
-    }
194
-
195
-
196
-    /**
197
-     * Gets item_id
198
-     *
199
-     * @return string
200
-     * @throws EE_Error
201
-     */
202
-    public function OBJ_ID()
203
-    {
204
-        return $this->get('OBJ_ID');
205
-    }
206
-
207
-
208
-    /**
209
-     * Sets item_id
210
-     *
211
-     * @param string $item_id
212
-     * @throws EE_Error
213
-     */
214
-    public function set_OBJ_ID($item_id)
215
-    {
216
-        $this->set('OBJ_ID', $item_id);
217
-    }
218
-
219
-
220
-    /**
221
-     * Gets item_type
222
-     *
223
-     * @return string
224
-     * @throws EE_Error
225
-     */
226
-    public function OBJ_type()
227
-    {
228
-        return $this->get('OBJ_type');
229
-    }
230
-
231
-
232
-    /**
233
-     * Gets item_type
234
-     *
235
-     * @return string
236
-     * @throws EE_Error
237
-     */
238
-    public function OBJ_type_i18n()
239
-    {
240
-        $obj_type = $this->OBJ_type();
241
-        switch ($obj_type) {
242
-            case 'Event':
243
-                $obj_type = __('Event', 'event_espresso');
244
-                break;
245
-            case 'Price':
246
-                $obj_type = __('Price', 'event_espresso');
247
-                break;
248
-            case 'Promotion':
249
-                $obj_type = __('Promotion', 'event_espresso');
250
-                break;
251
-            case 'Ticket':
252
-                $obj_type = __('Ticket', 'event_espresso');
253
-                break;
254
-            case 'Transaction':
255
-                $obj_type = __('Transaction', 'event_espresso');
256
-                break;
257
-        }
258
-        return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
259
-    }
260
-
261
-
262
-    /**
263
-     * Sets item_type
264
-     *
265
-     * @param string $OBJ_type
266
-     * @throws EE_Error
267
-     */
268
-    public function set_OBJ_type($OBJ_type)
269
-    {
270
-        $this->set('OBJ_type', $OBJ_type);
271
-    }
272
-
273
-
274
-    /**
275
-     * Gets unit_price
276
-     *
277
-     * @return float
278
-     * @throws EE_Error
279
-     */
280
-    public function unit_price()
281
-    {
282
-        return $this->get('LIN_unit_price');
283
-    }
284
-
285
-
286
-    /**
287
-     * Sets unit_price
288
-     *
289
-     * @param float $unit_price
290
-     * @throws EE_Error
291
-     */
292
-    public function set_unit_price($unit_price)
293
-    {
294
-        $this->set('LIN_unit_price', $unit_price);
295
-    }
296
-
297
-
298
-    /**
299
-     * Checks if this item is a percentage modifier or not
300
-     *
301
-     * @return boolean
302
-     * @throws EE_Error
303
-     */
304
-    public function is_percent()
305
-    {
306
-        if ($this->is_tax_sub_total()) {
307
-            //tax subtotals HAVE a percent on them, that percentage only applies
308
-            //to taxable items, so its' an exception. Treat it like a flat line item
309
-            return false;
310
-        }
311
-        $unit_price = abs($this->get('LIN_unit_price'));
312
-        $percent = abs($this->get('LIN_percent'));
313
-        if ($unit_price < .001 && $percent) {
314
-            return true;
315
-        }
316
-        if ($unit_price >= .001 && !$percent) {
317
-            return false;
318
-        }
319
-        if ($unit_price >= .001 && $percent) {
320
-            throw new EE_Error(
321
-                sprintf(
322
-                    esc_html__('A Line Item can not have a unit price of (%s) AND a percent (%s)!', 'event_espresso'),
323
-                    $unit_price, $percent
324
-                )
325
-            );
326
-        }
327
-        // if they're both 0, assume its not a percent item
328
-        return false;
329
-    }
330
-
331
-
332
-    /**
333
-     * Gets percent (between 100-.001)
334
-     *
335
-     * @return float
336
-     * @throws EE_Error
337
-     */
338
-    public function percent()
339
-    {
340
-        return $this->get('LIN_percent');
341
-    }
342
-
343
-
344
-    /**
345
-     * Sets percent (between 100-0.01)
346
-     *
347
-     * @param float $percent
348
-     * @throws EE_Error
349
-     */
350
-    public function set_percent($percent)
351
-    {
352
-        $this->set('LIN_percent', $percent);
353
-    }
354
-
355
-
356
-    /**
357
-     * Gets total
358
-     *
359
-     * @return float
360
-     * @throws EE_Error
361
-     */
362
-    public function total()
363
-    {
364
-        return $this->get('LIN_total');
365
-    }
366
-
367
-
368
-    /**
369
-     * Sets total
370
-     *
371
-     * @param float $total
372
-     * @throws EE_Error
373
-     */
374
-    public function set_total($total)
375
-    {
376
-        $this->set('LIN_total', $total);
377
-    }
378
-
379
-
380
-    /**
381
-     * Gets order
382
-     *
383
-     * @return int
384
-     * @throws EE_Error
385
-     */
386
-    public function order()
387
-    {
388
-        return $this->get('LIN_order');
389
-    }
390
-
391
-
392
-    /**
393
-     * Sets order
394
-     *
395
-     * @param int $order
396
-     * @throws EE_Error
397
-     */
398
-    public function set_order($order)
399
-    {
400
-        $this->set('LIN_order', $order);
401
-    }
402
-
403
-
404
-    /**
405
-     * Gets parent
406
-     *
407
-     * @return int
408
-     * @throws EE_Error
409
-     */
410
-    public function parent_ID()
411
-    {
412
-        return $this->get('LIN_parent');
413
-    }
414
-
415
-
416
-    /**
417
-     * Sets parent
418
-     *
419
-     * @param int $parent
420
-     * @throws EE_Error
421
-     */
422
-    public function set_parent_ID($parent)
423
-    {
424
-        $this->set('LIN_parent', $parent);
425
-    }
426
-
427
-
428
-    /**
429
-     * Gets type
430
-     *
431
-     * @return string
432
-     * @throws EE_Error
433
-     */
434
-    public function type()
435
-    {
436
-        return $this->get('LIN_type');
437
-    }
438
-
439
-
440
-    /**
441
-     * Sets type
442
-     *
443
-     * @param string $type
444
-     * @throws EE_Error
445
-     */
446
-    public function set_type($type)
447
-    {
448
-        $this->set('LIN_type', $type);
449
-    }
450
-
451
-
452
-    /**
453
-     * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
454
-     * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
455
-     * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
456
-     * or indirectly by `EE_Line_item::add_child_line_item()`)
457
-     *
458
-     * @return EE_Base_Class|EE_Line_Item
459
-     * @throws EE_Error
460
-     */
461
-    public function parent()
462
-    {
463
-        return $this->ID()
464
-            ? $this->get_model()->get_one_by_ID($this->parent_ID())
465
-            : $this->_parent;
466
-    }
467
-
468
-
469
-    /**
470
-     * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
471
-     *
472
-     * @return EE_Base_Class[]|EE_Line_Item[]
473
-     * @throws EE_Error
474
-     */
475
-    public function children()
476
-    {
477
-        if ($this->ID()) {
478
-            return $this->get_model()->get_all(
479
-                array(
480
-                    array('LIN_parent' => $this->ID()),
481
-                    'order_by' => array('LIN_order' => 'ASC'),
482
-                )
483
-            );
484
-        }
485
-        if (!is_array($this->_children)) {
486
-            $this->_children = array();
487
-        }
488
-        return $this->_children;
489
-    }
490
-
491
-
492
-    /**
493
-     * Gets code
494
-     *
495
-     * @return string
496
-     * @throws EE_Error
497
-     */
498
-    public function code()
499
-    {
500
-        return $this->get('LIN_code');
501
-    }
502
-
503
-
504
-    /**
505
-     * Sets code
506
-     *
507
-     * @param string $code
508
-     * @throws EE_Error
509
-     */
510
-    public function set_code($code)
511
-    {
512
-        $this->set('LIN_code', $code);
513
-    }
514
-
515
-
516
-    /**
517
-     * Gets is_taxable
518
-     *
519
-     * @return boolean
520
-     * @throws EE_Error
521
-     */
522
-    public function is_taxable()
523
-    {
524
-        return $this->get('LIN_is_taxable');
525
-    }
526
-
527
-
528
-    /**
529
-     * Sets is_taxable
530
-     *
531
-     * @param boolean $is_taxable
532
-     * @throws EE_Error
533
-     */
534
-    public function set_is_taxable($is_taxable)
535
-    {
536
-        $this->set('LIN_is_taxable', $is_taxable);
537
-    }
538
-
539
-
540
-    /**
541
-     * Gets the object that this model-joins-to.
542
-     * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
543
-     * EEM_Promotion_Object
544
-     *
545
-     *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
546
-     *
547
-     * @return EE_Base_Class | NULL
548
-     * @throws EE_Error
549
-     */
550
-    public function get_object()
551
-    {
552
-        $model_name_of_related_obj = $this->OBJ_type();
553
-        return $this->get_model()->has_relation($model_name_of_related_obj)
554
-            ? $this->get_first_related($model_name_of_related_obj)
555
-            : null;
556
-    }
557
-
558
-
559
-    /**
560
-     * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
561
-     * (IE, if this line item is for a price or something else, will return NULL)
562
-     *
563
-     * @param array $query_params
564
-     * @return EE_Base_Class|EE_Ticket
565
-     * @throws EE_Error
566
-     */
567
-    public function ticket($query_params = array())
568
-    {
569
-        //we're going to assume that when this method is called we always want to receive the attached ticket EVEN if that ticket is archived.  This can be overridden via the incoming $query_params argument
570
-        $remove_defaults = array('default_where_conditions' => 'none');
571
-        $query_params = array_merge($remove_defaults, $query_params);
572
-        return $this->get_first_related('Ticket', $query_params);
573
-    }
574
-
575
-
576
-    /**
577
-     * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
578
-     *
579
-     * @return EE_Datetime | NULL
580
-     * @throws EE_Error
581
-     */
582
-    public function get_ticket_datetime()
583
-    {
584
-        if ($this->OBJ_type() === 'Ticket') {
585
-            $ticket = $this->ticket();
586
-            if ($ticket instanceof EE_Ticket) {
587
-                $datetime = $ticket->first_datetime();
588
-                if ($datetime instanceof EE_Datetime) {
589
-                    return $datetime;
590
-                }
591
-            }
592
-        }
593
-        return null;
594
-    }
595
-
596
-
597
-    /**
598
-     * Gets the event's name that's related to the ticket, if this is for
599
-     * a ticket
600
-     *
601
-     * @return string
602
-     * @throws EE_Error
603
-     */
604
-    public function ticket_event_name()
605
-    {
606
-        $event_name = esc_html__('Unknown', 'event_espresso');
607
-        $event = $this->ticket_event();
608
-        if ($event instanceof EE_Event) {
609
-            $event_name = $event->name();
610
-        }
611
-        return $event_name;
612
-    }
613
-
614
-
615
-    /**
616
-     * Gets the event that's related to the ticket, if this line item represents a ticket.
617
-     *
618
-     * @return EE_Event|null
619
-     * @throws EE_Error
620
-     */
621
-    public function ticket_event()
622
-    {
623
-        $event = null;
624
-        $ticket = $this->ticket();
625
-        if ($ticket instanceof EE_Ticket) {
626
-            $datetime = $ticket->first_datetime();
627
-            if ($datetime instanceof EE_Datetime) {
628
-                $event = $datetime->event();
629
-            }
630
-        }
631
-        return $event;
632
-    }
633
-
634
-
635
-    /**
636
-     * Gets the first datetime for this lien item, assuming it's for a ticket
637
-     *
638
-     * @param string $date_format
639
-     * @param string $time_format
640
-     * @return string
641
-     * @throws EE_Error
642
-     */
643
-    public function ticket_datetime_start($date_format = '', $time_format = '')
644
-    {
645
-        $first_datetime_string = esc_html__('Unknown', 'event_espresso');
646
-        $datetime = $this->get_ticket_datetime();
647
-        if ($datetime) {
648
-            $first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
649
-        }
650
-        return $first_datetime_string;
651
-    }
652
-
653
-
654
-    /**
655
-     * Adds the line item as a child to this line item. If there is another child line
656
-     * item with the same LIN_code, it is overwritten by this new one
657
-     *
658
-     * @param EEI_Line_Item $line_item
659
-     * @param bool $set_order
660
-     * @return bool success
661
-     * @throws EE_Error
662
-     */
663
-    public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
664
-    {
665
-        // should we calculate the LIN_order for this line item ?
666
-        if ($set_order || $line_item->order() === null) {
667
-            $line_item->set_order(count($this->children()));
668
-        }
669
-        if ($this->ID()) {
670
-            //check for any duplicate line items (with the same code), if so, this replaces it
671
-            $line_item_with_same_code = $this->get_child_line_item($line_item->code());
672
-            if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
673
-                $this->delete_child_line_item($line_item_with_same_code->code());
674
-            }
675
-            $line_item->set_parent_ID($this->ID());
676
-            if ($this->TXN_ID()) {
677
-                $line_item->set_TXN_ID($this->TXN_ID());
678
-            }
679
-            return $line_item->save();
680
-        }
681
-        $this->_children[$line_item->code()] = $line_item;
682
-        if ($line_item->parent() !== $this) {
683
-            $line_item->set_parent($this);
684
-        }
685
-        return true;
686
-    }
687
-
688
-
689
-    /**
690
-     * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
691
-     * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
692
-     * However, if this line item is NOT saved to the DB, this just caches the parent on
693
-     * the EE_Line_Item::_parent property.
694
-     *
695
-     * @param EE_Line_Item $line_item
696
-     * @throws EE_Error
697
-     */
698
-    public function set_parent($line_item)
699
-    {
700
-        if ($this->ID()) {
701
-            if (!$line_item->ID()) {
702
-                $line_item->save();
703
-            }
704
-            $this->set_parent_ID($line_item->ID());
705
-            $this->save();
706
-        } else {
707
-            $this->_parent = $line_item;
708
-            $this->set_parent_ID($line_item->ID());
709
-        }
710
-    }
711
-
712
-
713
-    /**
714
-     * Gets the child line item as specified by its code. Because this returns an object (by reference)
715
-     * you can modify this child line item and the parent (this object) can know about them
716
-     * because it also has a reference to that line item
717
-     *
718
-     * @param string $code
719
-     * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
720
-     * @throws EE_Error
721
-     */
722
-    public function get_child_line_item($code)
723
-    {
724
-        if ($this->ID()) {
725
-            return $this->get_model()->get_one(
726
-                array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
727
-            );
728
-        }
729
-        return isset($this->_children[$code])
730
-            ? $this->_children[$code]
731
-            : null;
732
-    }
733
-
734
-
735
-    /**
736
-     * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
737
-     * cached on it)
738
-     *
739
-     * @return int
740
-     * @throws EE_Error
741
-     */
742
-    public function delete_children_line_items()
743
-    {
744
-        if ($this->ID()) {
745
-            return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
746
-        }
747
-        $count = count($this->_children);
748
-        $this->_children = array();
749
-        return $count;
750
-    }
751
-
752
-
753
-    /**
754
-     * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
755
-     * HAS NOT been saved to the DB, removes the child line item with index $code.
756
-     * Also searches through the child's children for a matching line item. However, once a line item has been found
757
-     * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
758
-     * deleted)
759
-     *
760
-     * @param string $code
761
-     * @param bool $stop_search_once_found
762
-     * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
763
-     *             the DB yet)
764
-     * @throws EE_Error
765
-     */
766
-    public function delete_child_line_item($code, $stop_search_once_found = true)
767
-    {
768
-        if ($this->ID()) {
769
-            $items_deleted = 0;
770
-            if ($this->code() === $code) {
771
-                $items_deleted += EEH_Line_Item::delete_all_child_items($this);
772
-                $items_deleted += (int)$this->delete();
773
-                if ($stop_search_once_found) {
774
-                    return $items_deleted;
775
-                }
776
-            }
777
-            foreach ($this->children() as $child_line_item) {
778
-                $items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
779
-            }
780
-            return $items_deleted;
781
-        }
782
-        if (isset($this->_children[$code])) {
783
-            unset($this->_children[$code]);
784
-            return 1;
785
-        }
786
-        return 0;
787
-    }
788
-
789
-
790
-    /**
791
-     * If this line item is in the database, is of the type subtotal, and
792
-     * has no children, why do we have it? It should be deleted so this function
793
-     * does that
794
-     *
795
-     * @return boolean
796
-     * @throws EE_Error
797
-     */
798
-    public function delete_if_childless_subtotal()
799
-    {
800
-        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && !$this->children()) {
801
-            return $this->delete();
802
-        }
803
-        return false;
804
-    }
805
-
806
-
807
-    /**
808
-     * Creates a code and returns a string. doesn't assign the code to this model object
809
-     *
810
-     * @return string
811
-     * @throws EE_Error
812
-     */
813
-    public function generate_code()
814
-    {
815
-        // each line item in the cart requires a unique identifier
816
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
817
-    }
818
-
819
-
820
-    /**
821
-     * @return bool
822
-     * @throws EE_Error
823
-     */
824
-    public function is_tax()
825
-    {
826
-        return $this->type() === EEM_Line_Item::type_tax;
827
-    }
828
-
829
-
830
-    /**
831
-     * @return bool
832
-     * @throws EE_Error
833
-     */
834
-    public function is_tax_sub_total()
835
-    {
836
-        return $this->type() === EEM_Line_Item::type_tax_sub_total;
837
-    }
838
-
839
-
840
-    /**
841
-     * @return bool
842
-     * @throws EE_Error
843
-     */
844
-    public function is_line_item()
845
-    {
846
-        return $this->type() === EEM_Line_Item::type_line_item;
847
-    }
848
-
849
-
850
-    /**
851
-     * @return bool
852
-     * @throws EE_Error
853
-     */
854
-    public function is_sub_line_item()
855
-    {
856
-        return $this->type() === EEM_Line_Item::type_sub_line_item;
857
-    }
858
-
859
-
860
-    /**
861
-     * @return bool
862
-     * @throws EE_Error
863
-     */
864
-    public function is_sub_total()
865
-    {
866
-        return $this->type() === EEM_Line_Item::type_sub_total;
867
-    }
868
-
869
-
870
-    /**
871
-     * Whether or not this line item is a cancellation line item
872
-     *
873
-     * @return boolean
874
-     * @throws EE_Error
875
-     */
876
-    public function is_cancellation()
877
-    {
878
-        return EEM_Line_Item::type_cancellation === $this->type();
879
-    }
880
-
881
-
882
-    /**
883
-     * @return bool
884
-     * @throws EE_Error
885
-     */
886
-    public function is_total()
887
-    {
888
-        return $this->type() === EEM_Line_Item::type_total;
889
-    }
890
-
891
-
892
-    /**
893
-     * @return bool
894
-     * @throws EE_Error
895
-     */
896
-    public function is_cancelled()
897
-    {
898
-        return $this->type() === EEM_Line_Item::type_cancellation;
899
-    }
900
-
901
-
902
-    /**
903
-     * @return string like '2, 004.00', formatted according to the localized currency
904
-     * @throws EE_Error
905
-     */
906
-    public function unit_price_no_code()
907
-    {
908
-        return $this->get_pretty('LIN_unit_price', 'no_currency_code');
909
-    }
910
-
911
-
912
-    /**
913
-     * @return string like '2, 004.00', formatted according to the localized currency
914
-     * @throws EE_Error
915
-     */
916
-    public function total_no_code()
917
-    {
918
-        return $this->get_pretty('LIN_total', 'no_currency_code');
919
-    }
920
-
921
-
922
-    /**
923
-     * Gets the final total on this item, taking taxes into account.
924
-     * Has the side-effect of setting the sub-total as it was just calculated.
925
-     * If this is used on a grand-total line item, also updates the transaction's
926
-     * TXN_total (provided this line item is allowed to persist, otherwise we don't
927
-     * want to change a persistable transaction with info from a non-persistent line item)
928
-     *
929
-     * @return float
930
-     * @throws EE_Error
931
-     * @throws InvalidArgumentException
932
-     * @throws InvalidInterfaceException
933
-     * @throws InvalidDataTypeException
934
-     */
935
-    public function recalculate_total_including_taxes()
936
-    {
937
-        $pre_tax_total = $this->recalculate_pre_tax_total();
938
-        $tax_total = $this->recalculate_taxes_and_tax_total();
939
-        $total = $pre_tax_total + $tax_total;
940
-        // no negative totals plz
941
-        $total = max($total, 0);
942
-        $this->set_total($total);
943
-        //only update the related transaction's total
944
-        //if we intend to save this line item and its a grand total
945
-        if (
946
-            $this->allow_persist() && $this->type() === EEM_Line_Item::type_total
947
-            && $this->transaction()
948
-            instanceof
949
-            EE_Transaction
950
-        ) {
951
-            $this->transaction()->set_total($total);
952
-            if ($this->transaction()->ID()) {
953
-                $this->transaction()->save();
954
-            }
955
-        }
956
-        $this->maybe_save();
957
-        return $total;
958
-    }
959
-
960
-
961
-    /**
962
-     * Recursively goes through all the children and recalculates sub-totals EXCEPT for
963
-     * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
964
-     * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
965
-     * when this is called on the grand total
966
-     *
967
-     * @return float
968
-     * @throws InvalidArgumentException
969
-     * @throws InvalidInterfaceException
970
-     * @throws InvalidDataTypeException
971
-     * @throws EE_Error
972
-     */
973
-    public function recalculate_pre_tax_total()
974
-    {
975
-        $total = 0;
976
-        $my_children = $this->children();
977
-        $has_children = !empty($my_children);
978
-        if ($has_children && $this->is_line_item()) {
979
-            $total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
980
-        } elseif (!$has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
981
-            $total = $this->unit_price() * $this->quantity();
982
-        } elseif ($this->is_sub_total() || $this->is_total()) {
983
-            $total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
984
-        } elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
985
-            // completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
986
-            return 0;
987
-        }
988
-        // ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
989
-        if (
990
-            !$this->is_line_item() && !$this->is_sub_line_item() && !$this->is_cancellation()
991
-        ) {
992
-            if ($this->OBJ_type() !== 'Event') {
993
-                $this->set_quantity(1);
994
-            }
995
-            if (!$this->is_percent()) {
996
-                $this->set_unit_price($total);
997
-            }
998
-        }
999
-        //we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1000
-        //so it ought to be
1001
-        if (!$this->is_total()) {
1002
-            $this->set_total($total);
1003
-            //if not a percent line item, make sure we keep the unit price in sync
1004
-            if (
1005
-                $has_children
1006
-                && $this->is_line_item()
1007
-                && !$this->is_percent()
1008
-            ) {
1009
-                if ($this->quantity() === 0) {
1010
-                    $new_unit_price = 0;
1011
-                } else {
1012
-                    $new_unit_price = $this->total() / $this->quantity();
1013
-                }
1014
-                $this->set_unit_price($new_unit_price);
1015
-            }
1016
-            $this->maybe_save();
1017
-        }
1018
-        return $total;
1019
-    }
1020
-
1021
-
1022
-    /**
1023
-     * Calculates the pretax total when this line item is a subtotal or total line item.
1024
-     * Basically does a sum-then-round approach (ie, any percent line item that are children
1025
-     * will calculate their total based on the un-rounded total we're working with so far, and
1026
-     * THEN round the result; instead of rounding as we go like with sub-line-items)
1027
-     *
1028
-     * @param float $calculated_total_so_far
1029
-     * @param EE_Line_Item[] $my_children
1030
-     * @return float
1031
-     * @throws InvalidArgumentException
1032
-     * @throws InvalidInterfaceException
1033
-     * @throws InvalidDataTypeException
1034
-     * @throws EE_Error
1035
-     */
1036
-    protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1037
-    {
1038
-        if ($my_children === null) {
1039
-            $my_children = $this->children();
1040
-        }
1041
-        $subtotal_quantity = 0;
1042
-        //get the total of all its children
1043
-        foreach ($my_children as $child_line_item) {
1044
-            if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1045
-                // percentage line items are based on total so far
1046
-                if ($child_line_item->is_percent()) {
1047
-                    //round as we go so that the line items add up ok
1048
-                    $percent_total = round(
1049
-                        $calculated_total_so_far * $child_line_item->percent() / 100,
1050
-                        EE_Registry::instance()->CFG->currency->dec_plc
1051
-                    );
1052
-                    $child_line_item->set_total($percent_total);
1053
-                    //so far all percent line items should have a quantity of 1
1054
-                    //(ie, no double percent discounts. Although that might be requested someday)
1055
-                    $child_line_item->set_quantity(1);
1056
-                    $child_line_item->maybe_save();
1057
-                    $calculated_total_so_far += $percent_total;
1058
-                } else {
1059
-                    //verify flat sub-line-item quantities match their parent
1060
-                    if ($child_line_item->is_sub_line_item()) {
1061
-                        $child_line_item->set_quantity($this->quantity());
1062
-                    }
1063
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1064
-                    $subtotal_quantity += $child_line_item->quantity();
1065
-                }
1066
-            }
1067
-        }
1068
-        if ($this->is_sub_total()) {
1069
-            // no negative totals plz
1070
-            $calculated_total_so_far = max($calculated_total_so_far, 0);
1071
-            $subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1072
-            $this->set_quantity($subtotal_quantity);
1073
-            $this->maybe_save();
1074
-        }
1075
-        return $calculated_total_so_far;
1076
-    }
1077
-
1078
-
1079
-    /**
1080
-     * Calculates the pretax total for a normal line item, in a round-then-sum approach
1081
-     * (where each sub-line-item is applied to the base price for the line item
1082
-     * and the result is immediately rounded, rather than summing all the sub-line-items
1083
-     * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1084
-     *
1085
-     * @param float $calculated_total_so_far
1086
-     * @param EE_Line_Item[] $my_children
1087
-     * @return float
1088
-     * @throws InvalidArgumentException
1089
-     * @throws InvalidInterfaceException
1090
-     * @throws InvalidDataTypeException
1091
-     * @throws EE_Error
1092
-     */
1093
-    protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1094
-    {
1095
-        if ($my_children === null) {
1096
-            $my_children = $this->children();
1097
-        }
1098
-        //we need to keep track of the running total for a single item,
1099
-        //because we need to round as we go
1100
-        $unit_price_for_total = 0;
1101
-        $quantity_for_total = 1;
1102
-        //get the total of all its children
1103
-        foreach ($my_children as $child_line_item) {
1104
-            if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1105
-                if ($child_line_item->is_percent()) {
1106
-                    //it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1107
-                    //not total multiplied by percent, because that ignores rounding along-the-way
1108
-                    $percent_unit_price = round(
1109
-                        $unit_price_for_total * $child_line_item->percent() / 100,
1110
-                        EE_Registry::instance()->CFG->currency->dec_plc
1111
-                    );
1112
-                    $percent_total = $percent_unit_price * $quantity_for_total;
1113
-                    $child_line_item->set_total($percent_total);
1114
-                    //so far all percent line items should have a quantity of 1
1115
-                    //(ie, no double percent discounts. Although that might be requested someday)
1116
-                    $child_line_item->set_quantity(1);
1117
-                    $child_line_item->maybe_save();
1118
-                    $calculated_total_so_far += $percent_total;
1119
-                    $unit_price_for_total += $percent_unit_price;
1120
-                } else {
1121
-                    //verify flat sub-line-item quantities match their parent
1122
-                    if ($child_line_item->is_sub_line_item()) {
1123
-                        $child_line_item->set_quantity($this->quantity());
1124
-                    }
1125
-                    $quantity_for_total = $child_line_item->quantity();
1126
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1127
-                    $unit_price_for_total += $child_line_item->unit_price();
1128
-                }
1129
-            }
1130
-        }
1131
-        return $calculated_total_so_far;
1132
-    }
1133
-
1134
-
1135
-    /**
1136
-     * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1137
-     * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1138
-     * and tax sub-total if already in the DB
1139
-     *
1140
-     * @return float
1141
-     * @throws EE_Error
1142
-     */
1143
-    public function recalculate_taxes_and_tax_total()
1144
-    {
1145
-        //get all taxes
1146
-        $taxes = $this->tax_descendants();
1147
-        //calculate the pretax total
1148
-        $taxable_total = $this->taxable_total();
1149
-        $tax_total = 0;
1150
-        foreach ($taxes as $tax) {
1151
-            $total_on_this_tax = $taxable_total * $tax->percent() / 100;
1152
-            //remember the total on this line item
1153
-            $tax->set_total($total_on_this_tax);
1154
-            $tax->maybe_save();
1155
-            $tax_total += $tax->total();
1156
-        }
1157
-        $this->_recalculate_tax_sub_total();
1158
-        return $tax_total;
1159
-    }
1160
-
1161
-
1162
-    /**
1163
-     * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1164
-     *
1165
-     * @return void
1166
-     * @throws EE_Error
1167
-     */
1168
-    private function _recalculate_tax_sub_total()
1169
-    {
1170
-        if ($this->is_tax_sub_total()) {
1171
-            $total = 0;
1172
-            $total_percent = 0;
1173
-            //simply loop through all its children (which should be taxes) and sum their total
1174
-            foreach ($this->children() as $child_tax) {
1175
-                if ($child_tax instanceof EE_Line_Item) {
1176
-                    $total += $child_tax->total();
1177
-                    $total_percent += $child_tax->percent();
1178
-                }
1179
-            }
1180
-            $this->set_total($total);
1181
-            $this->set_percent($total_percent);
1182
-            $this->maybe_save();
1183
-        } elseif ($this->is_total()) {
1184
-            foreach ($this->children() as $maybe_tax_subtotal) {
1185
-                if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1186
-                    $maybe_tax_subtotal->_recalculate_tax_sub_total();
1187
-                }
1188
-            }
1189
-        }
1190
-    }
1191
-
1192
-
1193
-    /**
1194
-     * Gets the total tax on this line item. Assumes taxes have already been calculated using
1195
-     * recalculate_taxes_and_total
1196
-     *
1197
-     * @return float
1198
-     * @throws EE_Error
1199
-     */
1200
-    public function get_total_tax()
1201
-    {
1202
-        $this->_recalculate_tax_sub_total();
1203
-        $total = 0;
1204
-        foreach ($this->tax_descendants() as $tax_line_item) {
1205
-            if ($tax_line_item instanceof EE_Line_Item) {
1206
-                $total += $tax_line_item->total();
1207
-            }
1208
-        }
1209
-        return $total;
1210
-    }
1211
-
1212
-
1213
-    /**
1214
-     * Gets the total for all the items purchased only
1215
-     *
1216
-     * @return float
1217
-     * @throws EE_Error
1218
-     */
1219
-    public function get_items_total()
1220
-    {
1221
-        //by default, let's make sure we're consistent with the existing line item
1222
-        if ($this->is_total()) {
1223
-            $pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1224
-            if ($pretax_subtotal_li instanceof EE_Line_Item) {
1225
-                return $pretax_subtotal_li->total();
1226
-            }
1227
-        }
1228
-        $total = 0;
1229
-        foreach ($this->get_items() as $item) {
1230
-            if ($item instanceof EE_Line_Item) {
1231
-                $total += $item->total();
1232
-            }
1233
-        }
1234
-        return $total;
1235
-    }
1236
-
1237
-
1238
-    /**
1239
-     * Gets all the descendants (ie, children or children of children etc) that
1240
-     * are of the type 'tax'
1241
-     *
1242
-     * @return EE_Line_Item[]
1243
-     */
1244
-    public function tax_descendants()
1245
-    {
1246
-        return EEH_Line_Item::get_tax_descendants($this);
1247
-    }
1248
-
1249
-
1250
-    /**
1251
-     * Gets all the real items purchased which are children of this item
1252
-     *
1253
-     * @return EE_Line_Item[]
1254
-     */
1255
-    public function get_items()
1256
-    {
1257
-        return EEH_Line_Item::get_line_item_descendants($this);
1258
-    }
1259
-
1260
-
1261
-    /**
1262
-     * Returns the amount taxable among this line item's children (or if it has no children,
1263
-     * how much of it is taxable). Does not recalculate totals or subtotals.
1264
-     * If the taxable total is negative, (eg, if none of the tickets were taxable,
1265
-     * but there is a "Taxable" discount), returns 0.
1266
-     *
1267
-     * @return float
1268
-     * @throws EE_Error
1269
-     */
1270
-    public function taxable_total()
1271
-    {
1272
-        $total = 0;
1273
-        if ($this->children()) {
1274
-            foreach ($this->children() as $child_line_item) {
1275
-                if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1276
-                    //if it's a percent item, only take into account the percent
1277
-                    //that's taxable too (the taxable total so far)
1278
-                    if ($child_line_item->is_percent()) {
1279
-                        $total += ($total * $child_line_item->percent() / 100);
1280
-                    } else {
1281
-                        $total += $child_line_item->total();
1282
-                    }
1283
-                } elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1284
-                    $total += $child_line_item->taxable_total();
1285
-                }
1286
-            }
1287
-        }
1288
-        return max($total, 0);
1289
-    }
1290
-
1291
-
1292
-    /**
1293
-     * Gets the transaction for this line item
1294
-     *
1295
-     * @return EE_Base_Class|EE_Transaction
1296
-     * @throws EE_Error
1297
-     */
1298
-    public function transaction()
1299
-    {
1300
-        return $this->get_first_related('Transaction');
1301
-    }
1302
-
1303
-
1304
-    /**
1305
-     * Saves this line item to the DB, and recursively saves its descendants.
1306
-     * Because there currently is no proper parent-child relation on the model,
1307
-     * save_this_and_cached() will NOT save the descendants.
1308
-     * Also sets the transaction on this line item and all its descendants before saving
1309
-     *
1310
-     * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1311
-     * @return int count of items saved
1312
-     * @throws EE_Error
1313
-     */
1314
-    public function save_this_and_descendants_to_txn($txn_id = null)
1315
-    {
1316
-        $count = 0;
1317
-        if (!$txn_id) {
1318
-            $txn_id = $this->TXN_ID();
1319
-        }
1320
-        $this->set_TXN_ID($txn_id);
1321
-        $children = $this->children();
1322
-        $count += $this->save()
1323
-            ? 1
1324
-            : 0;
1325
-        foreach ($children as $child_line_item) {
1326
-            if ($child_line_item instanceof EE_Line_Item) {
1327
-                $child_line_item->set_parent_ID($this->ID());
1328
-                $count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1329
-            }
1330
-        }
1331
-        return $count;
1332
-    }
1333
-
1334
-
1335
-    /**
1336
-     * Saves this line item to the DB, and recursively saves its descendants.
1337
-     *
1338
-     * @return int count of items saved
1339
-     * @throws EE_Error
1340
-     */
1341
-    public function save_this_and_descendants()
1342
-    {
1343
-        $count = 0;
1344
-        $children = $this->children();
1345
-        $count += $this->save()
1346
-            ? 1
1347
-            : 0;
1348
-        foreach ($children as $child_line_item) {
1349
-            if ($child_line_item instanceof EE_Line_Item) {
1350
-                $child_line_item->set_parent_ID($this->ID());
1351
-                $count += $child_line_item->save_this_and_descendants();
1352
-            }
1353
-        }
1354
-        return $count;
1355
-    }
1356
-
1357
-
1358
-    /**
1359
-     * returns the cancellation line item if this item was cancelled
1360
-     *
1361
-     * @return EE_Line_Item[]
1362
-     * @throws InvalidArgumentException
1363
-     * @throws InvalidInterfaceException
1364
-     * @throws InvalidDataTypeException
1365
-     * @throws ReflectionException
1366
-     * @throws EE_Error
1367
-     */
1368
-    public function get_cancellations()
1369
-    {
1370
-        EE_Registry::instance()->load_helper('Line_Item');
1371
-        return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1372
-    }
1373
-
1374
-
1375
-    /**
1376
-     * If this item has an ID, then this saves it again to update the db
1377
-     *
1378
-     * @return int count of items saved
1379
-     * @throws EE_Error
1380
-     */
1381
-    public function maybe_save()
1382
-    {
1383
-        if ($this->ID()) {
1384
-            return $this->save();
1385
-        }
1386
-        return false;
1387
-    }
1388
-
1389
-
1390
-    /**
1391
-     * clears the cached children and parent from the line item
1392
-     *
1393
-     * @return void
1394
-     */
1395
-    public function clear_related_line_item_cache()
1396
-    {
1397
-        $this->_children = array();
1398
-        $this->_parent = null;
1399
-    }
1400
-
1401
-
1402
-    /**
1403
-     * @param bool $raw
1404
-     * @return int
1405
-     * @throws EE_Error
1406
-     */
1407
-    public function timestamp($raw = false)
1408
-    {
1409
-        return $raw
1410
-            ? $this->get_raw('LIN_timestamp')
1411
-            : $this->get('LIN_timestamp');
1412
-    }
1413
-
1414
-
1415
-
1416
-
1417
-    /************************* DEPRECATED *************************/
1418
-    /**
1419
-     * @deprecated 4.6.0
1420
-     * @param string $type one of the constants on EEM_Line_Item
1421
-     * @return EE_Line_Item[]
1422
-     */
1423
-    protected function _get_descendants_of_type($type)
1424
-    {
1425
-        EE_Error::doing_it_wrong(
1426
-            'EE_Line_Item::_get_descendants_of_type()',
1427
-            __('Method replaced with EEH_Line_Item::get_descendants_of_type()', 'event_espresso'), '4.6.0'
1428
-        );
1429
-        return EEH_Line_Item::get_descendants_of_type($this, $type);
1430
-    }
1431
-
1432
-
1433
-    /**
1434
-     * @deprecated 4.6.0
1435
-     * @param string $type like one of the EEM_Line_Item::type_*
1436
-     * @return EE_Line_Item
1437
-     */
1438
-    public function get_nearest_descendant_of_type($type)
1439
-    {
1440
-        EE_Error::doing_it_wrong(
1441
-            'EE_Line_Item::get_nearest_descendant_of_type()',
1442
-            __('Method replaced with EEH_Line_Item::get_nearest_descendant_of_type()', 'event_espresso'), '4.6.0'
1443
-        );
1444
-        return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1445
-    }
20
+	/**
21
+	 * for children line items (currently not a normal relation)
22
+	 *
23
+	 * @type EE_Line_Item[]
24
+	 */
25
+	protected $_children = array();
26
+
27
+	/**
28
+	 * for the parent line item
29
+	 *
30
+	 * @var EE_Line_Item
31
+	 */
32
+	protected $_parent;
33
+
34
+
35
+	/**
36
+	 *
37
+	 * @param array $props_n_values incoming values
38
+	 * @param string $timezone incoming timezone (if not set the timezone set for the website will be
39
+	 *                                        used.)
40
+	 * @param array $date_formats incoming date_formats in an array where the first value is the
41
+	 *                                        date_format and the second value is the time format
42
+	 * @return EE_Line_Item
43
+	 * @throws EE_Error
44
+	 */
45
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
46
+	{
47
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
48
+		return $has_object
49
+			? $has_object
50
+			: new self($props_n_values, false, $timezone);
51
+	}
52
+
53
+
54
+	/**
55
+	 * @param array $props_n_values incoming values from the database
56
+	 * @param string $timezone incoming timezone as set by the model.  If not set the timezone for
57
+	 *                                the website will be used.
58
+	 * @return EE_Line_Item
59
+	 * @throws EE_Error
60
+	 */
61
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
62
+	{
63
+		return new self($props_n_values, true, $timezone);
64
+	}
65
+
66
+
67
+	/**
68
+	 * Adds some defaults if they're not specified
69
+	 *
70
+	 * @param array $fieldValues
71
+	 * @param bool $bydb
72
+	 * @param string $timezone
73
+	 * @throws EE_Error
74
+	 */
75
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
76
+	{
77
+		parent::__construct($fieldValues, $bydb, $timezone);
78
+		if (!$this->get('LIN_code')) {
79
+			$this->set_code($this->generate_code());
80
+		}
81
+	}
82
+
83
+
84
+	/**
85
+	 * Gets ID
86
+	 *
87
+	 * @return int
88
+	 * @throws EE_Error
89
+	 */
90
+	public function ID()
91
+	{
92
+		return $this->get('LIN_ID');
93
+	}
94
+
95
+
96
+	/**
97
+	 * Gets TXN_ID
98
+	 *
99
+	 * @return int
100
+	 * @throws EE_Error
101
+	 */
102
+	public function TXN_ID()
103
+	{
104
+		return $this->get('TXN_ID');
105
+	}
106
+
107
+
108
+	/**
109
+	 * Sets TXN_ID
110
+	 *
111
+	 * @param int $TXN_ID
112
+	 * @throws EE_Error
113
+	 */
114
+	public function set_TXN_ID($TXN_ID)
115
+	{
116
+		$this->set('TXN_ID', $TXN_ID);
117
+	}
118
+
119
+
120
+	/**
121
+	 * Gets name
122
+	 *
123
+	 * @return string
124
+	 * @throws EE_Error
125
+	 */
126
+	public function name()
127
+	{
128
+		$name = $this->get('LIN_name');
129
+		if (!$name) {
130
+			$name = ucwords(str_replace('-', ' ', $this->type()));
131
+		}
132
+		return $name;
133
+	}
134
+
135
+
136
+	/**
137
+	 * Sets name
138
+	 *
139
+	 * @param string $name
140
+	 * @throws EE_Error
141
+	 */
142
+	public function set_name($name)
143
+	{
144
+		$this->set('LIN_name', $name);
145
+	}
146
+
147
+
148
+	/**
149
+	 * Gets desc
150
+	 *
151
+	 * @return string
152
+	 * @throws EE_Error
153
+	 */
154
+	public function desc()
155
+	{
156
+		return $this->get('LIN_desc');
157
+	}
158
+
159
+
160
+	/**
161
+	 * Sets desc
162
+	 *
163
+	 * @param string $desc
164
+	 * @throws EE_Error
165
+	 */
166
+	public function set_desc($desc)
167
+	{
168
+		$this->set('LIN_desc', $desc);
169
+	}
170
+
171
+
172
+	/**
173
+	 * Gets quantity
174
+	 *
175
+	 * @return int
176
+	 * @throws EE_Error
177
+	 */
178
+	public function quantity()
179
+	{
180
+		return $this->get('LIN_quantity');
181
+	}
182
+
183
+
184
+	/**
185
+	 * Sets quantity
186
+	 *
187
+	 * @param int $quantity
188
+	 * @throws EE_Error
189
+	 */
190
+	public function set_quantity($quantity)
191
+	{
192
+		$this->set('LIN_quantity', max($quantity, 0));
193
+	}
194
+
195
+
196
+	/**
197
+	 * Gets item_id
198
+	 *
199
+	 * @return string
200
+	 * @throws EE_Error
201
+	 */
202
+	public function OBJ_ID()
203
+	{
204
+		return $this->get('OBJ_ID');
205
+	}
206
+
207
+
208
+	/**
209
+	 * Sets item_id
210
+	 *
211
+	 * @param string $item_id
212
+	 * @throws EE_Error
213
+	 */
214
+	public function set_OBJ_ID($item_id)
215
+	{
216
+		$this->set('OBJ_ID', $item_id);
217
+	}
218
+
219
+
220
+	/**
221
+	 * Gets item_type
222
+	 *
223
+	 * @return string
224
+	 * @throws EE_Error
225
+	 */
226
+	public function OBJ_type()
227
+	{
228
+		return $this->get('OBJ_type');
229
+	}
230
+
231
+
232
+	/**
233
+	 * Gets item_type
234
+	 *
235
+	 * @return string
236
+	 * @throws EE_Error
237
+	 */
238
+	public function OBJ_type_i18n()
239
+	{
240
+		$obj_type = $this->OBJ_type();
241
+		switch ($obj_type) {
242
+			case 'Event':
243
+				$obj_type = __('Event', 'event_espresso');
244
+				break;
245
+			case 'Price':
246
+				$obj_type = __('Price', 'event_espresso');
247
+				break;
248
+			case 'Promotion':
249
+				$obj_type = __('Promotion', 'event_espresso');
250
+				break;
251
+			case 'Ticket':
252
+				$obj_type = __('Ticket', 'event_espresso');
253
+				break;
254
+			case 'Transaction':
255
+				$obj_type = __('Transaction', 'event_espresso');
256
+				break;
257
+		}
258
+		return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
259
+	}
260
+
261
+
262
+	/**
263
+	 * Sets item_type
264
+	 *
265
+	 * @param string $OBJ_type
266
+	 * @throws EE_Error
267
+	 */
268
+	public function set_OBJ_type($OBJ_type)
269
+	{
270
+		$this->set('OBJ_type', $OBJ_type);
271
+	}
272
+
273
+
274
+	/**
275
+	 * Gets unit_price
276
+	 *
277
+	 * @return float
278
+	 * @throws EE_Error
279
+	 */
280
+	public function unit_price()
281
+	{
282
+		return $this->get('LIN_unit_price');
283
+	}
284
+
285
+
286
+	/**
287
+	 * Sets unit_price
288
+	 *
289
+	 * @param float $unit_price
290
+	 * @throws EE_Error
291
+	 */
292
+	public function set_unit_price($unit_price)
293
+	{
294
+		$this->set('LIN_unit_price', $unit_price);
295
+	}
296
+
297
+
298
+	/**
299
+	 * Checks if this item is a percentage modifier or not
300
+	 *
301
+	 * @return boolean
302
+	 * @throws EE_Error
303
+	 */
304
+	public function is_percent()
305
+	{
306
+		if ($this->is_tax_sub_total()) {
307
+			//tax subtotals HAVE a percent on them, that percentage only applies
308
+			//to taxable items, so its' an exception. Treat it like a flat line item
309
+			return false;
310
+		}
311
+		$unit_price = abs($this->get('LIN_unit_price'));
312
+		$percent = abs($this->get('LIN_percent'));
313
+		if ($unit_price < .001 && $percent) {
314
+			return true;
315
+		}
316
+		if ($unit_price >= .001 && !$percent) {
317
+			return false;
318
+		}
319
+		if ($unit_price >= .001 && $percent) {
320
+			throw new EE_Error(
321
+				sprintf(
322
+					esc_html__('A Line Item can not have a unit price of (%s) AND a percent (%s)!', 'event_espresso'),
323
+					$unit_price, $percent
324
+				)
325
+			);
326
+		}
327
+		// if they're both 0, assume its not a percent item
328
+		return false;
329
+	}
330
+
331
+
332
+	/**
333
+	 * Gets percent (between 100-.001)
334
+	 *
335
+	 * @return float
336
+	 * @throws EE_Error
337
+	 */
338
+	public function percent()
339
+	{
340
+		return $this->get('LIN_percent');
341
+	}
342
+
343
+
344
+	/**
345
+	 * Sets percent (between 100-0.01)
346
+	 *
347
+	 * @param float $percent
348
+	 * @throws EE_Error
349
+	 */
350
+	public function set_percent($percent)
351
+	{
352
+		$this->set('LIN_percent', $percent);
353
+	}
354
+
355
+
356
+	/**
357
+	 * Gets total
358
+	 *
359
+	 * @return float
360
+	 * @throws EE_Error
361
+	 */
362
+	public function total()
363
+	{
364
+		return $this->get('LIN_total');
365
+	}
366
+
367
+
368
+	/**
369
+	 * Sets total
370
+	 *
371
+	 * @param float $total
372
+	 * @throws EE_Error
373
+	 */
374
+	public function set_total($total)
375
+	{
376
+		$this->set('LIN_total', $total);
377
+	}
378
+
379
+
380
+	/**
381
+	 * Gets order
382
+	 *
383
+	 * @return int
384
+	 * @throws EE_Error
385
+	 */
386
+	public function order()
387
+	{
388
+		return $this->get('LIN_order');
389
+	}
390
+
391
+
392
+	/**
393
+	 * Sets order
394
+	 *
395
+	 * @param int $order
396
+	 * @throws EE_Error
397
+	 */
398
+	public function set_order($order)
399
+	{
400
+		$this->set('LIN_order', $order);
401
+	}
402
+
403
+
404
+	/**
405
+	 * Gets parent
406
+	 *
407
+	 * @return int
408
+	 * @throws EE_Error
409
+	 */
410
+	public function parent_ID()
411
+	{
412
+		return $this->get('LIN_parent');
413
+	}
414
+
415
+
416
+	/**
417
+	 * Sets parent
418
+	 *
419
+	 * @param int $parent
420
+	 * @throws EE_Error
421
+	 */
422
+	public function set_parent_ID($parent)
423
+	{
424
+		$this->set('LIN_parent', $parent);
425
+	}
426
+
427
+
428
+	/**
429
+	 * Gets type
430
+	 *
431
+	 * @return string
432
+	 * @throws EE_Error
433
+	 */
434
+	public function type()
435
+	{
436
+		return $this->get('LIN_type');
437
+	}
438
+
439
+
440
+	/**
441
+	 * Sets type
442
+	 *
443
+	 * @param string $type
444
+	 * @throws EE_Error
445
+	 */
446
+	public function set_type($type)
447
+	{
448
+		$this->set('LIN_type', $type);
449
+	}
450
+
451
+
452
+	/**
453
+	 * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
454
+	 * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
455
+	 * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
456
+	 * or indirectly by `EE_Line_item::add_child_line_item()`)
457
+	 *
458
+	 * @return EE_Base_Class|EE_Line_Item
459
+	 * @throws EE_Error
460
+	 */
461
+	public function parent()
462
+	{
463
+		return $this->ID()
464
+			? $this->get_model()->get_one_by_ID($this->parent_ID())
465
+			: $this->_parent;
466
+	}
467
+
468
+
469
+	/**
470
+	 * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
471
+	 *
472
+	 * @return EE_Base_Class[]|EE_Line_Item[]
473
+	 * @throws EE_Error
474
+	 */
475
+	public function children()
476
+	{
477
+		if ($this->ID()) {
478
+			return $this->get_model()->get_all(
479
+				array(
480
+					array('LIN_parent' => $this->ID()),
481
+					'order_by' => array('LIN_order' => 'ASC'),
482
+				)
483
+			);
484
+		}
485
+		if (!is_array($this->_children)) {
486
+			$this->_children = array();
487
+		}
488
+		return $this->_children;
489
+	}
490
+
491
+
492
+	/**
493
+	 * Gets code
494
+	 *
495
+	 * @return string
496
+	 * @throws EE_Error
497
+	 */
498
+	public function code()
499
+	{
500
+		return $this->get('LIN_code');
501
+	}
502
+
503
+
504
+	/**
505
+	 * Sets code
506
+	 *
507
+	 * @param string $code
508
+	 * @throws EE_Error
509
+	 */
510
+	public function set_code($code)
511
+	{
512
+		$this->set('LIN_code', $code);
513
+	}
514
+
515
+
516
+	/**
517
+	 * Gets is_taxable
518
+	 *
519
+	 * @return boolean
520
+	 * @throws EE_Error
521
+	 */
522
+	public function is_taxable()
523
+	{
524
+		return $this->get('LIN_is_taxable');
525
+	}
526
+
527
+
528
+	/**
529
+	 * Sets is_taxable
530
+	 *
531
+	 * @param boolean $is_taxable
532
+	 * @throws EE_Error
533
+	 */
534
+	public function set_is_taxable($is_taxable)
535
+	{
536
+		$this->set('LIN_is_taxable', $is_taxable);
537
+	}
538
+
539
+
540
+	/**
541
+	 * Gets the object that this model-joins-to.
542
+	 * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
543
+	 * EEM_Promotion_Object
544
+	 *
545
+	 *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
546
+	 *
547
+	 * @return EE_Base_Class | NULL
548
+	 * @throws EE_Error
549
+	 */
550
+	public function get_object()
551
+	{
552
+		$model_name_of_related_obj = $this->OBJ_type();
553
+		return $this->get_model()->has_relation($model_name_of_related_obj)
554
+			? $this->get_first_related($model_name_of_related_obj)
555
+			: null;
556
+	}
557
+
558
+
559
+	/**
560
+	 * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
561
+	 * (IE, if this line item is for a price or something else, will return NULL)
562
+	 *
563
+	 * @param array $query_params
564
+	 * @return EE_Base_Class|EE_Ticket
565
+	 * @throws EE_Error
566
+	 */
567
+	public function ticket($query_params = array())
568
+	{
569
+		//we're going to assume that when this method is called we always want to receive the attached ticket EVEN if that ticket is archived.  This can be overridden via the incoming $query_params argument
570
+		$remove_defaults = array('default_where_conditions' => 'none');
571
+		$query_params = array_merge($remove_defaults, $query_params);
572
+		return $this->get_first_related('Ticket', $query_params);
573
+	}
574
+
575
+
576
+	/**
577
+	 * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
578
+	 *
579
+	 * @return EE_Datetime | NULL
580
+	 * @throws EE_Error
581
+	 */
582
+	public function get_ticket_datetime()
583
+	{
584
+		if ($this->OBJ_type() === 'Ticket') {
585
+			$ticket = $this->ticket();
586
+			if ($ticket instanceof EE_Ticket) {
587
+				$datetime = $ticket->first_datetime();
588
+				if ($datetime instanceof EE_Datetime) {
589
+					return $datetime;
590
+				}
591
+			}
592
+		}
593
+		return null;
594
+	}
595
+
596
+
597
+	/**
598
+	 * Gets the event's name that's related to the ticket, if this is for
599
+	 * a ticket
600
+	 *
601
+	 * @return string
602
+	 * @throws EE_Error
603
+	 */
604
+	public function ticket_event_name()
605
+	{
606
+		$event_name = esc_html__('Unknown', 'event_espresso');
607
+		$event = $this->ticket_event();
608
+		if ($event instanceof EE_Event) {
609
+			$event_name = $event->name();
610
+		}
611
+		return $event_name;
612
+	}
613
+
614
+
615
+	/**
616
+	 * Gets the event that's related to the ticket, if this line item represents a ticket.
617
+	 *
618
+	 * @return EE_Event|null
619
+	 * @throws EE_Error
620
+	 */
621
+	public function ticket_event()
622
+	{
623
+		$event = null;
624
+		$ticket = $this->ticket();
625
+		if ($ticket instanceof EE_Ticket) {
626
+			$datetime = $ticket->first_datetime();
627
+			if ($datetime instanceof EE_Datetime) {
628
+				$event = $datetime->event();
629
+			}
630
+		}
631
+		return $event;
632
+	}
633
+
634
+
635
+	/**
636
+	 * Gets the first datetime for this lien item, assuming it's for a ticket
637
+	 *
638
+	 * @param string $date_format
639
+	 * @param string $time_format
640
+	 * @return string
641
+	 * @throws EE_Error
642
+	 */
643
+	public function ticket_datetime_start($date_format = '', $time_format = '')
644
+	{
645
+		$first_datetime_string = esc_html__('Unknown', 'event_espresso');
646
+		$datetime = $this->get_ticket_datetime();
647
+		if ($datetime) {
648
+			$first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
649
+		}
650
+		return $first_datetime_string;
651
+	}
652
+
653
+
654
+	/**
655
+	 * Adds the line item as a child to this line item. If there is another child line
656
+	 * item with the same LIN_code, it is overwritten by this new one
657
+	 *
658
+	 * @param EEI_Line_Item $line_item
659
+	 * @param bool $set_order
660
+	 * @return bool success
661
+	 * @throws EE_Error
662
+	 */
663
+	public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
664
+	{
665
+		// should we calculate the LIN_order for this line item ?
666
+		if ($set_order || $line_item->order() === null) {
667
+			$line_item->set_order(count($this->children()));
668
+		}
669
+		if ($this->ID()) {
670
+			//check for any duplicate line items (with the same code), if so, this replaces it
671
+			$line_item_with_same_code = $this->get_child_line_item($line_item->code());
672
+			if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
673
+				$this->delete_child_line_item($line_item_with_same_code->code());
674
+			}
675
+			$line_item->set_parent_ID($this->ID());
676
+			if ($this->TXN_ID()) {
677
+				$line_item->set_TXN_ID($this->TXN_ID());
678
+			}
679
+			return $line_item->save();
680
+		}
681
+		$this->_children[$line_item->code()] = $line_item;
682
+		if ($line_item->parent() !== $this) {
683
+			$line_item->set_parent($this);
684
+		}
685
+		return true;
686
+	}
687
+
688
+
689
+	/**
690
+	 * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
691
+	 * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
692
+	 * However, if this line item is NOT saved to the DB, this just caches the parent on
693
+	 * the EE_Line_Item::_parent property.
694
+	 *
695
+	 * @param EE_Line_Item $line_item
696
+	 * @throws EE_Error
697
+	 */
698
+	public function set_parent($line_item)
699
+	{
700
+		if ($this->ID()) {
701
+			if (!$line_item->ID()) {
702
+				$line_item->save();
703
+			}
704
+			$this->set_parent_ID($line_item->ID());
705
+			$this->save();
706
+		} else {
707
+			$this->_parent = $line_item;
708
+			$this->set_parent_ID($line_item->ID());
709
+		}
710
+	}
711
+
712
+
713
+	/**
714
+	 * Gets the child line item as specified by its code. Because this returns an object (by reference)
715
+	 * you can modify this child line item and the parent (this object) can know about them
716
+	 * because it also has a reference to that line item
717
+	 *
718
+	 * @param string $code
719
+	 * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
720
+	 * @throws EE_Error
721
+	 */
722
+	public function get_child_line_item($code)
723
+	{
724
+		if ($this->ID()) {
725
+			return $this->get_model()->get_one(
726
+				array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
727
+			);
728
+		}
729
+		return isset($this->_children[$code])
730
+			? $this->_children[$code]
731
+			: null;
732
+	}
733
+
734
+
735
+	/**
736
+	 * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
737
+	 * cached on it)
738
+	 *
739
+	 * @return int
740
+	 * @throws EE_Error
741
+	 */
742
+	public function delete_children_line_items()
743
+	{
744
+		if ($this->ID()) {
745
+			return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
746
+		}
747
+		$count = count($this->_children);
748
+		$this->_children = array();
749
+		return $count;
750
+	}
751
+
752
+
753
+	/**
754
+	 * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
755
+	 * HAS NOT been saved to the DB, removes the child line item with index $code.
756
+	 * Also searches through the child's children for a matching line item. However, once a line item has been found
757
+	 * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
758
+	 * deleted)
759
+	 *
760
+	 * @param string $code
761
+	 * @param bool $stop_search_once_found
762
+	 * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
763
+	 *             the DB yet)
764
+	 * @throws EE_Error
765
+	 */
766
+	public function delete_child_line_item($code, $stop_search_once_found = true)
767
+	{
768
+		if ($this->ID()) {
769
+			$items_deleted = 0;
770
+			if ($this->code() === $code) {
771
+				$items_deleted += EEH_Line_Item::delete_all_child_items($this);
772
+				$items_deleted += (int)$this->delete();
773
+				if ($stop_search_once_found) {
774
+					return $items_deleted;
775
+				}
776
+			}
777
+			foreach ($this->children() as $child_line_item) {
778
+				$items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
779
+			}
780
+			return $items_deleted;
781
+		}
782
+		if (isset($this->_children[$code])) {
783
+			unset($this->_children[$code]);
784
+			return 1;
785
+		}
786
+		return 0;
787
+	}
788
+
789
+
790
+	/**
791
+	 * If this line item is in the database, is of the type subtotal, and
792
+	 * has no children, why do we have it? It should be deleted so this function
793
+	 * does that
794
+	 *
795
+	 * @return boolean
796
+	 * @throws EE_Error
797
+	 */
798
+	public function delete_if_childless_subtotal()
799
+	{
800
+		if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && !$this->children()) {
801
+			return $this->delete();
802
+		}
803
+		return false;
804
+	}
805
+
806
+
807
+	/**
808
+	 * Creates a code and returns a string. doesn't assign the code to this model object
809
+	 *
810
+	 * @return string
811
+	 * @throws EE_Error
812
+	 */
813
+	public function generate_code()
814
+	{
815
+		// each line item in the cart requires a unique identifier
816
+		return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
817
+	}
818
+
819
+
820
+	/**
821
+	 * @return bool
822
+	 * @throws EE_Error
823
+	 */
824
+	public function is_tax()
825
+	{
826
+		return $this->type() === EEM_Line_Item::type_tax;
827
+	}
828
+
829
+
830
+	/**
831
+	 * @return bool
832
+	 * @throws EE_Error
833
+	 */
834
+	public function is_tax_sub_total()
835
+	{
836
+		return $this->type() === EEM_Line_Item::type_tax_sub_total;
837
+	}
838
+
839
+
840
+	/**
841
+	 * @return bool
842
+	 * @throws EE_Error
843
+	 */
844
+	public function is_line_item()
845
+	{
846
+		return $this->type() === EEM_Line_Item::type_line_item;
847
+	}
848
+
849
+
850
+	/**
851
+	 * @return bool
852
+	 * @throws EE_Error
853
+	 */
854
+	public function is_sub_line_item()
855
+	{
856
+		return $this->type() === EEM_Line_Item::type_sub_line_item;
857
+	}
858
+
859
+
860
+	/**
861
+	 * @return bool
862
+	 * @throws EE_Error
863
+	 */
864
+	public function is_sub_total()
865
+	{
866
+		return $this->type() === EEM_Line_Item::type_sub_total;
867
+	}
868
+
869
+
870
+	/**
871
+	 * Whether or not this line item is a cancellation line item
872
+	 *
873
+	 * @return boolean
874
+	 * @throws EE_Error
875
+	 */
876
+	public function is_cancellation()
877
+	{
878
+		return EEM_Line_Item::type_cancellation === $this->type();
879
+	}
880
+
881
+
882
+	/**
883
+	 * @return bool
884
+	 * @throws EE_Error
885
+	 */
886
+	public function is_total()
887
+	{
888
+		return $this->type() === EEM_Line_Item::type_total;
889
+	}
890
+
891
+
892
+	/**
893
+	 * @return bool
894
+	 * @throws EE_Error
895
+	 */
896
+	public function is_cancelled()
897
+	{
898
+		return $this->type() === EEM_Line_Item::type_cancellation;
899
+	}
900
+
901
+
902
+	/**
903
+	 * @return string like '2, 004.00', formatted according to the localized currency
904
+	 * @throws EE_Error
905
+	 */
906
+	public function unit_price_no_code()
907
+	{
908
+		return $this->get_pretty('LIN_unit_price', 'no_currency_code');
909
+	}
910
+
911
+
912
+	/**
913
+	 * @return string like '2, 004.00', formatted according to the localized currency
914
+	 * @throws EE_Error
915
+	 */
916
+	public function total_no_code()
917
+	{
918
+		return $this->get_pretty('LIN_total', 'no_currency_code');
919
+	}
920
+
921
+
922
+	/**
923
+	 * Gets the final total on this item, taking taxes into account.
924
+	 * Has the side-effect of setting the sub-total as it was just calculated.
925
+	 * If this is used on a grand-total line item, also updates the transaction's
926
+	 * TXN_total (provided this line item is allowed to persist, otherwise we don't
927
+	 * want to change a persistable transaction with info from a non-persistent line item)
928
+	 *
929
+	 * @return float
930
+	 * @throws EE_Error
931
+	 * @throws InvalidArgumentException
932
+	 * @throws InvalidInterfaceException
933
+	 * @throws InvalidDataTypeException
934
+	 */
935
+	public function recalculate_total_including_taxes()
936
+	{
937
+		$pre_tax_total = $this->recalculate_pre_tax_total();
938
+		$tax_total = $this->recalculate_taxes_and_tax_total();
939
+		$total = $pre_tax_total + $tax_total;
940
+		// no negative totals plz
941
+		$total = max($total, 0);
942
+		$this->set_total($total);
943
+		//only update the related transaction's total
944
+		//if we intend to save this line item and its a grand total
945
+		if (
946
+			$this->allow_persist() && $this->type() === EEM_Line_Item::type_total
947
+			&& $this->transaction()
948
+			instanceof
949
+			EE_Transaction
950
+		) {
951
+			$this->transaction()->set_total($total);
952
+			if ($this->transaction()->ID()) {
953
+				$this->transaction()->save();
954
+			}
955
+		}
956
+		$this->maybe_save();
957
+		return $total;
958
+	}
959
+
960
+
961
+	/**
962
+	 * Recursively goes through all the children and recalculates sub-totals EXCEPT for
963
+	 * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
964
+	 * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
965
+	 * when this is called on the grand total
966
+	 *
967
+	 * @return float
968
+	 * @throws InvalidArgumentException
969
+	 * @throws InvalidInterfaceException
970
+	 * @throws InvalidDataTypeException
971
+	 * @throws EE_Error
972
+	 */
973
+	public function recalculate_pre_tax_total()
974
+	{
975
+		$total = 0;
976
+		$my_children = $this->children();
977
+		$has_children = !empty($my_children);
978
+		if ($has_children && $this->is_line_item()) {
979
+			$total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
980
+		} elseif (!$has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
981
+			$total = $this->unit_price() * $this->quantity();
982
+		} elseif ($this->is_sub_total() || $this->is_total()) {
983
+			$total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
984
+		} elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
985
+			// completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
986
+			return 0;
987
+		}
988
+		// ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
989
+		if (
990
+			!$this->is_line_item() && !$this->is_sub_line_item() && !$this->is_cancellation()
991
+		) {
992
+			if ($this->OBJ_type() !== 'Event') {
993
+				$this->set_quantity(1);
994
+			}
995
+			if (!$this->is_percent()) {
996
+				$this->set_unit_price($total);
997
+			}
998
+		}
999
+		//we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1000
+		//so it ought to be
1001
+		if (!$this->is_total()) {
1002
+			$this->set_total($total);
1003
+			//if not a percent line item, make sure we keep the unit price in sync
1004
+			if (
1005
+				$has_children
1006
+				&& $this->is_line_item()
1007
+				&& !$this->is_percent()
1008
+			) {
1009
+				if ($this->quantity() === 0) {
1010
+					$new_unit_price = 0;
1011
+				} else {
1012
+					$new_unit_price = $this->total() / $this->quantity();
1013
+				}
1014
+				$this->set_unit_price($new_unit_price);
1015
+			}
1016
+			$this->maybe_save();
1017
+		}
1018
+		return $total;
1019
+	}
1020
+
1021
+
1022
+	/**
1023
+	 * Calculates the pretax total when this line item is a subtotal or total line item.
1024
+	 * Basically does a sum-then-round approach (ie, any percent line item that are children
1025
+	 * will calculate their total based on the un-rounded total we're working with so far, and
1026
+	 * THEN round the result; instead of rounding as we go like with sub-line-items)
1027
+	 *
1028
+	 * @param float $calculated_total_so_far
1029
+	 * @param EE_Line_Item[] $my_children
1030
+	 * @return float
1031
+	 * @throws InvalidArgumentException
1032
+	 * @throws InvalidInterfaceException
1033
+	 * @throws InvalidDataTypeException
1034
+	 * @throws EE_Error
1035
+	 */
1036
+	protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1037
+	{
1038
+		if ($my_children === null) {
1039
+			$my_children = $this->children();
1040
+		}
1041
+		$subtotal_quantity = 0;
1042
+		//get the total of all its children
1043
+		foreach ($my_children as $child_line_item) {
1044
+			if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1045
+				// percentage line items are based on total so far
1046
+				if ($child_line_item->is_percent()) {
1047
+					//round as we go so that the line items add up ok
1048
+					$percent_total = round(
1049
+						$calculated_total_so_far * $child_line_item->percent() / 100,
1050
+						EE_Registry::instance()->CFG->currency->dec_plc
1051
+					);
1052
+					$child_line_item->set_total($percent_total);
1053
+					//so far all percent line items should have a quantity of 1
1054
+					//(ie, no double percent discounts. Although that might be requested someday)
1055
+					$child_line_item->set_quantity(1);
1056
+					$child_line_item->maybe_save();
1057
+					$calculated_total_so_far += $percent_total;
1058
+				} else {
1059
+					//verify flat sub-line-item quantities match their parent
1060
+					if ($child_line_item->is_sub_line_item()) {
1061
+						$child_line_item->set_quantity($this->quantity());
1062
+					}
1063
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1064
+					$subtotal_quantity += $child_line_item->quantity();
1065
+				}
1066
+			}
1067
+		}
1068
+		if ($this->is_sub_total()) {
1069
+			// no negative totals plz
1070
+			$calculated_total_so_far = max($calculated_total_so_far, 0);
1071
+			$subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1072
+			$this->set_quantity($subtotal_quantity);
1073
+			$this->maybe_save();
1074
+		}
1075
+		return $calculated_total_so_far;
1076
+	}
1077
+
1078
+
1079
+	/**
1080
+	 * Calculates the pretax total for a normal line item, in a round-then-sum approach
1081
+	 * (where each sub-line-item is applied to the base price for the line item
1082
+	 * and the result is immediately rounded, rather than summing all the sub-line-items
1083
+	 * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1084
+	 *
1085
+	 * @param float $calculated_total_so_far
1086
+	 * @param EE_Line_Item[] $my_children
1087
+	 * @return float
1088
+	 * @throws InvalidArgumentException
1089
+	 * @throws InvalidInterfaceException
1090
+	 * @throws InvalidDataTypeException
1091
+	 * @throws EE_Error
1092
+	 */
1093
+	protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1094
+	{
1095
+		if ($my_children === null) {
1096
+			$my_children = $this->children();
1097
+		}
1098
+		//we need to keep track of the running total for a single item,
1099
+		//because we need to round as we go
1100
+		$unit_price_for_total = 0;
1101
+		$quantity_for_total = 1;
1102
+		//get the total of all its children
1103
+		foreach ($my_children as $child_line_item) {
1104
+			if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1105
+				if ($child_line_item->is_percent()) {
1106
+					//it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1107
+					//not total multiplied by percent, because that ignores rounding along-the-way
1108
+					$percent_unit_price = round(
1109
+						$unit_price_for_total * $child_line_item->percent() / 100,
1110
+						EE_Registry::instance()->CFG->currency->dec_plc
1111
+					);
1112
+					$percent_total = $percent_unit_price * $quantity_for_total;
1113
+					$child_line_item->set_total($percent_total);
1114
+					//so far all percent line items should have a quantity of 1
1115
+					//(ie, no double percent discounts. Although that might be requested someday)
1116
+					$child_line_item->set_quantity(1);
1117
+					$child_line_item->maybe_save();
1118
+					$calculated_total_so_far += $percent_total;
1119
+					$unit_price_for_total += $percent_unit_price;
1120
+				} else {
1121
+					//verify flat sub-line-item quantities match their parent
1122
+					if ($child_line_item->is_sub_line_item()) {
1123
+						$child_line_item->set_quantity($this->quantity());
1124
+					}
1125
+					$quantity_for_total = $child_line_item->quantity();
1126
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1127
+					$unit_price_for_total += $child_line_item->unit_price();
1128
+				}
1129
+			}
1130
+		}
1131
+		return $calculated_total_so_far;
1132
+	}
1133
+
1134
+
1135
+	/**
1136
+	 * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1137
+	 * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1138
+	 * and tax sub-total if already in the DB
1139
+	 *
1140
+	 * @return float
1141
+	 * @throws EE_Error
1142
+	 */
1143
+	public function recalculate_taxes_and_tax_total()
1144
+	{
1145
+		//get all taxes
1146
+		$taxes = $this->tax_descendants();
1147
+		//calculate the pretax total
1148
+		$taxable_total = $this->taxable_total();
1149
+		$tax_total = 0;
1150
+		foreach ($taxes as $tax) {
1151
+			$total_on_this_tax = $taxable_total * $tax->percent() / 100;
1152
+			//remember the total on this line item
1153
+			$tax->set_total($total_on_this_tax);
1154
+			$tax->maybe_save();
1155
+			$tax_total += $tax->total();
1156
+		}
1157
+		$this->_recalculate_tax_sub_total();
1158
+		return $tax_total;
1159
+	}
1160
+
1161
+
1162
+	/**
1163
+	 * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1164
+	 *
1165
+	 * @return void
1166
+	 * @throws EE_Error
1167
+	 */
1168
+	private function _recalculate_tax_sub_total()
1169
+	{
1170
+		if ($this->is_tax_sub_total()) {
1171
+			$total = 0;
1172
+			$total_percent = 0;
1173
+			//simply loop through all its children (which should be taxes) and sum their total
1174
+			foreach ($this->children() as $child_tax) {
1175
+				if ($child_tax instanceof EE_Line_Item) {
1176
+					$total += $child_tax->total();
1177
+					$total_percent += $child_tax->percent();
1178
+				}
1179
+			}
1180
+			$this->set_total($total);
1181
+			$this->set_percent($total_percent);
1182
+			$this->maybe_save();
1183
+		} elseif ($this->is_total()) {
1184
+			foreach ($this->children() as $maybe_tax_subtotal) {
1185
+				if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1186
+					$maybe_tax_subtotal->_recalculate_tax_sub_total();
1187
+				}
1188
+			}
1189
+		}
1190
+	}
1191
+
1192
+
1193
+	/**
1194
+	 * Gets the total tax on this line item. Assumes taxes have already been calculated using
1195
+	 * recalculate_taxes_and_total
1196
+	 *
1197
+	 * @return float
1198
+	 * @throws EE_Error
1199
+	 */
1200
+	public function get_total_tax()
1201
+	{
1202
+		$this->_recalculate_tax_sub_total();
1203
+		$total = 0;
1204
+		foreach ($this->tax_descendants() as $tax_line_item) {
1205
+			if ($tax_line_item instanceof EE_Line_Item) {
1206
+				$total += $tax_line_item->total();
1207
+			}
1208
+		}
1209
+		return $total;
1210
+	}
1211
+
1212
+
1213
+	/**
1214
+	 * Gets the total for all the items purchased only
1215
+	 *
1216
+	 * @return float
1217
+	 * @throws EE_Error
1218
+	 */
1219
+	public function get_items_total()
1220
+	{
1221
+		//by default, let's make sure we're consistent with the existing line item
1222
+		if ($this->is_total()) {
1223
+			$pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1224
+			if ($pretax_subtotal_li instanceof EE_Line_Item) {
1225
+				return $pretax_subtotal_li->total();
1226
+			}
1227
+		}
1228
+		$total = 0;
1229
+		foreach ($this->get_items() as $item) {
1230
+			if ($item instanceof EE_Line_Item) {
1231
+				$total += $item->total();
1232
+			}
1233
+		}
1234
+		return $total;
1235
+	}
1236
+
1237
+
1238
+	/**
1239
+	 * Gets all the descendants (ie, children or children of children etc) that
1240
+	 * are of the type 'tax'
1241
+	 *
1242
+	 * @return EE_Line_Item[]
1243
+	 */
1244
+	public function tax_descendants()
1245
+	{
1246
+		return EEH_Line_Item::get_tax_descendants($this);
1247
+	}
1248
+
1249
+
1250
+	/**
1251
+	 * Gets all the real items purchased which are children of this item
1252
+	 *
1253
+	 * @return EE_Line_Item[]
1254
+	 */
1255
+	public function get_items()
1256
+	{
1257
+		return EEH_Line_Item::get_line_item_descendants($this);
1258
+	}
1259
+
1260
+
1261
+	/**
1262
+	 * Returns the amount taxable among this line item's children (or if it has no children,
1263
+	 * how much of it is taxable). Does not recalculate totals or subtotals.
1264
+	 * If the taxable total is negative, (eg, if none of the tickets were taxable,
1265
+	 * but there is a "Taxable" discount), returns 0.
1266
+	 *
1267
+	 * @return float
1268
+	 * @throws EE_Error
1269
+	 */
1270
+	public function taxable_total()
1271
+	{
1272
+		$total = 0;
1273
+		if ($this->children()) {
1274
+			foreach ($this->children() as $child_line_item) {
1275
+				if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1276
+					//if it's a percent item, only take into account the percent
1277
+					//that's taxable too (the taxable total so far)
1278
+					if ($child_line_item->is_percent()) {
1279
+						$total += ($total * $child_line_item->percent() / 100);
1280
+					} else {
1281
+						$total += $child_line_item->total();
1282
+					}
1283
+				} elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1284
+					$total += $child_line_item->taxable_total();
1285
+				}
1286
+			}
1287
+		}
1288
+		return max($total, 0);
1289
+	}
1290
+
1291
+
1292
+	/**
1293
+	 * Gets the transaction for this line item
1294
+	 *
1295
+	 * @return EE_Base_Class|EE_Transaction
1296
+	 * @throws EE_Error
1297
+	 */
1298
+	public function transaction()
1299
+	{
1300
+		return $this->get_first_related('Transaction');
1301
+	}
1302
+
1303
+
1304
+	/**
1305
+	 * Saves this line item to the DB, and recursively saves its descendants.
1306
+	 * Because there currently is no proper parent-child relation on the model,
1307
+	 * save_this_and_cached() will NOT save the descendants.
1308
+	 * Also sets the transaction on this line item and all its descendants before saving
1309
+	 *
1310
+	 * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1311
+	 * @return int count of items saved
1312
+	 * @throws EE_Error
1313
+	 */
1314
+	public function save_this_and_descendants_to_txn($txn_id = null)
1315
+	{
1316
+		$count = 0;
1317
+		if (!$txn_id) {
1318
+			$txn_id = $this->TXN_ID();
1319
+		}
1320
+		$this->set_TXN_ID($txn_id);
1321
+		$children = $this->children();
1322
+		$count += $this->save()
1323
+			? 1
1324
+			: 0;
1325
+		foreach ($children as $child_line_item) {
1326
+			if ($child_line_item instanceof EE_Line_Item) {
1327
+				$child_line_item->set_parent_ID($this->ID());
1328
+				$count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1329
+			}
1330
+		}
1331
+		return $count;
1332
+	}
1333
+
1334
+
1335
+	/**
1336
+	 * Saves this line item to the DB, and recursively saves its descendants.
1337
+	 *
1338
+	 * @return int count of items saved
1339
+	 * @throws EE_Error
1340
+	 */
1341
+	public function save_this_and_descendants()
1342
+	{
1343
+		$count = 0;
1344
+		$children = $this->children();
1345
+		$count += $this->save()
1346
+			? 1
1347
+			: 0;
1348
+		foreach ($children as $child_line_item) {
1349
+			if ($child_line_item instanceof EE_Line_Item) {
1350
+				$child_line_item->set_parent_ID($this->ID());
1351
+				$count += $child_line_item->save_this_and_descendants();
1352
+			}
1353
+		}
1354
+		return $count;
1355
+	}
1356
+
1357
+
1358
+	/**
1359
+	 * returns the cancellation line item if this item was cancelled
1360
+	 *
1361
+	 * @return EE_Line_Item[]
1362
+	 * @throws InvalidArgumentException
1363
+	 * @throws InvalidInterfaceException
1364
+	 * @throws InvalidDataTypeException
1365
+	 * @throws ReflectionException
1366
+	 * @throws EE_Error
1367
+	 */
1368
+	public function get_cancellations()
1369
+	{
1370
+		EE_Registry::instance()->load_helper('Line_Item');
1371
+		return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1372
+	}
1373
+
1374
+
1375
+	/**
1376
+	 * If this item has an ID, then this saves it again to update the db
1377
+	 *
1378
+	 * @return int count of items saved
1379
+	 * @throws EE_Error
1380
+	 */
1381
+	public function maybe_save()
1382
+	{
1383
+		if ($this->ID()) {
1384
+			return $this->save();
1385
+		}
1386
+		return false;
1387
+	}
1388
+
1389
+
1390
+	/**
1391
+	 * clears the cached children and parent from the line item
1392
+	 *
1393
+	 * @return void
1394
+	 */
1395
+	public function clear_related_line_item_cache()
1396
+	{
1397
+		$this->_children = array();
1398
+		$this->_parent = null;
1399
+	}
1400
+
1401
+
1402
+	/**
1403
+	 * @param bool $raw
1404
+	 * @return int
1405
+	 * @throws EE_Error
1406
+	 */
1407
+	public function timestamp($raw = false)
1408
+	{
1409
+		return $raw
1410
+			? $this->get_raw('LIN_timestamp')
1411
+			: $this->get('LIN_timestamp');
1412
+	}
1413
+
1414
+
1415
+
1416
+
1417
+	/************************* DEPRECATED *************************/
1418
+	/**
1419
+	 * @deprecated 4.6.0
1420
+	 * @param string $type one of the constants on EEM_Line_Item
1421
+	 * @return EE_Line_Item[]
1422
+	 */
1423
+	protected function _get_descendants_of_type($type)
1424
+	{
1425
+		EE_Error::doing_it_wrong(
1426
+			'EE_Line_Item::_get_descendants_of_type()',
1427
+			__('Method replaced with EEH_Line_Item::get_descendants_of_type()', 'event_espresso'), '4.6.0'
1428
+		);
1429
+		return EEH_Line_Item::get_descendants_of_type($this, $type);
1430
+	}
1431
+
1432
+
1433
+	/**
1434
+	 * @deprecated 4.6.0
1435
+	 * @param string $type like one of the EEM_Line_Item::type_*
1436
+	 * @return EE_Line_Item
1437
+	 */
1438
+	public function get_nearest_descendant_of_type($type)
1439
+	{
1440
+		EE_Error::doing_it_wrong(
1441
+			'EE_Line_Item::get_nearest_descendant_of_type()',
1442
+			__('Method replaced with EEH_Line_Item::get_nearest_descendant_of_type()', 'event_espresso'), '4.6.0'
1443
+		);
1444
+		return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1445
+	}
1446 1446
 
1447 1447
 
1448 1448
 }
Please login to merge, or discard this patch.
Spacing   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -75,7 +75,7 @@  discard block
 block discarded – undo
75 75
     protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
76 76
     {
77 77
         parent::__construct($fieldValues, $bydb, $timezone);
78
-        if (!$this->get('LIN_code')) {
78
+        if ( ! $this->get('LIN_code')) {
79 79
             $this->set_code($this->generate_code());
80 80
         }
81 81
     }
@@ -126,7 +126,7 @@  discard block
 block discarded – undo
126 126
     public function name()
127 127
     {
128 128
         $name = $this->get('LIN_name');
129
-        if (!$name) {
129
+        if ( ! $name) {
130 130
             $name = ucwords(str_replace('-', ' ', $this->type()));
131 131
         }
132 132
         return $name;
@@ -313,7 +313,7 @@  discard block
 block discarded – undo
313 313
         if ($unit_price < .001 && $percent) {
314 314
             return true;
315 315
         }
316
-        if ($unit_price >= .001 && !$percent) {
316
+        if ($unit_price >= .001 && ! $percent) {
317 317
             return false;
318 318
         }
319 319
         if ($unit_price >= .001 && $percent) {
@@ -482,7 +482,7 @@  discard block
 block discarded – undo
482 482
                 )
483 483
             );
484 484
         }
485
-        if (!is_array($this->_children)) {
485
+        if ( ! is_array($this->_children)) {
486 486
             $this->_children = array();
487 487
         }
488 488
         return $this->_children;
@@ -698,7 +698,7 @@  discard block
 block discarded – undo
698 698
     public function set_parent($line_item)
699 699
     {
700 700
         if ($this->ID()) {
701
-            if (!$line_item->ID()) {
701
+            if ( ! $line_item->ID()) {
702 702
                 $line_item->save();
703 703
             }
704 704
             $this->set_parent_ID($line_item->ID());
@@ -769,7 +769,7 @@  discard block
 block discarded – undo
769 769
             $items_deleted = 0;
770 770
             if ($this->code() === $code) {
771 771
                 $items_deleted += EEH_Line_Item::delete_all_child_items($this);
772
-                $items_deleted += (int)$this->delete();
772
+                $items_deleted += (int) $this->delete();
773 773
                 if ($stop_search_once_found) {
774 774
                     return $items_deleted;
775 775
                 }
@@ -797,7 +797,7 @@  discard block
 block discarded – undo
797 797
      */
798 798
     public function delete_if_childless_subtotal()
799 799
     {
800
-        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && !$this->children()) {
800
+        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
801 801
             return $this->delete();
802 802
         }
803 803
         return false;
@@ -813,7 +813,7 @@  discard block
 block discarded – undo
813 813
     public function generate_code()
814 814
     {
815 815
         // each line item in the cart requires a unique identifier
816
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
816
+        return md5($this->get('OBJ_type').$this->get('OBJ_ID').microtime());
817 817
     }
818 818
 
819 819
 
@@ -974,10 +974,10 @@  discard block
 block discarded – undo
974 974
     {
975 975
         $total = 0;
976 976
         $my_children = $this->children();
977
-        $has_children = !empty($my_children);
977
+        $has_children = ! empty($my_children);
978 978
         if ($has_children && $this->is_line_item()) {
979 979
             $total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
980
-        } elseif (!$has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
980
+        } elseif ( ! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
981 981
             $total = $this->unit_price() * $this->quantity();
982 982
         } elseif ($this->is_sub_total() || $this->is_total()) {
983 983
             $total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
@@ -987,24 +987,24 @@  discard block
 block discarded – undo
987 987
         }
988 988
         // ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
989 989
         if (
990
-            !$this->is_line_item() && !$this->is_sub_line_item() && !$this->is_cancellation()
990
+            ! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()
991 991
         ) {
992 992
             if ($this->OBJ_type() !== 'Event') {
993 993
                 $this->set_quantity(1);
994 994
             }
995
-            if (!$this->is_percent()) {
995
+            if ( ! $this->is_percent()) {
996 996
                 $this->set_unit_price($total);
997 997
             }
998 998
         }
999 999
         //we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1000 1000
         //so it ought to be
1001
-        if (!$this->is_total()) {
1001
+        if ( ! $this->is_total()) {
1002 1002
             $this->set_total($total);
1003 1003
             //if not a percent line item, make sure we keep the unit price in sync
1004 1004
             if (
1005 1005
                 $has_children
1006 1006
                 && $this->is_line_item()
1007
-                && !$this->is_percent()
1007
+                && ! $this->is_percent()
1008 1008
             ) {
1009 1009
                 if ($this->quantity() === 0) {
1010 1010
                     $new_unit_price = 0;
@@ -1041,7 +1041,7 @@  discard block
 block discarded – undo
1041 1041
         $subtotal_quantity = 0;
1042 1042
         //get the total of all its children
1043 1043
         foreach ($my_children as $child_line_item) {
1044
-            if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1044
+            if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1045 1045
                 // percentage line items are based on total so far
1046 1046
                 if ($child_line_item->is_percent()) {
1047 1047
                     //round as we go so that the line items add up ok
@@ -1101,7 +1101,7 @@  discard block
 block discarded – undo
1101 1101
         $quantity_for_total = 1;
1102 1102
         //get the total of all its children
1103 1103
         foreach ($my_children as $child_line_item) {
1104
-            if ($child_line_item instanceof EE_Line_Item && !$child_line_item->is_cancellation()) {
1104
+            if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1105 1105
                 if ($child_line_item->is_percent()) {
1106 1106
                     //it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1107 1107
                     //not total multiplied by percent, because that ignores rounding along-the-way
@@ -1314,7 +1314,7 @@  discard block
 block discarded – undo
1314 1314
     public function save_this_and_descendants_to_txn($txn_id = null)
1315 1315
     {
1316 1316
         $count = 0;
1317
-        if (!$txn_id) {
1317
+        if ( ! $txn_id) {
1318 1318
             $txn_id = $this->TXN_ID();
1319 1319
         }
1320 1320
         $this->set_TXN_ID($txn_id);
Please login to merge, or discard this patch.
attendee_information/EE_SPCO_Reg_Step_Attendee_Information.class.php 3 patches
Doc Comments   +2 added lines, -1 removed lines patch added patch discarded remove patch
@@ -211,7 +211,7 @@  discard block
 block discarded – undo
211 211
 
212 212
     /**
213 213
      * @param EE_Registration $registration
214
-     * @return EE_Form_Section_Base
214
+     * @return EE_Form_Section_Proper|null
215 215
      * @throws EE_Error
216 216
      * @throws InvalidArgumentException
217 217
      * @throws \EventEspresso\core\exceptions\EntityNotFoundException
@@ -610,6 +610,7 @@  discard block
 block discarded – undo
610 610
      * @param EE_Registration $registration
611 611
      * @param EE_Question     $question
612 612
      * @param                 mixed EE_Answer|NULL      $answer
613
+     * @param EE_Answer $answer
613 614
      * @return EE_Form_Input_Base
614 615
      * @throws \EE_Error
615 616
      */
Please login to merge, or discard this patch.
Indentation   +1338 added lines, -1338 removed lines patch added patch discarded remove patch
@@ -17,1346 +17,1346 @@
 block discarded – undo
17 17
 class EE_SPCO_Reg_Step_Attendee_Information extends EE_SPCO_Reg_Step
18 18
 {
19 19
 
20
-    /**
21
-     * @type bool $_print_copy_info
22
-     */
23
-    private $_print_copy_info = false;
24
-
25
-    /**
26
-     * @type array $_attendee_data
27
-     */
28
-    private $_attendee_data = array();
29
-
30
-    /**
31
-     * @type array $_required_questions
32
-     */
33
-    private $_required_questions = array();
34
-
35
-    /**
36
-     * @type array $_registration_answers
37
-     */
38
-    private $_registration_answers = array();
39
-
40
-
41
-    /**
42
-     *    class constructor
43
-     *
44
-     * @access    public
45
-     * @param    EE_Checkout $checkout
46
-     */
47
-    public function __construct(EE_Checkout $checkout)
48
-    {
49
-        $this->_slug     = 'attendee_information';
50
-        $this->_name     = esc_html__('Attendee Information', 'event_espresso');
51
-        $this->_template = SPCO_REG_STEPS_PATH . $this->_slug . DS . 'attendee_info_main.template.php';
52
-        $this->checkout  = $checkout;
53
-        $this->_reset_success_message();
54
-        $this->set_instructions(
55
-            esc_html__('Please answer the following registration questions before proceeding.', 'event_espresso')
56
-        );
57
-    }
58
-
59
-
60
-    public function translate_js_strings()
61
-    {
62
-        EE_Registry::$i18n_js_strings['required_field']            = esc_html__(
63
-            ' is a required question.',
64
-            'event_espresso'
65
-        );
66
-        EE_Registry::$i18n_js_strings['required_multi_field']      = esc_html__(
67
-            ' is a required question. Please enter a value for at least one of the options.',
68
-            'event_espresso'
69
-        );
70
-        EE_Registry::$i18n_js_strings['answer_required_questions'] = esc_html__(
71
-            'Please answer all required questions correctly before proceeding.',
72
-            'event_espresso'
73
-        );
74
-        EE_Registry::$i18n_js_strings['attendee_info_copied']      = sprintf(
75
-            esc_html__(
76
-                'The attendee information was successfully copied.%sPlease ensure the rest of the registration form is completed before proceeding.',
77
-                'event_espresso'
78
-            ),
79
-            '<br/>'
80
-        );
81
-        EE_Registry::$i18n_js_strings['attendee_info_copy_error']  = esc_html__(
82
-            'An unknown error occurred on the server while attempting to copy the attendee information. Please refresh the page and try again.',
83
-            'event_espresso'
84
-        );
85
-        EE_Registry::$i18n_js_strings['enter_valid_email']         = esc_html__(
86
-            'You must enter a valid email address.',
87
-            'event_espresso'
88
-        );
89
-        EE_Registry::$i18n_js_strings['valid_email_and_questions'] = esc_html__(
90
-            'You must enter a valid email address and answer all other required questions before you can proceed.',
91
-            'event_espresso'
92
-        );
93
-    }
94
-
95
-
96
-    public function enqueue_styles_and_scripts()
97
-    {
98
-    }
99
-
100
-
101
-    /**
102
-     * @return boolean
103
-     */
104
-    public function initialize_reg_step()
105
-    {
106
-        return true;
107
-    }
108
-
109
-
110
-    /**
111
-     * @return EE_Form_Section_Proper
112
-     * @throws EE_Error
113
-     * @throws InvalidArgumentException
114
-     * @throws \EventEspresso\core\exceptions\EntityNotFoundException
115
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
116
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
117
-     */
118
-    public function generate_reg_form()
119
-    {
120
-        $this->_print_copy_info = false;
121
-        $primary_registrant     = null;
122
-        // autoload Line_Item_Display classes
123
-        EEH_Autoloader::register_line_item_display_autoloaders();
124
-        $Line_Item_Display = new EE_Line_Item_Display();
125
-        // calculate taxes
126
-        $Line_Item_Display->display_line_item(
127
-            $this->checkout->cart->get_grand_total(),
128
-            array('set_tax_rate' => true)
129
-        );
130
-        /** @var $subsections EE_Form_Section_Proper[] */
131
-        $subsections   = array(
132
-            'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
133
-        );
134
-        $template_args = array(
135
-            'revisit'       => $this->checkout->revisit,
136
-            'registrations' => array(),
137
-            'ticket_count'  => array(),
138
-        );
139
-        // grab the saved registrations from the transaction
140
-        $registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
141
-        if ($registrations) {
142
-            foreach ($registrations as $registration) {
143
-                // can this registration be processed during this visit ?
144
-                if ($registration instanceof EE_Registration
145
-                    && $this->checkout->visit_allows_processing_of_this_registration($registration)
146
-                ) {
147
-                    $subsection = $this->_registrations_reg_form($registration);
148
-                    if(!$subsection instanceof EE_Form_Section_Proper) {
149
-                        continue;
150
-                    }
151
-                    $subsections[ $registration->reg_url_link() ] = $subsection;
152
-                    if (! $this->checkout->admin_request) {
153
-                        $template_args['registrations'][$registration->reg_url_link()]    = $registration;
154
-                        $template_args['ticket_count'][$registration->ticket()->ID()]     = isset(
155
-                            $template_args['ticket_count'][$registration->ticket()->ID()]
156
-                        )
157
-                            ? $template_args['ticket_count'][$registration->ticket()->ID()] + 1
158
-                            : 1;
159
-                        $ticket_line_item = EEH_Line_Item::get_line_items_by_object_type_and_IDs(
160
-                            $this->checkout->cart->get_grand_total(),
161
-                            'Ticket',
162
-                            array($registration->ticket()->ID())
163
-                        );
164
-                        $ticket_line_item = is_array($ticket_line_item)
165
-                            ? reset($ticket_line_item)
166
-                            : $ticket_line_item;
167
-                        $template_args['ticket_line_item'][$registration->ticket()->ID()] =
168
-                            $Line_Item_Display->display_line_item($ticket_line_item);
169
-                    }
170
-                    if ($registration->is_primary_registrant()) {
171
-                        $primary_registrant = $registration->reg_url_link();
172
-                    }
173
-                }
174
-            }
175
-            // print_copy_info ?
176
-            if ($primary_registrant && ! $this->checkout->admin_request && count($registrations) > 1) {
177
-                // TODO: add admin option for toggling copy attendee info,
178
-                // then use that value to change $this->_print_copy_info
179
-                $copy_options['spco_copy_attendee_chk'] = $this->_print_copy_info
180
-                    ? $this->_copy_attendee_info_form()
181
-                    : $this->_auto_copy_attendee_info();
182
-                // generate hidden input
183
-                if (isset($subsections[$primary_registrant])
184
-                    && $subsections[$primary_registrant] instanceof EE_Form_Section_Proper
185
-                ) {
186
-                    $subsections[$primary_registrant]->add_subsections(
187
-                        $copy_options,
188
-                        'primary_registrant',
189
-                        false
190
-                    );
191
-                }
192
-            }
193
-        }
194
-        return new EE_Form_Section_Proper(
195
-            array(
196
-                'name'            => $this->reg_form_name(),
197
-                'html_id'         => $this->reg_form_name(),
198
-                'subsections'     => $subsections,
199
-                'layout_strategy' => $this->checkout->admin_request ?
200
-                    new EE_Div_Per_Section_Layout() :
201
-                    new EE_Template_Layout(
202
-                        array(
203
-                            'layout_template_file' => $this->_template, // layout_template
204
-                            'template_args'        => $template_args,
205
-                        )
206
-                    ),
207
-            )
208
-        );
209
-    }
210
-
211
-
212
-    /**
213
-     * @param EE_Registration $registration
214
-     * @return EE_Form_Section_Base
215
-     * @throws EE_Error
216
-     * @throws InvalidArgumentException
217
-     * @throws \EventEspresso\core\exceptions\EntityNotFoundException
218
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
219
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
220
-     */
221
-    private function _registrations_reg_form(EE_Registration $registration)
222
-    {
223
-        static $attendee_nmbr = 1;
224
-        $form_args = array();
225
-        // verify that registration has valid event
226
-        if ($registration->event() instanceof EE_Event) {
227
-            $question_groups = $registration->event()->question_groups(
228
-                apply_filters(
229
-                    'FHEE__EE_SPCO_Reg_Step_Attendee_Information___registrations_reg_form__question_groups_query_parameters',
230
-                    array(
231
-                        array(
232
-                            'Event.EVT_ID'                     => $registration->event()->ID(),
233
-                            'Event_Question_Group.EQG_primary' => $registration->count() === 1 ? true : false,
234
-                        ),
235
-                        'order_by' => array('QSG_order' => 'ASC'),
236
-                    ),
237
-                    $registration,
238
-                    $this
239
-                )
240
-            );
241
-            if ($question_groups) {
242
-                // array of params to pass to parent constructor
243
-                $form_args = array(
244
-                    'html_id'         => 'ee-registration-' . $registration->reg_url_link(),
245
-                    'html_class'      => 'ee-reg-form-attendee-dv',
246
-                    'html_style'      => $this->checkout->admin_request
247
-                        ? 'padding:0em 2em 1em; margin:3em 0 0; border:1px solid #ddd;'
248
-                        : '',
249
-                    'subsections'     => array(),
250
-                    'layout_strategy' => new EE_Fieldset_Section_Layout(
251
-                        array(
252
-                            'legend_class' => 'spco-attendee-lgnd smaller-text lt-grey-text',
253
-                            'legend_text'  => sprintf(__('Attendee %d', 'event_espresso'), $attendee_nmbr),
254
-                        )
255
-                    ),
256
-                );
257
-                foreach ($question_groups as $question_group) {
258
-                    if ($question_group instanceof EE_Question_Group) {
259
-                        $form_args['subsections'][$question_group->identifier()] = $this->_question_group_reg_form(
260
-                            $registration,
261
-                            $question_group
262
-                        );
263
-                    }
264
-                }
265
-                // add hidden input
266
-                $form_args['subsections']['additional_attendee_reg_info'] = $this->_additional_attendee_reg_info_input(
267
-                    $registration
268
-                );
269
-                // if we have question groups for additional attendees, then display the copy options
270
-                $this->_print_copy_info = $attendee_nmbr > 1 ? true : $this->_print_copy_info;
271
-                if ($registration->is_primary_registrant()) {
272
-                    // generate hidden input
273
-                    $form_args['subsections']['primary_registrant'] = $this->_additional_primary_registrant_inputs(
274
-                        $registration
275
-                    );
276
-                }
277
-            }
278
-        }
279
-        $attendee_nmbr++;
280
-        return ! empty($form_args) ? new EE_Form_Section_Proper($form_args) : null;
281
-    }
282
-
283
-
284
-    /**
285
-     * _additional_attendee_reg_info_input
286
-     *
287
-     * @access public
288
-     * @param EE_Registration $registration
289
-     * @param bool            $additional_attendee_reg_info
290
-     * @return    EE_Form_Input_Base
291
-     * @throws \EE_Error
292
-     */
293
-    private function _additional_attendee_reg_info_input(
294
-        EE_Registration $registration,
295
-        $additional_attendee_reg_info = true
296
-    ) {
297
-        // generate hidden input
298
-        return new EE_Hidden_Input(
299
-            array(
300
-                'html_id' => 'additional-attendee-reg-info-' . $registration->reg_url_link(),
301
-                'default' => $additional_attendee_reg_info,
302
-            )
303
-        );
304
-    }
305
-
306
-
307
-    /**
308
-     * @param EE_Registration   $registration
309
-     * @param EE_Question_Group $question_group
310
-     * @return EE_Form_Section_Proper
311
-     * @throws EE_Error
312
-     * @throws InvalidArgumentException
313
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
314
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
315
-     */
316
-    private function _question_group_reg_form(EE_Registration $registration, EE_Question_Group $question_group)
317
-    {
318
-        // array of params to pass to parent constructor
319
-        $form_args = array(
320
-            'html_id'         => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-' . $registration->ID(),
321
-            'html_class'      => $this->checkout->admin_request
322
-                ? 'form-table ee-reg-form-qstn-grp-dv'
323
-                : 'ee-reg-form-qstn-grp-dv',
324
-            'html_label_id'   => 'ee-reg-form-qstn-grp-' . $question_group->identifier() .  '-' . $registration->ID() . '-lbl',
325
-            'subsections'     => array(
326
-                'reg_form_qstn_grp_hdr' => $this->_question_group_header($question_group),
327
-            ),
328
-            'layout_strategy' => $this->checkout->admin_request
329
-                ? new EE_Admin_Two_Column_Layout()
330
-                : new EE_Div_Per_Section_Layout(),
331
-        );
332
-        // where params
333
-        $query_params = array('QST_deleted' => 0);
334
-        // don't load admin only questions on the frontend
335
-        if (! $this->checkout->admin_request) {
336
-            $query_params['QST_admin_only'] = array('!=', true);
337
-        }
338
-        $questions = $question_group->get_many_related(
339
-            'Question',
340
-            apply_filters(
341
-                'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__related_questions_query_params',
342
-                array(
343
-                    $query_params,
344
-                    'order_by' => array(
345
-                        'Question_Group_Question.QGQ_order' => 'ASC',
346
-                    ),
347
-                ),
348
-                $question_group,
349
-                $registration,
350
-                $this
351
-            )
352
-        );
353
-        // filter for additional content before questions
354
-        $form_args['subsections']['reg_form_questions_before'] = new EE_Form_Section_HTML(
355
-            apply_filters(
356
-                'FHEE__EEH_Form_Fields__generate_question_groups_html__before_question_group_questions',
357
-                '',
358
-                $registration,
359
-                $question_group,
360
-                $this
361
-            )
362
-        );
363
-        // loop thru questions
364
-        foreach ($questions as $question) {
365
-            if ($question instanceof EE_Question) {
366
-                $identifier                            = $question->is_system_question()
367
-                    ? $question->system_ID()
368
-                    : $question->ID();
369
-                $form_args['subsections'][$identifier] = $this->reg_form_question($registration, $question);
370
-            }
371
-        }
372
-        $form_args['subsections'] = apply_filters(
373
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information__question_group_reg_form__subsections_array',
374
-            $form_args['subsections'],
375
-            $registration,
376
-            $question_group,
377
-            $this
378
-        );
379
-        // filter for additional content after questions
380
-        $form_args['subsections']['reg_form_questions_after'] = new EE_Form_Section_HTML(
381
-            apply_filters(
382
-                'FHEE__EEH_Form_Fields__generate_question_groups_html__after_question_group_questions',
383
-                '',
384
-                $registration,
385
-                $question_group,
386
-                $this
387
-            )
388
-        );
20
+	/**
21
+	 * @type bool $_print_copy_info
22
+	 */
23
+	private $_print_copy_info = false;
24
+
25
+	/**
26
+	 * @type array $_attendee_data
27
+	 */
28
+	private $_attendee_data = array();
29
+
30
+	/**
31
+	 * @type array $_required_questions
32
+	 */
33
+	private $_required_questions = array();
34
+
35
+	/**
36
+	 * @type array $_registration_answers
37
+	 */
38
+	private $_registration_answers = array();
39
+
40
+
41
+	/**
42
+	 *    class constructor
43
+	 *
44
+	 * @access    public
45
+	 * @param    EE_Checkout $checkout
46
+	 */
47
+	public function __construct(EE_Checkout $checkout)
48
+	{
49
+		$this->_slug     = 'attendee_information';
50
+		$this->_name     = esc_html__('Attendee Information', 'event_espresso');
51
+		$this->_template = SPCO_REG_STEPS_PATH . $this->_slug . DS . 'attendee_info_main.template.php';
52
+		$this->checkout  = $checkout;
53
+		$this->_reset_success_message();
54
+		$this->set_instructions(
55
+			esc_html__('Please answer the following registration questions before proceeding.', 'event_espresso')
56
+		);
57
+	}
58
+
59
+
60
+	public function translate_js_strings()
61
+	{
62
+		EE_Registry::$i18n_js_strings['required_field']            = esc_html__(
63
+			' is a required question.',
64
+			'event_espresso'
65
+		);
66
+		EE_Registry::$i18n_js_strings['required_multi_field']      = esc_html__(
67
+			' is a required question. Please enter a value for at least one of the options.',
68
+			'event_espresso'
69
+		);
70
+		EE_Registry::$i18n_js_strings['answer_required_questions'] = esc_html__(
71
+			'Please answer all required questions correctly before proceeding.',
72
+			'event_espresso'
73
+		);
74
+		EE_Registry::$i18n_js_strings['attendee_info_copied']      = sprintf(
75
+			esc_html__(
76
+				'The attendee information was successfully copied.%sPlease ensure the rest of the registration form is completed before proceeding.',
77
+				'event_espresso'
78
+			),
79
+			'<br/>'
80
+		);
81
+		EE_Registry::$i18n_js_strings['attendee_info_copy_error']  = esc_html__(
82
+			'An unknown error occurred on the server while attempting to copy the attendee information. Please refresh the page and try again.',
83
+			'event_espresso'
84
+		);
85
+		EE_Registry::$i18n_js_strings['enter_valid_email']         = esc_html__(
86
+			'You must enter a valid email address.',
87
+			'event_espresso'
88
+		);
89
+		EE_Registry::$i18n_js_strings['valid_email_and_questions'] = esc_html__(
90
+			'You must enter a valid email address and answer all other required questions before you can proceed.',
91
+			'event_espresso'
92
+		);
93
+	}
94
+
95
+
96
+	public function enqueue_styles_and_scripts()
97
+	{
98
+	}
99
+
100
+
101
+	/**
102
+	 * @return boolean
103
+	 */
104
+	public function initialize_reg_step()
105
+	{
106
+		return true;
107
+	}
108
+
109
+
110
+	/**
111
+	 * @return EE_Form_Section_Proper
112
+	 * @throws EE_Error
113
+	 * @throws InvalidArgumentException
114
+	 * @throws \EventEspresso\core\exceptions\EntityNotFoundException
115
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
116
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
117
+	 */
118
+	public function generate_reg_form()
119
+	{
120
+		$this->_print_copy_info = false;
121
+		$primary_registrant     = null;
122
+		// autoload Line_Item_Display classes
123
+		EEH_Autoloader::register_line_item_display_autoloaders();
124
+		$Line_Item_Display = new EE_Line_Item_Display();
125
+		// calculate taxes
126
+		$Line_Item_Display->display_line_item(
127
+			$this->checkout->cart->get_grand_total(),
128
+			array('set_tax_rate' => true)
129
+		);
130
+		/** @var $subsections EE_Form_Section_Proper[] */
131
+		$subsections   = array(
132
+			'default_hidden_inputs' => $this->reg_step_hidden_inputs(),
133
+		);
134
+		$template_args = array(
135
+			'revisit'       => $this->checkout->revisit,
136
+			'registrations' => array(),
137
+			'ticket_count'  => array(),
138
+		);
139
+		// grab the saved registrations from the transaction
140
+		$registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
141
+		if ($registrations) {
142
+			foreach ($registrations as $registration) {
143
+				// can this registration be processed during this visit ?
144
+				if ($registration instanceof EE_Registration
145
+					&& $this->checkout->visit_allows_processing_of_this_registration($registration)
146
+				) {
147
+					$subsection = $this->_registrations_reg_form($registration);
148
+					if(!$subsection instanceof EE_Form_Section_Proper) {
149
+						continue;
150
+					}
151
+					$subsections[ $registration->reg_url_link() ] = $subsection;
152
+					if (! $this->checkout->admin_request) {
153
+						$template_args['registrations'][$registration->reg_url_link()]    = $registration;
154
+						$template_args['ticket_count'][$registration->ticket()->ID()]     = isset(
155
+							$template_args['ticket_count'][$registration->ticket()->ID()]
156
+						)
157
+							? $template_args['ticket_count'][$registration->ticket()->ID()] + 1
158
+							: 1;
159
+						$ticket_line_item = EEH_Line_Item::get_line_items_by_object_type_and_IDs(
160
+							$this->checkout->cart->get_grand_total(),
161
+							'Ticket',
162
+							array($registration->ticket()->ID())
163
+						);
164
+						$ticket_line_item = is_array($ticket_line_item)
165
+							? reset($ticket_line_item)
166
+							: $ticket_line_item;
167
+						$template_args['ticket_line_item'][$registration->ticket()->ID()] =
168
+							$Line_Item_Display->display_line_item($ticket_line_item);
169
+					}
170
+					if ($registration->is_primary_registrant()) {
171
+						$primary_registrant = $registration->reg_url_link();
172
+					}
173
+				}
174
+			}
175
+			// print_copy_info ?
176
+			if ($primary_registrant && ! $this->checkout->admin_request && count($registrations) > 1) {
177
+				// TODO: add admin option for toggling copy attendee info,
178
+				// then use that value to change $this->_print_copy_info
179
+				$copy_options['spco_copy_attendee_chk'] = $this->_print_copy_info
180
+					? $this->_copy_attendee_info_form()
181
+					: $this->_auto_copy_attendee_info();
182
+				// generate hidden input
183
+				if (isset($subsections[$primary_registrant])
184
+					&& $subsections[$primary_registrant] instanceof EE_Form_Section_Proper
185
+				) {
186
+					$subsections[$primary_registrant]->add_subsections(
187
+						$copy_options,
188
+						'primary_registrant',
189
+						false
190
+					);
191
+				}
192
+			}
193
+		}
194
+		return new EE_Form_Section_Proper(
195
+			array(
196
+				'name'            => $this->reg_form_name(),
197
+				'html_id'         => $this->reg_form_name(),
198
+				'subsections'     => $subsections,
199
+				'layout_strategy' => $this->checkout->admin_request ?
200
+					new EE_Div_Per_Section_Layout() :
201
+					new EE_Template_Layout(
202
+						array(
203
+							'layout_template_file' => $this->_template, // layout_template
204
+							'template_args'        => $template_args,
205
+						)
206
+					),
207
+			)
208
+		);
209
+	}
210
+
211
+
212
+	/**
213
+	 * @param EE_Registration $registration
214
+	 * @return EE_Form_Section_Base
215
+	 * @throws EE_Error
216
+	 * @throws InvalidArgumentException
217
+	 * @throws \EventEspresso\core\exceptions\EntityNotFoundException
218
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
219
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
220
+	 */
221
+	private function _registrations_reg_form(EE_Registration $registration)
222
+	{
223
+		static $attendee_nmbr = 1;
224
+		$form_args = array();
225
+		// verify that registration has valid event
226
+		if ($registration->event() instanceof EE_Event) {
227
+			$question_groups = $registration->event()->question_groups(
228
+				apply_filters(
229
+					'FHEE__EE_SPCO_Reg_Step_Attendee_Information___registrations_reg_form__question_groups_query_parameters',
230
+					array(
231
+						array(
232
+							'Event.EVT_ID'                     => $registration->event()->ID(),
233
+							'Event_Question_Group.EQG_primary' => $registration->count() === 1 ? true : false,
234
+						),
235
+						'order_by' => array('QSG_order' => 'ASC'),
236
+					),
237
+					$registration,
238
+					$this
239
+				)
240
+			);
241
+			if ($question_groups) {
242
+				// array of params to pass to parent constructor
243
+				$form_args = array(
244
+					'html_id'         => 'ee-registration-' . $registration->reg_url_link(),
245
+					'html_class'      => 'ee-reg-form-attendee-dv',
246
+					'html_style'      => $this->checkout->admin_request
247
+						? 'padding:0em 2em 1em; margin:3em 0 0; border:1px solid #ddd;'
248
+						: '',
249
+					'subsections'     => array(),
250
+					'layout_strategy' => new EE_Fieldset_Section_Layout(
251
+						array(
252
+							'legend_class' => 'spco-attendee-lgnd smaller-text lt-grey-text',
253
+							'legend_text'  => sprintf(__('Attendee %d', 'event_espresso'), $attendee_nmbr),
254
+						)
255
+					),
256
+				);
257
+				foreach ($question_groups as $question_group) {
258
+					if ($question_group instanceof EE_Question_Group) {
259
+						$form_args['subsections'][$question_group->identifier()] = $this->_question_group_reg_form(
260
+							$registration,
261
+							$question_group
262
+						);
263
+					}
264
+				}
265
+				// add hidden input
266
+				$form_args['subsections']['additional_attendee_reg_info'] = $this->_additional_attendee_reg_info_input(
267
+					$registration
268
+				);
269
+				// if we have question groups for additional attendees, then display the copy options
270
+				$this->_print_copy_info = $attendee_nmbr > 1 ? true : $this->_print_copy_info;
271
+				if ($registration->is_primary_registrant()) {
272
+					// generate hidden input
273
+					$form_args['subsections']['primary_registrant'] = $this->_additional_primary_registrant_inputs(
274
+						$registration
275
+					);
276
+				}
277
+			}
278
+		}
279
+		$attendee_nmbr++;
280
+		return ! empty($form_args) ? new EE_Form_Section_Proper($form_args) : null;
281
+	}
282
+
283
+
284
+	/**
285
+	 * _additional_attendee_reg_info_input
286
+	 *
287
+	 * @access public
288
+	 * @param EE_Registration $registration
289
+	 * @param bool            $additional_attendee_reg_info
290
+	 * @return    EE_Form_Input_Base
291
+	 * @throws \EE_Error
292
+	 */
293
+	private function _additional_attendee_reg_info_input(
294
+		EE_Registration $registration,
295
+		$additional_attendee_reg_info = true
296
+	) {
297
+		// generate hidden input
298
+		return new EE_Hidden_Input(
299
+			array(
300
+				'html_id' => 'additional-attendee-reg-info-' . $registration->reg_url_link(),
301
+				'default' => $additional_attendee_reg_info,
302
+			)
303
+		);
304
+	}
305
+
306
+
307
+	/**
308
+	 * @param EE_Registration   $registration
309
+	 * @param EE_Question_Group $question_group
310
+	 * @return EE_Form_Section_Proper
311
+	 * @throws EE_Error
312
+	 * @throws InvalidArgumentException
313
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
314
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
315
+	 */
316
+	private function _question_group_reg_form(EE_Registration $registration, EE_Question_Group $question_group)
317
+	{
318
+		// array of params to pass to parent constructor
319
+		$form_args = array(
320
+			'html_id'         => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-' . $registration->ID(),
321
+			'html_class'      => $this->checkout->admin_request
322
+				? 'form-table ee-reg-form-qstn-grp-dv'
323
+				: 'ee-reg-form-qstn-grp-dv',
324
+			'html_label_id'   => 'ee-reg-form-qstn-grp-' . $question_group->identifier() .  '-' . $registration->ID() . '-lbl',
325
+			'subsections'     => array(
326
+				'reg_form_qstn_grp_hdr' => $this->_question_group_header($question_group),
327
+			),
328
+			'layout_strategy' => $this->checkout->admin_request
329
+				? new EE_Admin_Two_Column_Layout()
330
+				: new EE_Div_Per_Section_Layout(),
331
+		);
332
+		// where params
333
+		$query_params = array('QST_deleted' => 0);
334
+		// don't load admin only questions on the frontend
335
+		if (! $this->checkout->admin_request) {
336
+			$query_params['QST_admin_only'] = array('!=', true);
337
+		}
338
+		$questions = $question_group->get_many_related(
339
+			'Question',
340
+			apply_filters(
341
+				'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__related_questions_query_params',
342
+				array(
343
+					$query_params,
344
+					'order_by' => array(
345
+						'Question_Group_Question.QGQ_order' => 'ASC',
346
+					),
347
+				),
348
+				$question_group,
349
+				$registration,
350
+				$this
351
+			)
352
+		);
353
+		// filter for additional content before questions
354
+		$form_args['subsections']['reg_form_questions_before'] = new EE_Form_Section_HTML(
355
+			apply_filters(
356
+				'FHEE__EEH_Form_Fields__generate_question_groups_html__before_question_group_questions',
357
+				'',
358
+				$registration,
359
+				$question_group,
360
+				$this
361
+			)
362
+		);
363
+		// loop thru questions
364
+		foreach ($questions as $question) {
365
+			if ($question instanceof EE_Question) {
366
+				$identifier                            = $question->is_system_question()
367
+					? $question->system_ID()
368
+					: $question->ID();
369
+				$form_args['subsections'][$identifier] = $this->reg_form_question($registration, $question);
370
+			}
371
+		}
372
+		$form_args['subsections'] = apply_filters(
373
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information__question_group_reg_form__subsections_array',
374
+			$form_args['subsections'],
375
+			$registration,
376
+			$question_group,
377
+			$this
378
+		);
379
+		// filter for additional content after questions
380
+		$form_args['subsections']['reg_form_questions_after'] = new EE_Form_Section_HTML(
381
+			apply_filters(
382
+				'FHEE__EEH_Form_Fields__generate_question_groups_html__after_question_group_questions',
383
+				'',
384
+				$registration,
385
+				$question_group,
386
+				$this
387
+			)
388
+		);
389 389
 //		d( $form_args );
390
-        $question_group_reg_form = new EE_Form_Section_Proper($form_args);
391
-        return apply_filters(
392
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__question_group_reg_form',
393
-            $question_group_reg_form,
394
-            $registration,
395
-            $question_group,
396
-            $this
397
-        );
398
-    }
399
-
400
-
401
-    /**
402
-     * @access public
403
-     * @param EE_Question_Group $question_group
404
-     * @return    EE_Form_Section_HTML
405
-     */
406
-    private function _question_group_header(EE_Question_Group $question_group)
407
-    {
408
-        $html = '';
409
-        // group_name
410
-        if ($question_group->show_group_name() && $question_group->name() !== '') {
411
-            if ($this->checkout->admin_request) {
412
-                $html .= EEH_HTML::br();
413
-                $html .= EEH_HTML::h3(
414
-                    $question_group->name(),
415
-                    '',
416
-                    'ee-reg-form-qstn-grp-title title',
417
-                    'font-size: 1.3em; padding-left:0;'
418
-                );
419
-            } else {
420
-                $html .= EEH_HTML::h4(
421
-                    $question_group->name(),
422
-                    '',
423
-                    'ee-reg-form-qstn-grp-title section-title'
424
-                );
425
-            }
426
-        }
427
-        // group_desc
428
-        if ($question_group->show_group_desc() && $question_group->desc() !== '') {
429
-            $html .= EEH_HTML::p(
430
-                $question_group->desc(),
431
-                '',
432
-                $this->checkout->admin_request
433
-                    ? 'ee-reg-form-qstn-grp-desc-pg'
434
-                    : 'ee-reg-form-qstn-grp-desc-pg small-text lt-grey-text'
435
-            );
436
-        }
437
-        return new EE_Form_Section_HTML($html);
438
-    }
439
-
440
-
441
-    /**
442
-     * @access public
443
-     * @return    EE_Form_Section_Proper
444
-     * @throws \EE_Error
445
-     */
446
-    private function _copy_attendee_info_form()
447
-    {
448
-        // array of params to pass to parent constructor
449
-        return new EE_Form_Section_Proper(
450
-            array(
451
-                'subsections'     => $this->_copy_attendee_info_inputs(),
452
-                'layout_strategy' => new EE_Template_Layout(
453
-                    array(
454
-                        'layout_template_file'     => SPCO_REG_STEPS_PATH
455
-                                                      . $this->_slug
456
-                                                      . DS
457
-                                                      . 'copy_attendee_info.template.php',
458
-                        'begin_template_file'      => null,
459
-                        'input_template_file'      => null,
460
-                        'subsection_template_file' => null,
461
-                        'end_template_file'        => null,
462
-                    )
463
-                ),
464
-            )
465
-        );
466
-    }
467
-
468
-
469
-    /**
470
-     * _auto_copy_attendee_info
471
-     *
472
-     * @access public
473
-     * @return EE_Form_Section_HTML
474
-     */
475
-    private function _auto_copy_attendee_info()
476
-    {
477
-        return new EE_Form_Section_HTML(
478
-            EEH_Template::locate_template(
479
-                SPCO_REG_STEPS_PATH . $this->_slug . DS . '_auto_copy_attendee_info.template.php',
480
-                apply_filters(
481
-                    'FHEE__EE_SPCO_Reg_Step_Attendee_Information__auto_copy_attendee_info__template_args',
482
-                    array()
483
-                ),
484
-                true,
485
-                true
486
-            )
487
-        );
488
-    }
489
-
490
-
491
-    /**
492
-     * _copy_attendee_info_inputs
493
-     *
494
-     * @access public
495
-     * @return array
496
-     * @throws \EE_Error
497
-     */
498
-    private function _copy_attendee_info_inputs()
499
-    {
500
-        $copy_attendee_info_inputs = array();
501
-        $prev_ticket               = null;
502
-        // grab the saved registrations from the transaction
503
-        $registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
504
-        foreach ($registrations as $registration) {
505
-            // for all  attendees other than the primary attendee
506
-            if ($registration instanceof EE_Registration && ! $registration->is_primary_registrant()) {
507
-                // if this is a new ticket OR if this is the very first additional attendee after the primary attendee
508
-                if ($registration->ticket()->ID() !== $prev_ticket) {
509
-                    $item_name = $registration->ticket()->name();
510
-                    $item_name .= $registration->ticket()->description() !== ''
511
-                        ? ' - ' . $registration->ticket()->description()
512
-                        : '';
513
-                    $copy_attendee_info_inputs['spco_copy_attendee_chk[ticket-' . $registration->ticket()->ID() . ']'] =
514
-                        new EE_Form_Section_HTML(
515
-                            '<h6 class="spco-copy-attendee-event-hdr">' . $item_name . '</h6>'
516
-                        );
517
-                    $prev_ticket = $registration->ticket()->ID();
518
-                }
519
-
520
-                $copy_attendee_info_inputs['spco_copy_attendee_chk[' . $registration->ID() . ']'] =
521
-                    new EE_Checkbox_Multi_Input(
522
-                        array(
523
-                            $registration->ID() => sprintf(
524
-                                esc_html__('Attendee #%s', 'event_espresso'),
525
-                                $registration->count()
526
-                            ),
527
-                        ),
528
-                        array(
529
-                            'html_id'                 => 'spco-copy-attendee-chk-' . $registration->reg_url_link(),
530
-                            'html_class'              => 'spco-copy-attendee-chk ee-do-not-validate',
531
-                            'display_html_label_text' => false,
532
-                        )
533
-                    );
534
-            }
535
-        }
536
-        return $copy_attendee_info_inputs;
537
-    }
538
-
539
-
540
-    /**
541
-     * _additional_primary_registrant_inputs
542
-     *
543
-     * @access public
544
-     * @param EE_Registration $registration
545
-     * @return    EE_Form_Input_Base
546
-     * @throws \EE_Error
547
-     */
548
-    private function _additional_primary_registrant_inputs(EE_Registration $registration)
549
-    {
550
-        // generate hidden input
551
-        return new EE_Hidden_Input(
552
-            array(
553
-                'html_id' => 'primary_registrant',
554
-                'default' => $registration->reg_url_link(),
555
-            )
556
-        );
557
-    }
558
-
559
-
560
-    /**
561
-     * @access public
562
-     * @param EE_Registration $registration
563
-     * @param EE_Question     $question
564
-     * @return EE_Form_Input_Base
565
-     * @throws EE_Error
566
-     * @throws InvalidArgumentException
567
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
568
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
569
-     */
570
-    public function reg_form_question(EE_Registration $registration, EE_Question $question)
571
-    {
572
-
573
-        // if this question was for an attendee detail, then check for that answer
574
-        $answer_value = EEM_Answer::instance()->get_attendee_property_answer_value(
575
-            $registration,
576
-            $question->system_ID()
577
-        );
578
-        $answer       = $answer_value === null
579
-            ? EEM_Answer::instance()->get_one(
580
-                array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()))
581
-            )
582
-            : null;
583
-        // if NOT returning to edit an existing registration
584
-        // OR if this question is for an attendee property
585
-        // OR we still don't have an EE_Answer object
586
-        if ($answer_value || ! $answer instanceof EE_Answer || ! $registration->reg_url_link()) {
587
-            // create an EE_Answer object for storing everything in
588
-            $answer = EE_Answer::new_instance(array(
589
-                'QST_ID' => $question->ID(),
590
-                'REG_ID' => $registration->ID(),
591
-            ));
592
-        }
593
-        // verify instance
594
-        if ($answer instanceof EE_Answer) {
595
-            if (! empty($answer_value)) {
596
-                $answer->set('ANS_value', $answer_value);
597
-            }
598
-            $answer->cache('Question', $question);
599
-            //remember system ID had a bug where sometimes it could be null
600
-            $answer_cache_id = $question->is_system_question()
601
-                ? $question->system_ID() . '-' . $registration->reg_url_link()
602
-                : $question->ID() . '-' . $registration->reg_url_link();
603
-            $registration->cache('Answer', $answer, $answer_cache_id);
604
-        }
605
-        return $this->_generate_question_input($registration, $question, $answer);
606
-    }
607
-
608
-
609
-    /**
610
-     * @param EE_Registration $registration
611
-     * @param EE_Question     $question
612
-     * @param                 mixed EE_Answer|NULL      $answer
613
-     * @return EE_Form_Input_Base
614
-     * @throws \EE_Error
615
-     */
616
-    private function _generate_question_input(EE_Registration $registration, EE_Question $question, $answer)
617
-    {
618
-        $identifier                             = $question->is_system_question()
619
-            ? $question->system_ID()
620
-            : $question->ID();
621
-        $this->_required_questions[$identifier] = $question->required() ? true : false;
622
-        add_filter(
623
-            'FHEE__EE_Question__generate_form_input__country_options',
624
-            array($this, 'use_cached_countries_for_form_input'),
625
-            10,
626
-            4
627
-        );
628
-        add_filter(
629
-            'FHEE__EE_Question__generate_form_input__state_options',
630
-            array($this, 'use_cached_states_for_form_input'),
631
-            10,
632
-            4
633
-        );
634
-        $input_constructor_args                  = array(
635
-            'html_name'        => 'ee_reg_qstn[' . $registration->ID() . '][' . $identifier . ']',
636
-            'html_id'          => 'ee_reg_qstn-' . $registration->ID() . '-' . $identifier,
637
-            'html_class'       => 'ee-reg-qstn ee-reg-qstn-' . $identifier,
638
-            'html_label_id'    => 'ee_reg_qstn-' . $registration->ID() . '-' . $identifier,
639
-            'html_label_class' => 'ee-reg-qstn',
640
-        );
641
-        $input_constructor_args['html_label_id'] .= '-lbl';
642
-        if ($answer instanceof EE_Answer && $answer->ID()) {
643
-            $input_constructor_args['html_name']     .= '[' . $answer->ID() . ']';
644
-            $input_constructor_args['html_id']       .= '-' . $answer->ID();
645
-            $input_constructor_args['html_label_id'] .= '-' . $answer->ID();
646
-        }
647
-        $form_input = $question->generate_form_input(
648
-            $registration,
649
-            $answer,
650
-            $input_constructor_args
651
-        );
652
-        remove_filter(
653
-            'FHEE__EE_Question__generate_form_input__country_options',
654
-            array($this, 'use_cached_countries_for_form_input')
655
-        );
656
-        remove_filter(
657
-            'FHEE__EE_Question__generate_form_input__state_options',
658
-            array($this, 'use_cached_states_for_form_input')
659
-        );
660
-        return $form_input;
661
-    }
662
-
663
-
664
-    /**
665
-     * Gets the list of countries for the form input
666
-     *
667
-     * @param array|null       $countries_list
668
-     * @param \EE_Question     $question
669
-     * @param \EE_Registration $registration
670
-     * @param \EE_Answer       $answer
671
-     * @return array 2d keys are country IDs, values are their names
672
-     * @throws EE_Error
673
-     * @throws InvalidArgumentException
674
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
675
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
676
-     */
677
-    public function use_cached_countries_for_form_input(
678
-        $countries_list,
679
-        \EE_Question $question = null,
680
-        \EE_Registration $registration = null,
681
-        \EE_Answer $answer = null
682
-    ) {
683
-        $country_options = array('' => '');
684
-        // get possibly cached list of countries
685
-        $countries = $this->checkout->action === 'process_reg_step'
686
-            ? EEM_Country::instance()->get_all_countries()
687
-            : EEM_Country::instance()->get_all_active_countries();
688
-        if (! empty($countries)) {
689
-            foreach ($countries as $country) {
690
-                if ($country instanceof EE_Country) {
691
-                    $country_options[$country->ID()] = $country->name();
692
-                }
693
-            }
694
-        }
695
-        if ($question instanceof EE_Question
696
-            && $registration instanceof EE_Registration) {
697
-            $answer = EEM_Answer::instance()->get_one(
698
-                array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()))
699
-            );
700
-        } else {
701
-            $answer = EE_Answer::new_instance();
702
-        }
703
-        $country_options = apply_filters(
704
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__country_options',
705
-            $country_options,
706
-            $this,
707
-            $registration,
708
-            $question,
709
-            $answer
710
-        );
711
-        return $country_options;
712
-    }
713
-
714
-
715
-    /**
716
-     * Gets the list of states for the form input
717
-     *
718
-     * @param array|null       $states_list
719
-     * @param \EE_Question     $question
720
-     * @param \EE_Registration $registration
721
-     * @param \EE_Answer       $answer
722
-     * @return array 2d keys are state IDs, values are their names
723
-     * @throws EE_Error
724
-     * @throws InvalidArgumentException
725
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
726
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
727
-     */
728
-    public function use_cached_states_for_form_input(
729
-        $states_list,
730
-        \EE_Question $question = null,
731
-        \EE_Registration $registration = null,
732
-        \EE_Answer $answer = null
733
-    ) {
734
-        $state_options = array('' => array('' => ''));
735
-        $states        = $this->checkout->action === 'process_reg_step'
736
-            ? EEM_State::instance()->get_all_states()
737
-            : EEM_State::instance()->get_all_active_states();
738
-        if (! empty($states)) {
739
-            foreach ($states as $state) {
740
-                if ($state instanceof EE_State) {
741
-                    $state_options[$state->country()->name()][$state->ID()] = $state->name();
742
-                }
743
-            }
744
-        }
745
-        $state_options = apply_filters(
746
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__state_options',
747
-            $state_options,
748
-            $this,
749
-            $registration,
750
-            $question,
751
-            $answer
752
-        );
753
-        return $state_options;
754
-    }
755
-
756
-
757
-
758
-
759
-
760
-
761
-    /********************************************************************************************************/
762
-    /****************************************  PROCESS REG STEP  ****************************************/
763
-    /********************************************************************************************************/
764
-    /**
765
-     * @return bool
766
-     * @throws EE_Error
767
-     * @throws InvalidArgumentException
768
-     * @throws ReflectionException
769
-     * @throws RuntimeException
770
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
771
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
772
-     */
773
-    public function process_reg_step()
774
-    {
775
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
776
-        // grab validated data from form
777
-        $valid_data = $this->checkout->current_step->valid_data();
778
-        // EEH_Debug_Tools::printr( $_REQUEST, '$_REQUEST', __FILE__, __LINE__ );
779
-        // EEH_Debug_Tools::printr( $valid_data, '$valid_data', __FILE__, __LINE__ );
780
-        // if we don't have any $valid_data then something went TERRIBLY WRONG !!!
781
-        if (empty($valid_data)) {
782
-            EE_Error::add_error(
783
-                esc_html__('No valid question responses were received.', 'event_espresso'),
784
-                __FILE__,
785
-                __FUNCTION__,
786
-                __LINE__
787
-            );
788
-            return false;
789
-        }
790
-        if (! $this->checkout->transaction instanceof EE_Transaction || ! $this->checkout->continue_reg) {
791
-            EE_Error::add_error(
792
-                esc_html__(
793
-                    'A valid transaction could not be initiated for processing your registrations.',
794
-                    'event_espresso'
795
-                ),
796
-                __FILE__,
797
-                __FUNCTION__,
798
-                __LINE__
799
-            );
800
-            return false;
801
-        }
802
-        // get cached registrations
803
-        $registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
804
-        // verify we got the goods
805
-        if (empty($registrations)) {
806
-            //combine the old translated string with a new one, in order to not break translations
807
-            $error_message = esc_html__( 'Your form data could not be applied to any valid registrations.', 'event_espresso' )
808
-                             . sprintf(
809
-                                 esc_html__('%3$sThis can sometimes happen if too much time has been taken to complete the registration process.%3$sPlease return to the %1$sEvent List%2$s and reselect your tickets. If the problem continues, please contact the site administrator.', 'event_espresso'),
810
-                                 '<a href="' . get_post_type_archive_link('espresso_events') . '" >',
811
-                                 '</a>',
812
-                                 '<br />'
813
-                             );
814
-            EE_Error::add_error(
815
-                $error_message,
816
-                __FILE__,
817
-                __FUNCTION__,
818
-                __LINE__
819
-            );
820
-            return false;
821
-        }
822
-        // extract attendee info from form data and save to model objects
823
-        $registrations_processed = $this->_process_registrations($registrations, $valid_data);
824
-        // if first pass thru SPCO,
825
-        // then let's check processed registrations against the total number of tickets in the cart
826
-        if ($registrations_processed === false) {
827
-            // but return immediately if the previous step exited early due to errors
828
-            return false;
829
-        } elseif (! $this->checkout->revisit && $registrations_processed !== $this->checkout->total_ticket_count) {
830
-            // generate a correctly translated string for all possible singular/plural combinations
831
-            if ($this->checkout->total_ticket_count === 1 && $registrations_processed !== 1) {
832
-                $error_msg = sprintf(
833
-                    esc_html__(
834
-                        'There was %1$d ticket in the Event Queue, but %2$ds registrations were processed',
835
-                        'event_espresso'
836
-                    ),
837
-                    $this->checkout->total_ticket_count,
838
-                    $registrations_processed
839
-                );
840
-            } elseif ($this->checkout->total_ticket_count !== 1 && $registrations_processed === 1) {
841
-                $error_msg = sprintf(
842
-                    esc_html__(
843
-                        'There was a total of %1$d tickets in the Event Queue, but only %2$ds registration was processed',
844
-                        'event_espresso'
845
-                    ),
846
-                    $this->checkout->total_ticket_count,
847
-                    $registrations_processed
848
-                );
849
-            } else {
850
-                $error_msg = sprintf(
851
-                    esc_html__(
852
-                        'There was a total of %1$d tickets in the Event Queue, but %2$ds registrations were processed',
853
-                        'event_espresso'
854
-                    ),
855
-                    $this->checkout->total_ticket_count,
856
-                    $registrations_processed
857
-                );
858
-            }
859
-            EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
860
-            return false;
861
-        }
862
-        // mark this reg step as completed
863
-        $this->set_completed();
864
-        $this->_set_success_message(
865
-            esc_html__('The Attendee Information Step has been successfully completed.', 'event_espresso')
866
-        );
867
-        //do action in case a plugin wants to do something with the data submitted in step 1.
868
-        //passes EE_Single_Page_Checkout, and it's posted data
869
-        do_action('AHEE__EE_Single_Page_Checkout__process_attendee_information__end', $this, $valid_data);
870
-        return true;
871
-    }
872
-
873
-
874
-    /**
875
-     *    _process_registrations
876
-     *
877
-     * @param EE_Registration[] $registrations
878
-     * @param array             $valid_data
879
-     * @return bool|int
880
-     * @throws \EventEspresso\core\exceptions\EntityNotFoundException
881
-     * @throws EE_Error
882
-     * @throws InvalidArgumentException
883
-     * @throws ReflectionException
884
-     * @throws RuntimeException
885
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
886
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
887
-     */
888
-    private function _process_registrations($registrations = array(), $valid_data = array())
889
-    {
890
-        // load resources and set some defaults
891
-        EE_Registry::instance()->load_model('Attendee');
892
-        // holder for primary registrant attendee object
893
-        $this->checkout->primary_attendee_obj = null;
894
-        // array for tracking reg form data for the primary registrant
895
-        $primary_registrant = array(
896
-            'line_item_id' => null,
897
-        );
898
-        $copy_primary       = false;
899
-        // reg form sections that do not contain inputs
900
-        $non_input_form_sections = array(
901
-            'primary_registrant',
902
-            'additional_attendee_reg_info',
903
-            'spco_copy_attendee_chk',
904
-        );
905
-        // attendee counter
906
-        $att_nmbr = 0;
907
-        // grab the saved registrations from the transaction
908
-        foreach ($registrations as $registration) {
909
-            // verify EE_Registration object
910
-            if (! $registration instanceof EE_Registration) {
911
-                EE_Error::add_error(
912
-                    esc_html__(
913
-                        'An invalid Registration object was discovered when attempting to process your registration information.',
914
-                        'event_espresso'
915
-                    ),
916
-                    __FILE__,
917
-                    __FUNCTION__,
918
-                    __LINE__
919
-                );
920
-                return false;
921
-            }
922
-            /** @var string $reg_url_link */
923
-            $reg_url_link = $registration->reg_url_link();
924
-            // reg_url_link exists ?
925
-            if (! empty($reg_url_link)) {
926
-                // should this registration be processed during this visit ?
927
-                if ($this->checkout->visit_allows_processing_of_this_registration($registration)) {
928
-                    // if NOT revisiting, then let's save the registration now,
929
-                    // so that we have a REG_ID to use when generating other objects
930
-                    if (! $this->checkout->revisit) {
931
-                        $registration->save();
932
-                    }
933
-                    /**
934
-                     * This allows plugins to trigger a fail on processing of a
935
-                     * registration for any conditions they may have for it to pass.
936
-                     *
937
-                     * @var bool   if true is returned by the plugin then the
938
-                     *            registration processing is halted.
939
-                     */
940
-                    if (apply_filters(
941
-                        'FHEE__EE_SPCO_Reg_Step_Attendee_Information___process_registrations__pre_registration_process',
942
-                        false,
943
-                        $att_nmbr,
944
-                        $registration,
945
-                        $registrations,
946
-                        $valid_data,
947
-                        $this
948
-                    )) {
949
-                        return false;
950
-                    }
951
-
952
-                    // Houston, we have a registration!
953
-                    $att_nmbr++;
954
-                    $this->_attendee_data[$reg_url_link] = array();
955
-                    // grab any existing related answer objects
956
-                    $this->_registration_answers = $registration->answers();
957
-                    // unset( $valid_data[ $reg_url_link ]['additional_attendee_reg_info'] );
958
-                    if (isset($valid_data[$reg_url_link])) {
959
-                        // do we need to copy basic info from primary attendee ?
960
-                        $copy_primary = isset($valid_data[$reg_url_link]['additional_attendee_reg_info'])
961
-                                        && absint($valid_data[$reg_url_link]['additional_attendee_reg_info']) === 0
962
-                            ? true
963
-                            : false;
964
-                        // filter form input data for this registration
965
-                        $valid_data[$reg_url_link] = (array)apply_filters(
966
-                            'FHEE__EE_Single_Page_Checkout__process_attendee_information__valid_data_line_item',
967
-                            $valid_data[$reg_url_link]
968
-                        );
969
-                        if (isset($valid_data['primary_attendee'])) {
970
-                            $primary_registrant['line_item_id'] = ! empty($valid_data['primary_attendee'])
971
-                                ? $valid_data['primary_attendee']
972
-                                : false;
973
-                            unset($valid_data['primary_attendee']);
974
-                        }
975
-                        // now loop through our array of valid post data && process attendee reg forms
976
-                        foreach ($valid_data[$reg_url_link] as $form_section => $form_inputs) {
977
-                            if (! in_array($form_section, $non_input_form_sections)) {
978
-                                foreach ($form_inputs as $form_input => $input_value) {
979
-                                    // \EEH_Debug_Tools::printr( $input_value, $form_input, __FILE__, __LINE__ );
980
-                                    // check for critical inputs
981
-                                    if (! $this->_verify_critical_attendee_details_are_set_and_validate_email(
982
-                                        $form_input,
983
-                                        $input_value
984
-                                    )
985
-                                    ) {
986
-                                        return false;
987
-                                    }
988
-                                    // store a bit of data about the primary attendee
989
-                                    if ($att_nmbr === 1
990
-                                        && ! empty($input_value)
991
-                                        && $reg_url_link === $primary_registrant['line_item_id']
992
-                                    ) {
993
-                                        $primary_registrant[$form_input] = $input_value;
994
-                                    } elseif ($copy_primary
995
-                                        && $input_value === null
996
-                                        && isset($primary_registrant[$form_input])
997
-                                    ) {
998
-                                        $input_value = $primary_registrant[$form_input];
999
-                                    }
1000
-                                    // now attempt to save the input data
1001
-                                    if (! $this->_save_registration_form_input(
1002
-                                        $registration,
1003
-                                        $form_input,
1004
-                                        $input_value
1005
-                                    )
1006
-                                    ) {
1007
-                                        EE_Error::add_error(
1008
-                                            sprintf(
1009
-                                                esc_html__(
1010
-                                                    'Unable to save registration form data for the form input: "%1$s" with the submitted value: "%2$s"',
1011
-                                                    'event_espresso'
1012
-                                                ),
1013
-                                                $form_input,
1014
-                                                $input_value
1015
-                                            ),
1016
-                                            __FILE__,
1017
-                                            __FUNCTION__,
1018
-                                            __LINE__
1019
-                                        );
1020
-                                        return false;
1021
-                                    }
1022
-                                }
1023
-                            }
1024
-                        }  // end of foreach ( $valid_data[ $reg_url_link ] as $form_section => $form_inputs )
1025
-                    }
1026
-                    //EEH_Debug_Tools::printr( $this->_attendee_data, '$this->_attendee_data', __FILE__, __LINE__ );
1027
-                    // this registration does not require additional attendee information ?
1028
-                    if ($copy_primary
1029
-                        && $att_nmbr > 1
1030
-                        && $this->checkout->primary_attendee_obj instanceof EE_Attendee
1031
-                    ) {
1032
-                        // just copy the primary registrant
1033
-                        $attendee = $this->checkout->primary_attendee_obj;
1034
-                    } else {
1035
-                        // ensure critical details are set for additional attendees
1036
-                        $this->_attendee_data[$reg_url_link] = $att_nmbr > 1
1037
-                            ? $this->_copy_critical_attendee_details_from_primary_registrant(
1038
-                                $this->_attendee_data[$reg_url_link]
1039
-                            )
1040
-                            : $this->_attendee_data[$reg_url_link];
1041
-                        // execute create attendee command (which may return an existing attendee)
1042
-                        $attendee = EE_Registry::instance()->BUS->execute(
1043
-                            new CreateAttendeeCommand(
1044
-                                $this->_attendee_data[$reg_url_link],
1045
-                                $registration
1046
-                            )
1047
-                        );
1048
-                        // who's #1 ?
1049
-                        if ($att_nmbr === 1) {
1050
-                            $this->checkout->primary_attendee_obj = $attendee;
1051
-                        }
1052
-                    }
1053
-                    // EEH_Debug_Tools::printr( $attendee, '$attendee', __FILE__, __LINE__ );
1054
-                    // add relation to registration, set attendee ID, and cache attendee
1055
-                    $this->_associate_attendee_with_registration($registration, $attendee);
1056
-                    // \EEH_Debug_Tools::printr( $registration, '$registration', __FILE__, __LINE__ );
1057
-                    if (! $registration->attendee() instanceof EE_Attendee) {
1058
-                        EE_Error::add_error(
1059
-                            sprintf(
1060
-                                esc_html__(
1061
-                                    'Registration %s has an invalid or missing Attendee object.',
1062
-                                    'event_espresso'
1063
-                                ),
1064
-                                $reg_url_link
1065
-                            ),
1066
-                            __FILE__,
1067
-                            __FUNCTION__,
1068
-                            __LINE__
1069
-                        );
1070
-                        return false;
1071
-                    }
1072
-                    /** @type EE_Registration_Processor $registration_processor */
1073
-                    $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
1074
-                    // at this point, we should have enough details about the registrant to consider the registration
1075
-                    // NOT incomplete
1076
-                    $registration_processor->toggle_incomplete_registration_status_to_default(
1077
-                        $registration,
1078
-                        false,
1079
-                        new Context(
1080
-                            'spco_reg_step_attendee_information_process_registrations',
1081
-                            esc_html__(
1082
-                                'Finished populating registration with details from the registration form after submitting the Attendee Information Reg Step.',
1083
-                                'event_espresso'
1084
-                            )
1085
-                        )
1086
-                    );
1087
-                    // we can also consider the TXN to not have been failed, so temporarily upgrade it's status to
1088
-                    // abandoned
1089
-                    $this->checkout->transaction->toggle_failed_transaction_status();
1090
-                    // if we've gotten this far, then let's save what we have
1091
-                    $registration->save();
1092
-                    // add relation between TXN and registration
1093
-                    $this->_associate_registration_with_transaction($registration);
1094
-                }
1095
-            } else {
1096
-                EE_Error::add_error(
1097
-                    esc_html__(
1098
-                        'An invalid or missing line item ID was encountered while attempting to process the registration form.',
1099
-                        'event_espresso'
1100
-                    ),
1101
-                    __FILE__,
1102
-                    __FUNCTION__,
1103
-                    __LINE__
1104
-                );
1105
-                // remove malformed data
1106
-                unset($valid_data[$reg_url_link]);
1107
-                return false;
1108
-            }
1109
-
1110
-        } // end of foreach ( $this->checkout->transaction->registrations()  as $registration )
1111
-        return $att_nmbr;
1112
-    }
1113
-
1114
-
1115
-    /**
1116
-     *    _save_registration_form_input
1117
-     *
1118
-     * @param EE_Registration $registration
1119
-     * @param string          $form_input
1120
-     * @param string          $input_value
1121
-     * @return bool
1122
-     * @throws EE_Error
1123
-     * @throws InvalidArgumentException
1124
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
1125
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
1126
-     */
1127
-    private function _save_registration_form_input(
1128
-        EE_Registration $registration,
1129
-        $form_input = '',
1130
-        $input_value = ''
1131
-    ) {
1132
-        // \EEH_Debug_Tools::printr( __FUNCTION__, __CLASS__, __FILE__, __LINE__, 2 );
1133
-        // \EEH_Debug_Tools::printr( $form_input, '$form_input', __FILE__, __LINE__ );
1134
-        // \EEH_Debug_Tools::printr( $input_value, '$input_value', __FILE__, __LINE__ );
1135
-        // allow for plugins to hook in and do their own processing of the form input.
1136
-        // For plugins to bypass normal processing here, they just need to return a boolean value.
1137
-        if (apply_filters(
1138
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___save_registration_form_input',
1139
-            false,
1140
-            $registration,
1141
-            $form_input,
1142
-            $input_value,
1143
-            $this
1144
-        )) {
1145
-            return true;
1146
-        }
1147
-        /*
390
+		$question_group_reg_form = new EE_Form_Section_Proper($form_args);
391
+		return apply_filters(
392
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___question_group_reg_form__question_group_reg_form',
393
+			$question_group_reg_form,
394
+			$registration,
395
+			$question_group,
396
+			$this
397
+		);
398
+	}
399
+
400
+
401
+	/**
402
+	 * @access public
403
+	 * @param EE_Question_Group $question_group
404
+	 * @return    EE_Form_Section_HTML
405
+	 */
406
+	private function _question_group_header(EE_Question_Group $question_group)
407
+	{
408
+		$html = '';
409
+		// group_name
410
+		if ($question_group->show_group_name() && $question_group->name() !== '') {
411
+			if ($this->checkout->admin_request) {
412
+				$html .= EEH_HTML::br();
413
+				$html .= EEH_HTML::h3(
414
+					$question_group->name(),
415
+					'',
416
+					'ee-reg-form-qstn-grp-title title',
417
+					'font-size: 1.3em; padding-left:0;'
418
+				);
419
+			} else {
420
+				$html .= EEH_HTML::h4(
421
+					$question_group->name(),
422
+					'',
423
+					'ee-reg-form-qstn-grp-title section-title'
424
+				);
425
+			}
426
+		}
427
+		// group_desc
428
+		if ($question_group->show_group_desc() && $question_group->desc() !== '') {
429
+			$html .= EEH_HTML::p(
430
+				$question_group->desc(),
431
+				'',
432
+				$this->checkout->admin_request
433
+					? 'ee-reg-form-qstn-grp-desc-pg'
434
+					: 'ee-reg-form-qstn-grp-desc-pg small-text lt-grey-text'
435
+			);
436
+		}
437
+		return new EE_Form_Section_HTML($html);
438
+	}
439
+
440
+
441
+	/**
442
+	 * @access public
443
+	 * @return    EE_Form_Section_Proper
444
+	 * @throws \EE_Error
445
+	 */
446
+	private function _copy_attendee_info_form()
447
+	{
448
+		// array of params to pass to parent constructor
449
+		return new EE_Form_Section_Proper(
450
+			array(
451
+				'subsections'     => $this->_copy_attendee_info_inputs(),
452
+				'layout_strategy' => new EE_Template_Layout(
453
+					array(
454
+						'layout_template_file'     => SPCO_REG_STEPS_PATH
455
+													  . $this->_slug
456
+													  . DS
457
+													  . 'copy_attendee_info.template.php',
458
+						'begin_template_file'      => null,
459
+						'input_template_file'      => null,
460
+						'subsection_template_file' => null,
461
+						'end_template_file'        => null,
462
+					)
463
+				),
464
+			)
465
+		);
466
+	}
467
+
468
+
469
+	/**
470
+	 * _auto_copy_attendee_info
471
+	 *
472
+	 * @access public
473
+	 * @return EE_Form_Section_HTML
474
+	 */
475
+	private function _auto_copy_attendee_info()
476
+	{
477
+		return new EE_Form_Section_HTML(
478
+			EEH_Template::locate_template(
479
+				SPCO_REG_STEPS_PATH . $this->_slug . DS . '_auto_copy_attendee_info.template.php',
480
+				apply_filters(
481
+					'FHEE__EE_SPCO_Reg_Step_Attendee_Information__auto_copy_attendee_info__template_args',
482
+					array()
483
+				),
484
+				true,
485
+				true
486
+			)
487
+		);
488
+	}
489
+
490
+
491
+	/**
492
+	 * _copy_attendee_info_inputs
493
+	 *
494
+	 * @access public
495
+	 * @return array
496
+	 * @throws \EE_Error
497
+	 */
498
+	private function _copy_attendee_info_inputs()
499
+	{
500
+		$copy_attendee_info_inputs = array();
501
+		$prev_ticket               = null;
502
+		// grab the saved registrations from the transaction
503
+		$registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
504
+		foreach ($registrations as $registration) {
505
+			// for all  attendees other than the primary attendee
506
+			if ($registration instanceof EE_Registration && ! $registration->is_primary_registrant()) {
507
+				// if this is a new ticket OR if this is the very first additional attendee after the primary attendee
508
+				if ($registration->ticket()->ID() !== $prev_ticket) {
509
+					$item_name = $registration->ticket()->name();
510
+					$item_name .= $registration->ticket()->description() !== ''
511
+						? ' - ' . $registration->ticket()->description()
512
+						: '';
513
+					$copy_attendee_info_inputs['spco_copy_attendee_chk[ticket-' . $registration->ticket()->ID() . ']'] =
514
+						new EE_Form_Section_HTML(
515
+							'<h6 class="spco-copy-attendee-event-hdr">' . $item_name . '</h6>'
516
+						);
517
+					$prev_ticket = $registration->ticket()->ID();
518
+				}
519
+
520
+				$copy_attendee_info_inputs['spco_copy_attendee_chk[' . $registration->ID() . ']'] =
521
+					new EE_Checkbox_Multi_Input(
522
+						array(
523
+							$registration->ID() => sprintf(
524
+								esc_html__('Attendee #%s', 'event_espresso'),
525
+								$registration->count()
526
+							),
527
+						),
528
+						array(
529
+							'html_id'                 => 'spco-copy-attendee-chk-' . $registration->reg_url_link(),
530
+							'html_class'              => 'spco-copy-attendee-chk ee-do-not-validate',
531
+							'display_html_label_text' => false,
532
+						)
533
+					);
534
+			}
535
+		}
536
+		return $copy_attendee_info_inputs;
537
+	}
538
+
539
+
540
+	/**
541
+	 * _additional_primary_registrant_inputs
542
+	 *
543
+	 * @access public
544
+	 * @param EE_Registration $registration
545
+	 * @return    EE_Form_Input_Base
546
+	 * @throws \EE_Error
547
+	 */
548
+	private function _additional_primary_registrant_inputs(EE_Registration $registration)
549
+	{
550
+		// generate hidden input
551
+		return new EE_Hidden_Input(
552
+			array(
553
+				'html_id' => 'primary_registrant',
554
+				'default' => $registration->reg_url_link(),
555
+			)
556
+		);
557
+	}
558
+
559
+
560
+	/**
561
+	 * @access public
562
+	 * @param EE_Registration $registration
563
+	 * @param EE_Question     $question
564
+	 * @return EE_Form_Input_Base
565
+	 * @throws EE_Error
566
+	 * @throws InvalidArgumentException
567
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
568
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
569
+	 */
570
+	public function reg_form_question(EE_Registration $registration, EE_Question $question)
571
+	{
572
+
573
+		// if this question was for an attendee detail, then check for that answer
574
+		$answer_value = EEM_Answer::instance()->get_attendee_property_answer_value(
575
+			$registration,
576
+			$question->system_ID()
577
+		);
578
+		$answer       = $answer_value === null
579
+			? EEM_Answer::instance()->get_one(
580
+				array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()))
581
+			)
582
+			: null;
583
+		// if NOT returning to edit an existing registration
584
+		// OR if this question is for an attendee property
585
+		// OR we still don't have an EE_Answer object
586
+		if ($answer_value || ! $answer instanceof EE_Answer || ! $registration->reg_url_link()) {
587
+			// create an EE_Answer object for storing everything in
588
+			$answer = EE_Answer::new_instance(array(
589
+				'QST_ID' => $question->ID(),
590
+				'REG_ID' => $registration->ID(),
591
+			));
592
+		}
593
+		// verify instance
594
+		if ($answer instanceof EE_Answer) {
595
+			if (! empty($answer_value)) {
596
+				$answer->set('ANS_value', $answer_value);
597
+			}
598
+			$answer->cache('Question', $question);
599
+			//remember system ID had a bug where sometimes it could be null
600
+			$answer_cache_id = $question->is_system_question()
601
+				? $question->system_ID() . '-' . $registration->reg_url_link()
602
+				: $question->ID() . '-' . $registration->reg_url_link();
603
+			$registration->cache('Answer', $answer, $answer_cache_id);
604
+		}
605
+		return $this->_generate_question_input($registration, $question, $answer);
606
+	}
607
+
608
+
609
+	/**
610
+	 * @param EE_Registration $registration
611
+	 * @param EE_Question     $question
612
+	 * @param                 mixed EE_Answer|NULL      $answer
613
+	 * @return EE_Form_Input_Base
614
+	 * @throws \EE_Error
615
+	 */
616
+	private function _generate_question_input(EE_Registration $registration, EE_Question $question, $answer)
617
+	{
618
+		$identifier                             = $question->is_system_question()
619
+			? $question->system_ID()
620
+			: $question->ID();
621
+		$this->_required_questions[$identifier] = $question->required() ? true : false;
622
+		add_filter(
623
+			'FHEE__EE_Question__generate_form_input__country_options',
624
+			array($this, 'use_cached_countries_for_form_input'),
625
+			10,
626
+			4
627
+		);
628
+		add_filter(
629
+			'FHEE__EE_Question__generate_form_input__state_options',
630
+			array($this, 'use_cached_states_for_form_input'),
631
+			10,
632
+			4
633
+		);
634
+		$input_constructor_args                  = array(
635
+			'html_name'        => 'ee_reg_qstn[' . $registration->ID() . '][' . $identifier . ']',
636
+			'html_id'          => 'ee_reg_qstn-' . $registration->ID() . '-' . $identifier,
637
+			'html_class'       => 'ee-reg-qstn ee-reg-qstn-' . $identifier,
638
+			'html_label_id'    => 'ee_reg_qstn-' . $registration->ID() . '-' . $identifier,
639
+			'html_label_class' => 'ee-reg-qstn',
640
+		);
641
+		$input_constructor_args['html_label_id'] .= '-lbl';
642
+		if ($answer instanceof EE_Answer && $answer->ID()) {
643
+			$input_constructor_args['html_name']     .= '[' . $answer->ID() . ']';
644
+			$input_constructor_args['html_id']       .= '-' . $answer->ID();
645
+			$input_constructor_args['html_label_id'] .= '-' . $answer->ID();
646
+		}
647
+		$form_input = $question->generate_form_input(
648
+			$registration,
649
+			$answer,
650
+			$input_constructor_args
651
+		);
652
+		remove_filter(
653
+			'FHEE__EE_Question__generate_form_input__country_options',
654
+			array($this, 'use_cached_countries_for_form_input')
655
+		);
656
+		remove_filter(
657
+			'FHEE__EE_Question__generate_form_input__state_options',
658
+			array($this, 'use_cached_states_for_form_input')
659
+		);
660
+		return $form_input;
661
+	}
662
+
663
+
664
+	/**
665
+	 * Gets the list of countries for the form input
666
+	 *
667
+	 * @param array|null       $countries_list
668
+	 * @param \EE_Question     $question
669
+	 * @param \EE_Registration $registration
670
+	 * @param \EE_Answer       $answer
671
+	 * @return array 2d keys are country IDs, values are their names
672
+	 * @throws EE_Error
673
+	 * @throws InvalidArgumentException
674
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
675
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
676
+	 */
677
+	public function use_cached_countries_for_form_input(
678
+		$countries_list,
679
+		\EE_Question $question = null,
680
+		\EE_Registration $registration = null,
681
+		\EE_Answer $answer = null
682
+	) {
683
+		$country_options = array('' => '');
684
+		// get possibly cached list of countries
685
+		$countries = $this->checkout->action === 'process_reg_step'
686
+			? EEM_Country::instance()->get_all_countries()
687
+			: EEM_Country::instance()->get_all_active_countries();
688
+		if (! empty($countries)) {
689
+			foreach ($countries as $country) {
690
+				if ($country instanceof EE_Country) {
691
+					$country_options[$country->ID()] = $country->name();
692
+				}
693
+			}
694
+		}
695
+		if ($question instanceof EE_Question
696
+			&& $registration instanceof EE_Registration) {
697
+			$answer = EEM_Answer::instance()->get_one(
698
+				array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()))
699
+			);
700
+		} else {
701
+			$answer = EE_Answer::new_instance();
702
+		}
703
+		$country_options = apply_filters(
704
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__country_options',
705
+			$country_options,
706
+			$this,
707
+			$registration,
708
+			$question,
709
+			$answer
710
+		);
711
+		return $country_options;
712
+	}
713
+
714
+
715
+	/**
716
+	 * Gets the list of states for the form input
717
+	 *
718
+	 * @param array|null       $states_list
719
+	 * @param \EE_Question     $question
720
+	 * @param \EE_Registration $registration
721
+	 * @param \EE_Answer       $answer
722
+	 * @return array 2d keys are state IDs, values are their names
723
+	 * @throws EE_Error
724
+	 * @throws InvalidArgumentException
725
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
726
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
727
+	 */
728
+	public function use_cached_states_for_form_input(
729
+		$states_list,
730
+		\EE_Question $question = null,
731
+		\EE_Registration $registration = null,
732
+		\EE_Answer $answer = null
733
+	) {
734
+		$state_options = array('' => array('' => ''));
735
+		$states        = $this->checkout->action === 'process_reg_step'
736
+			? EEM_State::instance()->get_all_states()
737
+			: EEM_State::instance()->get_all_active_states();
738
+		if (! empty($states)) {
739
+			foreach ($states as $state) {
740
+				if ($state instanceof EE_State) {
741
+					$state_options[$state->country()->name()][$state->ID()] = $state->name();
742
+				}
743
+			}
744
+		}
745
+		$state_options = apply_filters(
746
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__state_options',
747
+			$state_options,
748
+			$this,
749
+			$registration,
750
+			$question,
751
+			$answer
752
+		);
753
+		return $state_options;
754
+	}
755
+
756
+
757
+
758
+
759
+
760
+
761
+	/********************************************************************************************************/
762
+	/****************************************  PROCESS REG STEP  ****************************************/
763
+	/********************************************************************************************************/
764
+	/**
765
+	 * @return bool
766
+	 * @throws EE_Error
767
+	 * @throws InvalidArgumentException
768
+	 * @throws ReflectionException
769
+	 * @throws RuntimeException
770
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
771
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
772
+	 */
773
+	public function process_reg_step()
774
+	{
775
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
776
+		// grab validated data from form
777
+		$valid_data = $this->checkout->current_step->valid_data();
778
+		// EEH_Debug_Tools::printr( $_REQUEST, '$_REQUEST', __FILE__, __LINE__ );
779
+		// EEH_Debug_Tools::printr( $valid_data, '$valid_data', __FILE__, __LINE__ );
780
+		// if we don't have any $valid_data then something went TERRIBLY WRONG !!!
781
+		if (empty($valid_data)) {
782
+			EE_Error::add_error(
783
+				esc_html__('No valid question responses were received.', 'event_espresso'),
784
+				__FILE__,
785
+				__FUNCTION__,
786
+				__LINE__
787
+			);
788
+			return false;
789
+		}
790
+		if (! $this->checkout->transaction instanceof EE_Transaction || ! $this->checkout->continue_reg) {
791
+			EE_Error::add_error(
792
+				esc_html__(
793
+					'A valid transaction could not be initiated for processing your registrations.',
794
+					'event_espresso'
795
+				),
796
+				__FILE__,
797
+				__FUNCTION__,
798
+				__LINE__
799
+			);
800
+			return false;
801
+		}
802
+		// get cached registrations
803
+		$registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
804
+		// verify we got the goods
805
+		if (empty($registrations)) {
806
+			//combine the old translated string with a new one, in order to not break translations
807
+			$error_message = esc_html__( 'Your form data could not be applied to any valid registrations.', 'event_espresso' )
808
+							 . sprintf(
809
+								 esc_html__('%3$sThis can sometimes happen if too much time has been taken to complete the registration process.%3$sPlease return to the %1$sEvent List%2$s and reselect your tickets. If the problem continues, please contact the site administrator.', 'event_espresso'),
810
+								 '<a href="' . get_post_type_archive_link('espresso_events') . '" >',
811
+								 '</a>',
812
+								 '<br />'
813
+							 );
814
+			EE_Error::add_error(
815
+				$error_message,
816
+				__FILE__,
817
+				__FUNCTION__,
818
+				__LINE__
819
+			);
820
+			return false;
821
+		}
822
+		// extract attendee info from form data and save to model objects
823
+		$registrations_processed = $this->_process_registrations($registrations, $valid_data);
824
+		// if first pass thru SPCO,
825
+		// then let's check processed registrations against the total number of tickets in the cart
826
+		if ($registrations_processed === false) {
827
+			// but return immediately if the previous step exited early due to errors
828
+			return false;
829
+		} elseif (! $this->checkout->revisit && $registrations_processed !== $this->checkout->total_ticket_count) {
830
+			// generate a correctly translated string for all possible singular/plural combinations
831
+			if ($this->checkout->total_ticket_count === 1 && $registrations_processed !== 1) {
832
+				$error_msg = sprintf(
833
+					esc_html__(
834
+						'There was %1$d ticket in the Event Queue, but %2$ds registrations were processed',
835
+						'event_espresso'
836
+					),
837
+					$this->checkout->total_ticket_count,
838
+					$registrations_processed
839
+				);
840
+			} elseif ($this->checkout->total_ticket_count !== 1 && $registrations_processed === 1) {
841
+				$error_msg = sprintf(
842
+					esc_html__(
843
+						'There was a total of %1$d tickets in the Event Queue, but only %2$ds registration was processed',
844
+						'event_espresso'
845
+					),
846
+					$this->checkout->total_ticket_count,
847
+					$registrations_processed
848
+				);
849
+			} else {
850
+				$error_msg = sprintf(
851
+					esc_html__(
852
+						'There was a total of %1$d tickets in the Event Queue, but %2$ds registrations were processed',
853
+						'event_espresso'
854
+					),
855
+					$this->checkout->total_ticket_count,
856
+					$registrations_processed
857
+				);
858
+			}
859
+			EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
860
+			return false;
861
+		}
862
+		// mark this reg step as completed
863
+		$this->set_completed();
864
+		$this->_set_success_message(
865
+			esc_html__('The Attendee Information Step has been successfully completed.', 'event_espresso')
866
+		);
867
+		//do action in case a plugin wants to do something with the data submitted in step 1.
868
+		//passes EE_Single_Page_Checkout, and it's posted data
869
+		do_action('AHEE__EE_Single_Page_Checkout__process_attendee_information__end', $this, $valid_data);
870
+		return true;
871
+	}
872
+
873
+
874
+	/**
875
+	 *    _process_registrations
876
+	 *
877
+	 * @param EE_Registration[] $registrations
878
+	 * @param array             $valid_data
879
+	 * @return bool|int
880
+	 * @throws \EventEspresso\core\exceptions\EntityNotFoundException
881
+	 * @throws EE_Error
882
+	 * @throws InvalidArgumentException
883
+	 * @throws ReflectionException
884
+	 * @throws RuntimeException
885
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
886
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
887
+	 */
888
+	private function _process_registrations($registrations = array(), $valid_data = array())
889
+	{
890
+		// load resources and set some defaults
891
+		EE_Registry::instance()->load_model('Attendee');
892
+		// holder for primary registrant attendee object
893
+		$this->checkout->primary_attendee_obj = null;
894
+		// array for tracking reg form data for the primary registrant
895
+		$primary_registrant = array(
896
+			'line_item_id' => null,
897
+		);
898
+		$copy_primary       = false;
899
+		// reg form sections that do not contain inputs
900
+		$non_input_form_sections = array(
901
+			'primary_registrant',
902
+			'additional_attendee_reg_info',
903
+			'spco_copy_attendee_chk',
904
+		);
905
+		// attendee counter
906
+		$att_nmbr = 0;
907
+		// grab the saved registrations from the transaction
908
+		foreach ($registrations as $registration) {
909
+			// verify EE_Registration object
910
+			if (! $registration instanceof EE_Registration) {
911
+				EE_Error::add_error(
912
+					esc_html__(
913
+						'An invalid Registration object was discovered when attempting to process your registration information.',
914
+						'event_espresso'
915
+					),
916
+					__FILE__,
917
+					__FUNCTION__,
918
+					__LINE__
919
+				);
920
+				return false;
921
+			}
922
+			/** @var string $reg_url_link */
923
+			$reg_url_link = $registration->reg_url_link();
924
+			// reg_url_link exists ?
925
+			if (! empty($reg_url_link)) {
926
+				// should this registration be processed during this visit ?
927
+				if ($this->checkout->visit_allows_processing_of_this_registration($registration)) {
928
+					// if NOT revisiting, then let's save the registration now,
929
+					// so that we have a REG_ID to use when generating other objects
930
+					if (! $this->checkout->revisit) {
931
+						$registration->save();
932
+					}
933
+					/**
934
+					 * This allows plugins to trigger a fail on processing of a
935
+					 * registration for any conditions they may have for it to pass.
936
+					 *
937
+					 * @var bool   if true is returned by the plugin then the
938
+					 *            registration processing is halted.
939
+					 */
940
+					if (apply_filters(
941
+						'FHEE__EE_SPCO_Reg_Step_Attendee_Information___process_registrations__pre_registration_process',
942
+						false,
943
+						$att_nmbr,
944
+						$registration,
945
+						$registrations,
946
+						$valid_data,
947
+						$this
948
+					)) {
949
+						return false;
950
+					}
951
+
952
+					// Houston, we have a registration!
953
+					$att_nmbr++;
954
+					$this->_attendee_data[$reg_url_link] = array();
955
+					// grab any existing related answer objects
956
+					$this->_registration_answers = $registration->answers();
957
+					// unset( $valid_data[ $reg_url_link ]['additional_attendee_reg_info'] );
958
+					if (isset($valid_data[$reg_url_link])) {
959
+						// do we need to copy basic info from primary attendee ?
960
+						$copy_primary = isset($valid_data[$reg_url_link]['additional_attendee_reg_info'])
961
+										&& absint($valid_data[$reg_url_link]['additional_attendee_reg_info']) === 0
962
+							? true
963
+							: false;
964
+						// filter form input data for this registration
965
+						$valid_data[$reg_url_link] = (array)apply_filters(
966
+							'FHEE__EE_Single_Page_Checkout__process_attendee_information__valid_data_line_item',
967
+							$valid_data[$reg_url_link]
968
+						);
969
+						if (isset($valid_data['primary_attendee'])) {
970
+							$primary_registrant['line_item_id'] = ! empty($valid_data['primary_attendee'])
971
+								? $valid_data['primary_attendee']
972
+								: false;
973
+							unset($valid_data['primary_attendee']);
974
+						}
975
+						// now loop through our array of valid post data && process attendee reg forms
976
+						foreach ($valid_data[$reg_url_link] as $form_section => $form_inputs) {
977
+							if (! in_array($form_section, $non_input_form_sections)) {
978
+								foreach ($form_inputs as $form_input => $input_value) {
979
+									// \EEH_Debug_Tools::printr( $input_value, $form_input, __FILE__, __LINE__ );
980
+									// check for critical inputs
981
+									if (! $this->_verify_critical_attendee_details_are_set_and_validate_email(
982
+										$form_input,
983
+										$input_value
984
+									)
985
+									) {
986
+										return false;
987
+									}
988
+									// store a bit of data about the primary attendee
989
+									if ($att_nmbr === 1
990
+										&& ! empty($input_value)
991
+										&& $reg_url_link === $primary_registrant['line_item_id']
992
+									) {
993
+										$primary_registrant[$form_input] = $input_value;
994
+									} elseif ($copy_primary
995
+										&& $input_value === null
996
+										&& isset($primary_registrant[$form_input])
997
+									) {
998
+										$input_value = $primary_registrant[$form_input];
999
+									}
1000
+									// now attempt to save the input data
1001
+									if (! $this->_save_registration_form_input(
1002
+										$registration,
1003
+										$form_input,
1004
+										$input_value
1005
+									)
1006
+									) {
1007
+										EE_Error::add_error(
1008
+											sprintf(
1009
+												esc_html__(
1010
+													'Unable to save registration form data for the form input: "%1$s" with the submitted value: "%2$s"',
1011
+													'event_espresso'
1012
+												),
1013
+												$form_input,
1014
+												$input_value
1015
+											),
1016
+											__FILE__,
1017
+											__FUNCTION__,
1018
+											__LINE__
1019
+										);
1020
+										return false;
1021
+									}
1022
+								}
1023
+							}
1024
+						}  // end of foreach ( $valid_data[ $reg_url_link ] as $form_section => $form_inputs )
1025
+					}
1026
+					//EEH_Debug_Tools::printr( $this->_attendee_data, '$this->_attendee_data', __FILE__, __LINE__ );
1027
+					// this registration does not require additional attendee information ?
1028
+					if ($copy_primary
1029
+						&& $att_nmbr > 1
1030
+						&& $this->checkout->primary_attendee_obj instanceof EE_Attendee
1031
+					) {
1032
+						// just copy the primary registrant
1033
+						$attendee = $this->checkout->primary_attendee_obj;
1034
+					} else {
1035
+						// ensure critical details are set for additional attendees
1036
+						$this->_attendee_data[$reg_url_link] = $att_nmbr > 1
1037
+							? $this->_copy_critical_attendee_details_from_primary_registrant(
1038
+								$this->_attendee_data[$reg_url_link]
1039
+							)
1040
+							: $this->_attendee_data[$reg_url_link];
1041
+						// execute create attendee command (which may return an existing attendee)
1042
+						$attendee = EE_Registry::instance()->BUS->execute(
1043
+							new CreateAttendeeCommand(
1044
+								$this->_attendee_data[$reg_url_link],
1045
+								$registration
1046
+							)
1047
+						);
1048
+						// who's #1 ?
1049
+						if ($att_nmbr === 1) {
1050
+							$this->checkout->primary_attendee_obj = $attendee;
1051
+						}
1052
+					}
1053
+					// EEH_Debug_Tools::printr( $attendee, '$attendee', __FILE__, __LINE__ );
1054
+					// add relation to registration, set attendee ID, and cache attendee
1055
+					$this->_associate_attendee_with_registration($registration, $attendee);
1056
+					// \EEH_Debug_Tools::printr( $registration, '$registration', __FILE__, __LINE__ );
1057
+					if (! $registration->attendee() instanceof EE_Attendee) {
1058
+						EE_Error::add_error(
1059
+							sprintf(
1060
+								esc_html__(
1061
+									'Registration %s has an invalid or missing Attendee object.',
1062
+									'event_espresso'
1063
+								),
1064
+								$reg_url_link
1065
+							),
1066
+							__FILE__,
1067
+							__FUNCTION__,
1068
+							__LINE__
1069
+						);
1070
+						return false;
1071
+					}
1072
+					/** @type EE_Registration_Processor $registration_processor */
1073
+					$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
1074
+					// at this point, we should have enough details about the registrant to consider the registration
1075
+					// NOT incomplete
1076
+					$registration_processor->toggle_incomplete_registration_status_to_default(
1077
+						$registration,
1078
+						false,
1079
+						new Context(
1080
+							'spco_reg_step_attendee_information_process_registrations',
1081
+							esc_html__(
1082
+								'Finished populating registration with details from the registration form after submitting the Attendee Information Reg Step.',
1083
+								'event_espresso'
1084
+							)
1085
+						)
1086
+					);
1087
+					// we can also consider the TXN to not have been failed, so temporarily upgrade it's status to
1088
+					// abandoned
1089
+					$this->checkout->transaction->toggle_failed_transaction_status();
1090
+					// if we've gotten this far, then let's save what we have
1091
+					$registration->save();
1092
+					// add relation between TXN and registration
1093
+					$this->_associate_registration_with_transaction($registration);
1094
+				}
1095
+			} else {
1096
+				EE_Error::add_error(
1097
+					esc_html__(
1098
+						'An invalid or missing line item ID was encountered while attempting to process the registration form.',
1099
+						'event_espresso'
1100
+					),
1101
+					__FILE__,
1102
+					__FUNCTION__,
1103
+					__LINE__
1104
+				);
1105
+				// remove malformed data
1106
+				unset($valid_data[$reg_url_link]);
1107
+				return false;
1108
+			}
1109
+
1110
+		} // end of foreach ( $this->checkout->transaction->registrations()  as $registration )
1111
+		return $att_nmbr;
1112
+	}
1113
+
1114
+
1115
+	/**
1116
+	 *    _save_registration_form_input
1117
+	 *
1118
+	 * @param EE_Registration $registration
1119
+	 * @param string          $form_input
1120
+	 * @param string          $input_value
1121
+	 * @return bool
1122
+	 * @throws EE_Error
1123
+	 * @throws InvalidArgumentException
1124
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
1125
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
1126
+	 */
1127
+	private function _save_registration_form_input(
1128
+		EE_Registration $registration,
1129
+		$form_input = '',
1130
+		$input_value = ''
1131
+	) {
1132
+		// \EEH_Debug_Tools::printr( __FUNCTION__, __CLASS__, __FILE__, __LINE__, 2 );
1133
+		// \EEH_Debug_Tools::printr( $form_input, '$form_input', __FILE__, __LINE__ );
1134
+		// \EEH_Debug_Tools::printr( $input_value, '$input_value', __FILE__, __LINE__ );
1135
+		// allow for plugins to hook in and do their own processing of the form input.
1136
+		// For plugins to bypass normal processing here, they just need to return a boolean value.
1137
+		if (apply_filters(
1138
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___save_registration_form_input',
1139
+			false,
1140
+			$registration,
1141
+			$form_input,
1142
+			$input_value,
1143
+			$this
1144
+		)) {
1145
+			return true;
1146
+		}
1147
+		/*
1148 1148
          * $answer_cache_id is the key used to find the EE_Answer we want
1149 1149
          * @see https://events.codebasehq.com/projects/event-espresso/tickets/10477
1150 1150
          */
1151
-        $answer_cache_id = $this->checkout->reg_url_link
1152
-            ? $form_input . '-' . $registration->reg_url_link()
1153
-            : $form_input;
1154
-        $answer_is_obj   = isset($this->_registration_answers[$answer_cache_id])
1155
-                           && $this->_registration_answers[$answer_cache_id] instanceof EE_Answer
1156
-            ? true
1157
-            : false;
1158
-        //rename form_inputs if they are EE_Attendee properties
1159
-        switch ((string) $form_input) {
1160
-            case 'state':
1161
-            case 'STA_ID':
1162
-                $attendee_property = true;
1163
-                $form_input        = 'STA_ID';
1164
-                break;
1165
-
1166
-            case 'country':
1167
-            case 'CNT_ISO':
1168
-                $attendee_property = true;
1169
-                $form_input        = 'CNT_ISO';
1170
-                break;
1171
-
1172
-            default:
1173
-                $ATT_input = 'ATT_' . $form_input;
1174
-                //EEH_Debug_Tools::printr( $ATT_input, '$ATT_input', __FILE__, __LINE__ );
1175
-                $attendee_property = EEM_Attendee::instance()->has_field($ATT_input) ? true : false;
1176
-                $form_input        = $attendee_property ? 'ATT_' . $form_input : $form_input;
1177
-        }
1178
-        // EEH_Debug_Tools::printr( $answer_cache_id, '$answer_cache_id', __FILE__, __LINE__ );
1179
-        // EEH_Debug_Tools::printr( $attendee_property, '$attendee_property', __FILE__, __LINE__ );
1180
-        // EEH_Debug_Tools::printr( $answer_is_obj, '$answer_is_obj', __FILE__, __LINE__ );
1181
-        // if this form input has a corresponding attendee property
1182
-        if ($attendee_property) {
1183
-            $this->_attendee_data[$registration->reg_url_link()][$form_input] = $input_value;
1184
-            if ($answer_is_obj) {
1185
-                // and delete the corresponding answer since we won't be storing this data in that object
1186
-                $registration->_remove_relation_to($this->_registration_answers[$answer_cache_id], 'Answer');
1187
-                $this->_registration_answers[$answer_cache_id]->delete_permanently();
1188
-            }
1189
-            return true;
1190
-        } elseif ($answer_is_obj) {
1191
-            // save this data to the answer object
1192
-            $this->_registration_answers[$answer_cache_id]->set_value($input_value);
1193
-            $result = $this->_registration_answers[$answer_cache_id]->save();
1194
-            return $result !== false ? true : false;
1195
-        } else {
1196
-            foreach ($this->_registration_answers as $answer) {
1197
-                if ($answer instanceof EE_Answer && $answer->question_ID() === $answer_cache_id) {
1198
-                    $answer->set_value($input_value);
1199
-                    $result = $answer->save();
1200
-                    return $result !== false ? true : false;
1201
-                }
1202
-            }
1203
-        }
1204
-        return false;
1205
-    }
1206
-
1207
-
1208
-    /**
1209
-     *    _verify_critical_attendee_details_are_set
1210
-     *
1211
-     * @param string $form_input
1212
-     * @param string $input_value
1213
-     * @return boolean
1214
-     */
1215
-    private function _verify_critical_attendee_details_are_set_and_validate_email(
1216
-        $form_input = '',
1217
-        $input_value = ''
1218
-    ) {
1219
-        if (empty($input_value)) {
1220
-            // if the form input isn't marked as being required, then just return
1221
-            if (! isset($this->_required_questions[$form_input]) || ! $this->_required_questions[$form_input]) {
1222
-                return true;
1223
-            }
1224
-            switch ($form_input) {
1225
-                case 'fname':
1226
-                    EE_Error::add_error(
1227
-                        esc_html__('First Name is a required value.', 'event_espresso'),
1228
-                        __FILE__,
1229
-                        __FUNCTION__,
1230
-                        __LINE__
1231
-                    );
1232
-                    return false;
1233
-                    break;
1234
-                case 'lname':
1235
-                    EE_Error::add_error(
1236
-                        esc_html__('Last Name is a required value.', 'event_espresso'),
1237
-                        __FILE__,
1238
-                        __FUNCTION__,
1239
-                        __LINE__
1240
-                    );
1241
-                    return false;
1242
-                    break;
1243
-                case 'email':
1244
-                    EE_Error::add_error(
1245
-                        esc_html__('Please enter a valid email address.', 'event_espresso'),
1246
-                        __FILE__,
1247
-                        __FUNCTION__,
1248
-                        __LINE__
1249
-                    );
1250
-                    return false;
1251
-                    break;
1252
-            }
1253
-        }
1254
-        return true;
1255
-    }
1256
-
1257
-
1258
-    /**
1259
-     *    _associate_attendee_with_registration
1260
-     *
1261
-     * @param EE_Registration $registration
1262
-     * @param EE_Attendee     $attendee
1263
-     * @return void
1264
-     * @throws EE_Error
1265
-     * @throws RuntimeException
1266
-     */
1267
-    private function _associate_attendee_with_registration(EE_Registration $registration, EE_Attendee $attendee)
1268
-    {
1269
-        // add relation to attendee
1270
-        $registration->_add_relation_to($attendee, 'Attendee');
1271
-        $registration->set_attendee_id($attendee->ID());
1272
-        $registration->update_cache_after_object_save('Attendee', $attendee);
1273
-    }
1274
-
1275
-
1276
-    /**
1277
-     *    _associate_registration_with_transaction
1278
-     *
1279
-     * @param EE_Registration $registration
1280
-     * @return void
1281
-     * @throws \EE_Error
1282
-     */
1283
-    private function _associate_registration_with_transaction(EE_Registration $registration)
1284
-    {
1285
-        // add relation to registration
1286
-        $this->checkout->transaction->_add_relation_to($registration, 'Registration');
1287
-        $this->checkout->transaction->update_cache_after_object_save('Registration', $registration);
1288
-    }
1289
-
1290
-
1291
-    /**
1292
-     *    _copy_critical_attendee_details_from_primary_registrant
1293
-     *    ensures that all attendees at least have data for first name, last name, and email address
1294
-     *
1295
-     * @param array $attendee_data
1296
-     * @return array
1297
-     * @throws \EE_Error
1298
-     */
1299
-    private function _copy_critical_attendee_details_from_primary_registrant($attendee_data = array())
1300
-    {
1301
-        // bare minimum critical details include first name, last name, email address
1302
-        $critical_attendee_details = array('ATT_fname', 'ATT_lname', 'ATT_email');
1303
-        // add address info to critical details?
1304
-        if (apply_filters(
1305
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information__merge_address_details_with_critical_attendee_details',
1306
-            false
1307
-        )) {
1308
-            $address_details           = array(
1309
-                'ATT_address',
1310
-                'ATT_address2',
1311
-                'ATT_city',
1312
-                'STA_ID',
1313
-                'CNT_ISO',
1314
-                'ATT_zip',
1315
-                'ATT_phone',
1316
-            );
1317
-            $critical_attendee_details = array_merge($critical_attendee_details, $address_details);
1318
-        }
1319
-        foreach ($critical_attendee_details as $critical_attendee_detail) {
1320
-            if (! isset($attendee_data[$critical_attendee_detail])
1321
-                || empty($attendee_data[$critical_attendee_detail])
1322
-            ) {
1323
-                $attendee_data[$critical_attendee_detail] = $this->checkout->primary_attendee_obj->get(
1324
-                    $critical_attendee_detail
1325
-                );
1326
-            }
1327
-        }
1328
-        return $attendee_data;
1329
-    }
1330
-
1331
-
1332
-    /**
1333
-     *    update_reg_step
1334
-     *    this is the final step after a user  revisits the site to edit their attendee information
1335
-     *    this gets called AFTER the process_reg_step() method above
1336
-     *
1337
-     * @return bool
1338
-     * @throws EE_Error
1339
-     * @throws InvalidArgumentException
1340
-     * @throws ReflectionException
1341
-     * @throws RuntimeException
1342
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
1343
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
1344
-     */
1345
-    public function update_reg_step()
1346
-    {
1347
-        // save everything
1348
-        if ($this->process_reg_step()) {
1349
-            $this->checkout->redirect     = true;
1350
-            $this->checkout->redirect_url = add_query_arg(
1351
-                array(
1352
-                    'e_reg_url_link' => $this->checkout->reg_url_link,
1353
-                    'revisit'        => true,
1354
-                ),
1355
-                $this->checkout->thank_you_page_url
1356
-            );
1357
-            $this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1358
-            return true;
1359
-        }
1360
-        return false;
1361
-    }
1151
+		$answer_cache_id = $this->checkout->reg_url_link
1152
+			? $form_input . '-' . $registration->reg_url_link()
1153
+			: $form_input;
1154
+		$answer_is_obj   = isset($this->_registration_answers[$answer_cache_id])
1155
+						   && $this->_registration_answers[$answer_cache_id] instanceof EE_Answer
1156
+			? true
1157
+			: false;
1158
+		//rename form_inputs if they are EE_Attendee properties
1159
+		switch ((string) $form_input) {
1160
+			case 'state':
1161
+			case 'STA_ID':
1162
+				$attendee_property = true;
1163
+				$form_input        = 'STA_ID';
1164
+				break;
1165
+
1166
+			case 'country':
1167
+			case 'CNT_ISO':
1168
+				$attendee_property = true;
1169
+				$form_input        = 'CNT_ISO';
1170
+				break;
1171
+
1172
+			default:
1173
+				$ATT_input = 'ATT_' . $form_input;
1174
+				//EEH_Debug_Tools::printr( $ATT_input, '$ATT_input', __FILE__, __LINE__ );
1175
+				$attendee_property = EEM_Attendee::instance()->has_field($ATT_input) ? true : false;
1176
+				$form_input        = $attendee_property ? 'ATT_' . $form_input : $form_input;
1177
+		}
1178
+		// EEH_Debug_Tools::printr( $answer_cache_id, '$answer_cache_id', __FILE__, __LINE__ );
1179
+		// EEH_Debug_Tools::printr( $attendee_property, '$attendee_property', __FILE__, __LINE__ );
1180
+		// EEH_Debug_Tools::printr( $answer_is_obj, '$answer_is_obj', __FILE__, __LINE__ );
1181
+		// if this form input has a corresponding attendee property
1182
+		if ($attendee_property) {
1183
+			$this->_attendee_data[$registration->reg_url_link()][$form_input] = $input_value;
1184
+			if ($answer_is_obj) {
1185
+				// and delete the corresponding answer since we won't be storing this data in that object
1186
+				$registration->_remove_relation_to($this->_registration_answers[$answer_cache_id], 'Answer');
1187
+				$this->_registration_answers[$answer_cache_id]->delete_permanently();
1188
+			}
1189
+			return true;
1190
+		} elseif ($answer_is_obj) {
1191
+			// save this data to the answer object
1192
+			$this->_registration_answers[$answer_cache_id]->set_value($input_value);
1193
+			$result = $this->_registration_answers[$answer_cache_id]->save();
1194
+			return $result !== false ? true : false;
1195
+		} else {
1196
+			foreach ($this->_registration_answers as $answer) {
1197
+				if ($answer instanceof EE_Answer && $answer->question_ID() === $answer_cache_id) {
1198
+					$answer->set_value($input_value);
1199
+					$result = $answer->save();
1200
+					return $result !== false ? true : false;
1201
+				}
1202
+			}
1203
+		}
1204
+		return false;
1205
+	}
1206
+
1207
+
1208
+	/**
1209
+	 *    _verify_critical_attendee_details_are_set
1210
+	 *
1211
+	 * @param string $form_input
1212
+	 * @param string $input_value
1213
+	 * @return boolean
1214
+	 */
1215
+	private function _verify_critical_attendee_details_are_set_and_validate_email(
1216
+		$form_input = '',
1217
+		$input_value = ''
1218
+	) {
1219
+		if (empty($input_value)) {
1220
+			// if the form input isn't marked as being required, then just return
1221
+			if (! isset($this->_required_questions[$form_input]) || ! $this->_required_questions[$form_input]) {
1222
+				return true;
1223
+			}
1224
+			switch ($form_input) {
1225
+				case 'fname':
1226
+					EE_Error::add_error(
1227
+						esc_html__('First Name is a required value.', 'event_espresso'),
1228
+						__FILE__,
1229
+						__FUNCTION__,
1230
+						__LINE__
1231
+					);
1232
+					return false;
1233
+					break;
1234
+				case 'lname':
1235
+					EE_Error::add_error(
1236
+						esc_html__('Last Name is a required value.', 'event_espresso'),
1237
+						__FILE__,
1238
+						__FUNCTION__,
1239
+						__LINE__
1240
+					);
1241
+					return false;
1242
+					break;
1243
+				case 'email':
1244
+					EE_Error::add_error(
1245
+						esc_html__('Please enter a valid email address.', 'event_espresso'),
1246
+						__FILE__,
1247
+						__FUNCTION__,
1248
+						__LINE__
1249
+					);
1250
+					return false;
1251
+					break;
1252
+			}
1253
+		}
1254
+		return true;
1255
+	}
1256
+
1257
+
1258
+	/**
1259
+	 *    _associate_attendee_with_registration
1260
+	 *
1261
+	 * @param EE_Registration $registration
1262
+	 * @param EE_Attendee     $attendee
1263
+	 * @return void
1264
+	 * @throws EE_Error
1265
+	 * @throws RuntimeException
1266
+	 */
1267
+	private function _associate_attendee_with_registration(EE_Registration $registration, EE_Attendee $attendee)
1268
+	{
1269
+		// add relation to attendee
1270
+		$registration->_add_relation_to($attendee, 'Attendee');
1271
+		$registration->set_attendee_id($attendee->ID());
1272
+		$registration->update_cache_after_object_save('Attendee', $attendee);
1273
+	}
1274
+
1275
+
1276
+	/**
1277
+	 *    _associate_registration_with_transaction
1278
+	 *
1279
+	 * @param EE_Registration $registration
1280
+	 * @return void
1281
+	 * @throws \EE_Error
1282
+	 */
1283
+	private function _associate_registration_with_transaction(EE_Registration $registration)
1284
+	{
1285
+		// add relation to registration
1286
+		$this->checkout->transaction->_add_relation_to($registration, 'Registration');
1287
+		$this->checkout->transaction->update_cache_after_object_save('Registration', $registration);
1288
+	}
1289
+
1290
+
1291
+	/**
1292
+	 *    _copy_critical_attendee_details_from_primary_registrant
1293
+	 *    ensures that all attendees at least have data for first name, last name, and email address
1294
+	 *
1295
+	 * @param array $attendee_data
1296
+	 * @return array
1297
+	 * @throws \EE_Error
1298
+	 */
1299
+	private function _copy_critical_attendee_details_from_primary_registrant($attendee_data = array())
1300
+	{
1301
+		// bare minimum critical details include first name, last name, email address
1302
+		$critical_attendee_details = array('ATT_fname', 'ATT_lname', 'ATT_email');
1303
+		// add address info to critical details?
1304
+		if (apply_filters(
1305
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information__merge_address_details_with_critical_attendee_details',
1306
+			false
1307
+		)) {
1308
+			$address_details           = array(
1309
+				'ATT_address',
1310
+				'ATT_address2',
1311
+				'ATT_city',
1312
+				'STA_ID',
1313
+				'CNT_ISO',
1314
+				'ATT_zip',
1315
+				'ATT_phone',
1316
+			);
1317
+			$critical_attendee_details = array_merge($critical_attendee_details, $address_details);
1318
+		}
1319
+		foreach ($critical_attendee_details as $critical_attendee_detail) {
1320
+			if (! isset($attendee_data[$critical_attendee_detail])
1321
+				|| empty($attendee_data[$critical_attendee_detail])
1322
+			) {
1323
+				$attendee_data[$critical_attendee_detail] = $this->checkout->primary_attendee_obj->get(
1324
+					$critical_attendee_detail
1325
+				);
1326
+			}
1327
+		}
1328
+		return $attendee_data;
1329
+	}
1330
+
1331
+
1332
+	/**
1333
+	 *    update_reg_step
1334
+	 *    this is the final step after a user  revisits the site to edit their attendee information
1335
+	 *    this gets called AFTER the process_reg_step() method above
1336
+	 *
1337
+	 * @return bool
1338
+	 * @throws EE_Error
1339
+	 * @throws InvalidArgumentException
1340
+	 * @throws ReflectionException
1341
+	 * @throws RuntimeException
1342
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
1343
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
1344
+	 */
1345
+	public function update_reg_step()
1346
+	{
1347
+		// save everything
1348
+		if ($this->process_reg_step()) {
1349
+			$this->checkout->redirect     = true;
1350
+			$this->checkout->redirect_url = add_query_arg(
1351
+				array(
1352
+					'e_reg_url_link' => $this->checkout->reg_url_link,
1353
+					'revisit'        => true,
1354
+				),
1355
+				$this->checkout->thank_you_page_url
1356
+			);
1357
+			$this->checkout->json_response->set_redirect_url($this->checkout->redirect_url);
1358
+			return true;
1359
+		}
1360
+		return false;
1361
+	}
1362 1362
 }
Please login to merge, or discard this patch.
Spacing   +53 added lines, -54 removed lines patch added patch discarded remove patch
@@ -48,7 +48,7 @@  discard block
 block discarded – undo
48 48
     {
49 49
         $this->_slug     = 'attendee_information';
50 50
         $this->_name     = esc_html__('Attendee Information', 'event_espresso');
51
-        $this->_template = SPCO_REG_STEPS_PATH . $this->_slug . DS . 'attendee_info_main.template.php';
51
+        $this->_template = SPCO_REG_STEPS_PATH.$this->_slug.DS.'attendee_info_main.template.php';
52 52
         $this->checkout  = $checkout;
53 53
         $this->_reset_success_message();
54 54
         $this->set_instructions(
@@ -59,11 +59,11 @@  discard block
 block discarded – undo
59 59
 
60 60
     public function translate_js_strings()
61 61
     {
62
-        EE_Registry::$i18n_js_strings['required_field']            = esc_html__(
62
+        EE_Registry::$i18n_js_strings['required_field'] = esc_html__(
63 63
             ' is a required question.',
64 64
             'event_espresso'
65 65
         );
66
-        EE_Registry::$i18n_js_strings['required_multi_field']      = esc_html__(
66
+        EE_Registry::$i18n_js_strings['required_multi_field'] = esc_html__(
67 67
             ' is a required question. Please enter a value for at least one of the options.',
68 68
             'event_espresso'
69 69
         );
@@ -71,18 +71,18 @@  discard block
 block discarded – undo
71 71
             'Please answer all required questions correctly before proceeding.',
72 72
             'event_espresso'
73 73
         );
74
-        EE_Registry::$i18n_js_strings['attendee_info_copied']      = sprintf(
74
+        EE_Registry::$i18n_js_strings['attendee_info_copied'] = sprintf(
75 75
             esc_html__(
76 76
                 'The attendee information was successfully copied.%sPlease ensure the rest of the registration form is completed before proceeding.',
77 77
                 'event_espresso'
78 78
             ),
79 79
             '<br/>'
80 80
         );
81
-        EE_Registry::$i18n_js_strings['attendee_info_copy_error']  = esc_html__(
81
+        EE_Registry::$i18n_js_strings['attendee_info_copy_error'] = esc_html__(
82 82
             'An unknown error occurred on the server while attempting to copy the attendee information. Please refresh the page and try again.',
83 83
             'event_espresso'
84 84
         );
85
-        EE_Registry::$i18n_js_strings['enter_valid_email']         = esc_html__(
85
+        EE_Registry::$i18n_js_strings['enter_valid_email'] = esc_html__(
86 86
             'You must enter a valid email address.',
87 87
             'event_espresso'
88 88
         );
@@ -145,11 +145,11 @@  discard block
 block discarded – undo
145 145
                     && $this->checkout->visit_allows_processing_of_this_registration($registration)
146 146
                 ) {
147 147
                     $subsection = $this->_registrations_reg_form($registration);
148
-                    if(!$subsection instanceof EE_Form_Section_Proper) {
148
+                    if ( ! $subsection instanceof EE_Form_Section_Proper) {
149 149
                         continue;
150 150
                     }
151
-                    $subsections[ $registration->reg_url_link() ] = $subsection;
152
-                    if (! $this->checkout->admin_request) {
151
+                    $subsections[$registration->reg_url_link()] = $subsection;
152
+                    if ( ! $this->checkout->admin_request) {
153 153
                         $template_args['registrations'][$registration->reg_url_link()]    = $registration;
154 154
                         $template_args['ticket_count'][$registration->ticket()->ID()]     = isset(
155 155
                             $template_args['ticket_count'][$registration->ticket()->ID()]
@@ -197,8 +197,7 @@  discard block
 block discarded – undo
197 197
                 'html_id'         => $this->reg_form_name(),
198 198
                 'subsections'     => $subsections,
199 199
                 'layout_strategy' => $this->checkout->admin_request ?
200
-                    new EE_Div_Per_Section_Layout() :
201
-                    new EE_Template_Layout(
200
+                    new EE_Div_Per_Section_Layout() : new EE_Template_Layout(
202 201
                         array(
203 202
                             'layout_template_file' => $this->_template, // layout_template
204 203
                             'template_args'        => $template_args,
@@ -241,7 +240,7 @@  discard block
 block discarded – undo
241 240
             if ($question_groups) {
242 241
                 // array of params to pass to parent constructor
243 242
                 $form_args = array(
244
-                    'html_id'         => 'ee-registration-' . $registration->reg_url_link(),
243
+                    'html_id'         => 'ee-registration-'.$registration->reg_url_link(),
245 244
                     'html_class'      => 'ee-reg-form-attendee-dv',
246 245
                     'html_style'      => $this->checkout->admin_request
247 246
                         ? 'padding:0em 2em 1em; margin:3em 0 0; border:1px solid #ddd;'
@@ -297,7 +296,7 @@  discard block
 block discarded – undo
297 296
         // generate hidden input
298 297
         return new EE_Hidden_Input(
299 298
             array(
300
-                'html_id' => 'additional-attendee-reg-info-' . $registration->reg_url_link(),
299
+                'html_id' => 'additional-attendee-reg-info-'.$registration->reg_url_link(),
301 300
                 'default' => $additional_attendee_reg_info,
302 301
             )
303 302
         );
@@ -317,11 +316,11 @@  discard block
 block discarded – undo
317 316
     {
318 317
         // array of params to pass to parent constructor
319 318
         $form_args = array(
320
-            'html_id'         => 'ee-reg-form-qstn-grp-' . $question_group->identifier() . '-' . $registration->ID(),
319
+            'html_id'         => 'ee-reg-form-qstn-grp-'.$question_group->identifier().'-'.$registration->ID(),
321 320
             'html_class'      => $this->checkout->admin_request
322 321
                 ? 'form-table ee-reg-form-qstn-grp-dv'
323 322
                 : 'ee-reg-form-qstn-grp-dv',
324
-            'html_label_id'   => 'ee-reg-form-qstn-grp-' . $question_group->identifier() .  '-' . $registration->ID() . '-lbl',
323
+            'html_label_id'   => 'ee-reg-form-qstn-grp-'.$question_group->identifier().'-'.$registration->ID().'-lbl',
325 324
             'subsections'     => array(
326 325
                 'reg_form_qstn_grp_hdr' => $this->_question_group_header($question_group),
327 326
             ),
@@ -332,7 +331,7 @@  discard block
 block discarded – undo
332 331
         // where params
333 332
         $query_params = array('QST_deleted' => 0);
334 333
         // don't load admin only questions on the frontend
335
-        if (! $this->checkout->admin_request) {
334
+        if ( ! $this->checkout->admin_request) {
336 335
             $query_params['QST_admin_only'] = array('!=', true);
337 336
         }
338 337
         $questions = $question_group->get_many_related(
@@ -476,7 +475,7 @@  discard block
 block discarded – undo
476 475
     {
477 476
         return new EE_Form_Section_HTML(
478 477
             EEH_Template::locate_template(
479
-                SPCO_REG_STEPS_PATH . $this->_slug . DS . '_auto_copy_attendee_info.template.php',
478
+                SPCO_REG_STEPS_PATH.$this->_slug.DS.'_auto_copy_attendee_info.template.php',
480 479
                 apply_filters(
481 480
                     'FHEE__EE_SPCO_Reg_Step_Attendee_Information__auto_copy_attendee_info__template_args',
482 481
                     array()
@@ -508,16 +507,16 @@  discard block
 block discarded – undo
508 507
                 if ($registration->ticket()->ID() !== $prev_ticket) {
509 508
                     $item_name = $registration->ticket()->name();
510 509
                     $item_name .= $registration->ticket()->description() !== ''
511
-                        ? ' - ' . $registration->ticket()->description()
510
+                        ? ' - '.$registration->ticket()->description()
512 511
                         : '';
513
-                    $copy_attendee_info_inputs['spco_copy_attendee_chk[ticket-' . $registration->ticket()->ID() . ']'] =
512
+                    $copy_attendee_info_inputs['spco_copy_attendee_chk[ticket-'.$registration->ticket()->ID().']'] =
514 513
                         new EE_Form_Section_HTML(
515
-                            '<h6 class="spco-copy-attendee-event-hdr">' . $item_name . '</h6>'
514
+                            '<h6 class="spco-copy-attendee-event-hdr">'.$item_name.'</h6>'
516 515
                         );
517 516
                     $prev_ticket = $registration->ticket()->ID();
518 517
                 }
519 518
 
520
-                $copy_attendee_info_inputs['spco_copy_attendee_chk[' . $registration->ID() . ']'] =
519
+                $copy_attendee_info_inputs['spco_copy_attendee_chk['.$registration->ID().']'] =
521 520
                     new EE_Checkbox_Multi_Input(
522 521
                         array(
523 522
                             $registration->ID() => sprintf(
@@ -526,7 +525,7 @@  discard block
 block discarded – undo
526 525
                             ),
527 526
                         ),
528 527
                         array(
529
-                            'html_id'                 => 'spco-copy-attendee-chk-' . $registration->reg_url_link(),
528
+                            'html_id'                 => 'spco-copy-attendee-chk-'.$registration->reg_url_link(),
530 529
                             'html_class'              => 'spco-copy-attendee-chk ee-do-not-validate',
531 530
                             'display_html_label_text' => false,
532 531
                         )
@@ -575,7 +574,7 @@  discard block
 block discarded – undo
575 574
             $registration,
576 575
             $question->system_ID()
577 576
         );
578
-        $answer       = $answer_value === null
577
+        $answer = $answer_value === null
579 578
             ? EEM_Answer::instance()->get_one(
580 579
                 array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()))
581 580
             )
@@ -592,14 +591,14 @@  discard block
 block discarded – undo
592 591
         }
593 592
         // verify instance
594 593
         if ($answer instanceof EE_Answer) {
595
-            if (! empty($answer_value)) {
594
+            if ( ! empty($answer_value)) {
596 595
                 $answer->set('ANS_value', $answer_value);
597 596
             }
598 597
             $answer->cache('Question', $question);
599 598
             //remember system ID had a bug where sometimes it could be null
600 599
             $answer_cache_id = $question->is_system_question()
601
-                ? $question->system_ID() . '-' . $registration->reg_url_link()
602
-                : $question->ID() . '-' . $registration->reg_url_link();
600
+                ? $question->system_ID().'-'.$registration->reg_url_link()
601
+                : $question->ID().'-'.$registration->reg_url_link();
603 602
             $registration->cache('Answer', $answer, $answer_cache_id);
604 603
         }
605 604
         return $this->_generate_question_input($registration, $question, $answer);
@@ -631,18 +630,18 @@  discard block
 block discarded – undo
631 630
             10,
632 631
             4
633 632
         );
634
-        $input_constructor_args                  = array(
635
-            'html_name'        => 'ee_reg_qstn[' . $registration->ID() . '][' . $identifier . ']',
636
-            'html_id'          => 'ee_reg_qstn-' . $registration->ID() . '-' . $identifier,
637
-            'html_class'       => 'ee-reg-qstn ee-reg-qstn-' . $identifier,
638
-            'html_label_id'    => 'ee_reg_qstn-' . $registration->ID() . '-' . $identifier,
633
+        $input_constructor_args = array(
634
+            'html_name'        => 'ee_reg_qstn['.$registration->ID().']['.$identifier.']',
635
+            'html_id'          => 'ee_reg_qstn-'.$registration->ID().'-'.$identifier,
636
+            'html_class'       => 'ee-reg-qstn ee-reg-qstn-'.$identifier,
637
+            'html_label_id'    => 'ee_reg_qstn-'.$registration->ID().'-'.$identifier,
639 638
             'html_label_class' => 'ee-reg-qstn',
640 639
         );
641 640
         $input_constructor_args['html_label_id'] .= '-lbl';
642 641
         if ($answer instanceof EE_Answer && $answer->ID()) {
643
-            $input_constructor_args['html_name']     .= '[' . $answer->ID() . ']';
644
-            $input_constructor_args['html_id']       .= '-' . $answer->ID();
645
-            $input_constructor_args['html_label_id'] .= '-' . $answer->ID();
642
+            $input_constructor_args['html_name']     .= '['.$answer->ID().']';
643
+            $input_constructor_args['html_id']       .= '-'.$answer->ID();
644
+            $input_constructor_args['html_label_id'] .= '-'.$answer->ID();
646 645
         }
647 646
         $form_input = $question->generate_form_input(
648 647
             $registration,
@@ -685,7 +684,7 @@  discard block
 block discarded – undo
685 684
         $countries = $this->checkout->action === 'process_reg_step'
686 685
             ? EEM_Country::instance()->get_all_countries()
687 686
             : EEM_Country::instance()->get_all_active_countries();
688
-        if (! empty($countries)) {
687
+        if ( ! empty($countries)) {
689 688
             foreach ($countries as $country) {
690 689
                 if ($country instanceof EE_Country) {
691 690
                     $country_options[$country->ID()] = $country->name();
@@ -735,7 +734,7 @@  discard block
 block discarded – undo
735 734
         $states        = $this->checkout->action === 'process_reg_step'
736 735
             ? EEM_State::instance()->get_all_states()
737 736
             : EEM_State::instance()->get_all_active_states();
738
-        if (! empty($states)) {
737
+        if ( ! empty($states)) {
739 738
             foreach ($states as $state) {
740 739
                 if ($state instanceof EE_State) {
741 740
                     $state_options[$state->country()->name()][$state->ID()] = $state->name();
@@ -787,7 +786,7 @@  discard block
 block discarded – undo
787 786
             );
788 787
             return false;
789 788
         }
790
-        if (! $this->checkout->transaction instanceof EE_Transaction || ! $this->checkout->continue_reg) {
789
+        if ( ! $this->checkout->transaction instanceof EE_Transaction || ! $this->checkout->continue_reg) {
791 790
             EE_Error::add_error(
792 791
                 esc_html__(
793 792
                     'A valid transaction could not be initiated for processing your registrations.',
@@ -804,10 +803,10 @@  discard block
 block discarded – undo
804 803
         // verify we got the goods
805 804
         if (empty($registrations)) {
806 805
             //combine the old translated string with a new one, in order to not break translations
807
-            $error_message = esc_html__( 'Your form data could not be applied to any valid registrations.', 'event_espresso' )
806
+            $error_message = esc_html__('Your form data could not be applied to any valid registrations.', 'event_espresso')
808 807
                              . sprintf(
809 808
                                  esc_html__('%3$sThis can sometimes happen if too much time has been taken to complete the registration process.%3$sPlease return to the %1$sEvent List%2$s and reselect your tickets. If the problem continues, please contact the site administrator.', 'event_espresso'),
810
-                                 '<a href="' . get_post_type_archive_link('espresso_events') . '" >',
809
+                                 '<a href="'.get_post_type_archive_link('espresso_events').'" >',
811 810
                                  '</a>',
812 811
                                  '<br />'
813 812
                              );
@@ -826,7 +825,7 @@  discard block
 block discarded – undo
826 825
         if ($registrations_processed === false) {
827 826
             // but return immediately if the previous step exited early due to errors
828 827
             return false;
829
-        } elseif (! $this->checkout->revisit && $registrations_processed !== $this->checkout->total_ticket_count) {
828
+        } elseif ( ! $this->checkout->revisit && $registrations_processed !== $this->checkout->total_ticket_count) {
830 829
             // generate a correctly translated string for all possible singular/plural combinations
831 830
             if ($this->checkout->total_ticket_count === 1 && $registrations_processed !== 1) {
832 831
                 $error_msg = sprintf(
@@ -907,7 +906,7 @@  discard block
 block discarded – undo
907 906
         // grab the saved registrations from the transaction
908 907
         foreach ($registrations as $registration) {
909 908
             // verify EE_Registration object
910
-            if (! $registration instanceof EE_Registration) {
909
+            if ( ! $registration instanceof EE_Registration) {
911 910
                 EE_Error::add_error(
912 911
                     esc_html__(
913 912
                         'An invalid Registration object was discovered when attempting to process your registration information.',
@@ -922,12 +921,12 @@  discard block
 block discarded – undo
922 921
             /** @var string $reg_url_link */
923 922
             $reg_url_link = $registration->reg_url_link();
924 923
             // reg_url_link exists ?
925
-            if (! empty($reg_url_link)) {
924
+            if ( ! empty($reg_url_link)) {
926 925
                 // should this registration be processed during this visit ?
927 926
                 if ($this->checkout->visit_allows_processing_of_this_registration($registration)) {
928 927
                     // if NOT revisiting, then let's save the registration now,
929 928
                     // so that we have a REG_ID to use when generating other objects
930
-                    if (! $this->checkout->revisit) {
929
+                    if ( ! $this->checkout->revisit) {
931 930
                         $registration->save();
932 931
                     }
933 932
                     /**
@@ -962,7 +961,7 @@  discard block
 block discarded – undo
962 961
                             ? true
963 962
                             : false;
964 963
                         // filter form input data for this registration
965
-                        $valid_data[$reg_url_link] = (array)apply_filters(
964
+                        $valid_data[$reg_url_link] = (array) apply_filters(
966 965
                             'FHEE__EE_Single_Page_Checkout__process_attendee_information__valid_data_line_item',
967 966
                             $valid_data[$reg_url_link]
968 967
                         );
@@ -974,11 +973,11 @@  discard block
 block discarded – undo
974 973
                         }
975 974
                         // now loop through our array of valid post data && process attendee reg forms
976 975
                         foreach ($valid_data[$reg_url_link] as $form_section => $form_inputs) {
977
-                            if (! in_array($form_section, $non_input_form_sections)) {
976
+                            if ( ! in_array($form_section, $non_input_form_sections)) {
978 977
                                 foreach ($form_inputs as $form_input => $input_value) {
979 978
                                     // \EEH_Debug_Tools::printr( $input_value, $form_input, __FILE__, __LINE__ );
980 979
                                     // check for critical inputs
981
-                                    if (! $this->_verify_critical_attendee_details_are_set_and_validate_email(
980
+                                    if ( ! $this->_verify_critical_attendee_details_are_set_and_validate_email(
982 981
                                         $form_input,
983 982
                                         $input_value
984 983
                                     )
@@ -998,7 +997,7 @@  discard block
 block discarded – undo
998 997
                                         $input_value = $primary_registrant[$form_input];
999 998
                                     }
1000 999
                                     // now attempt to save the input data
1001
-                                    if (! $this->_save_registration_form_input(
1000
+                                    if ( ! $this->_save_registration_form_input(
1002 1001
                                         $registration,
1003 1002
                                         $form_input,
1004 1003
                                         $input_value
@@ -1054,7 +1053,7 @@  discard block
 block discarded – undo
1054 1053
                     // add relation to registration, set attendee ID, and cache attendee
1055 1054
                     $this->_associate_attendee_with_registration($registration, $attendee);
1056 1055
                     // \EEH_Debug_Tools::printr( $registration, '$registration', __FILE__, __LINE__ );
1057
-                    if (! $registration->attendee() instanceof EE_Attendee) {
1056
+                    if ( ! $registration->attendee() instanceof EE_Attendee) {
1058 1057
                         EE_Error::add_error(
1059 1058
                             sprintf(
1060 1059
                                 esc_html__(
@@ -1149,7 +1148,7 @@  discard block
 block discarded – undo
1149 1148
          * @see https://events.codebasehq.com/projects/event-espresso/tickets/10477
1150 1149
          */
1151 1150
         $answer_cache_id = $this->checkout->reg_url_link
1152
-            ? $form_input . '-' . $registration->reg_url_link()
1151
+            ? $form_input.'-'.$registration->reg_url_link()
1153 1152
             : $form_input;
1154 1153
         $answer_is_obj   = isset($this->_registration_answers[$answer_cache_id])
1155 1154
                            && $this->_registration_answers[$answer_cache_id] instanceof EE_Answer
@@ -1170,10 +1169,10 @@  discard block
 block discarded – undo
1170 1169
                 break;
1171 1170
 
1172 1171
             default:
1173
-                $ATT_input = 'ATT_' . $form_input;
1172
+                $ATT_input = 'ATT_'.$form_input;
1174 1173
                 //EEH_Debug_Tools::printr( $ATT_input, '$ATT_input', __FILE__, __LINE__ );
1175 1174
                 $attendee_property = EEM_Attendee::instance()->has_field($ATT_input) ? true : false;
1176
-                $form_input        = $attendee_property ? 'ATT_' . $form_input : $form_input;
1175
+                $form_input        = $attendee_property ? 'ATT_'.$form_input : $form_input;
1177 1176
         }
1178 1177
         // EEH_Debug_Tools::printr( $answer_cache_id, '$answer_cache_id', __FILE__, __LINE__ );
1179 1178
         // EEH_Debug_Tools::printr( $attendee_property, '$attendee_property', __FILE__, __LINE__ );
@@ -1218,7 +1217,7 @@  discard block
 block discarded – undo
1218 1217
     ) {
1219 1218
         if (empty($input_value)) {
1220 1219
             // if the form input isn't marked as being required, then just return
1221
-            if (! isset($this->_required_questions[$form_input]) || ! $this->_required_questions[$form_input]) {
1220
+            if ( ! isset($this->_required_questions[$form_input]) || ! $this->_required_questions[$form_input]) {
1222 1221
                 return true;
1223 1222
             }
1224 1223
             switch ($form_input) {
@@ -1305,7 +1304,7 @@  discard block
 block discarded – undo
1305 1304
             'FHEE__EE_SPCO_Reg_Step_Attendee_Information__merge_address_details_with_critical_attendee_details',
1306 1305
             false
1307 1306
         )) {
1308
-            $address_details           = array(
1307
+            $address_details = array(
1309 1308
                 'ATT_address',
1310 1309
                 'ATT_address2',
1311 1310
                 'ATT_city',
@@ -1317,7 +1316,7 @@  discard block
 block discarded – undo
1317 1316
             $critical_attendee_details = array_merge($critical_attendee_details, $address_details);
1318 1317
         }
1319 1318
         foreach ($critical_attendee_details as $critical_attendee_detail) {
1320
-            if (! isset($attendee_data[$critical_attendee_detail])
1319
+            if ( ! isset($attendee_data[$critical_attendee_detail])
1321 1320
                 || empty($attendee_data[$critical_attendee_detail])
1322 1321
             ) {
1323 1322
                 $attendee_data[$critical_attendee_detail] = $this->checkout->primary_attendee_obj->get(
Please login to merge, or discard this patch.
caffeinated/modules/recaptcha/EED_Recaptcha.module.php 2 patches
Indentation   +376 added lines, -376 removed lines patch added patch discarded remove patch
@@ -22,382 +22,382 @@
 block discarded – undo
22 22
 class EED_Recaptcha extends EED_Module
23 23
 {
24 24
 
25
-    /**
26
-     * @var EE_Registration_Config $config
27
-     */
28
-    private static $config;
29
-
30
-    /**
31
-     * @type bool $_not_a_robot
32
-     */
33
-    private static $_not_a_robot;
34
-
35
-    /**
36
-     * @type string $_recaptcha_response
37
-     */
38
-    private static $_recaptcha_response;
39
-
40
-
41
-    /**
42
-     * @return EED_Module|EED_Recaptcha
43
-     */
44
-    public static function instance()
45
-    {
46
-        return parent::get_instance(__CLASS__);
47
-    }
48
-
49
-
50
-    /**
51
-     * set_hooks - for hooking into EE Core, other modules, etc
52
-     *
53
-     * @return void
54
-     * @throws InvalidArgumentException
55
-     * @throws InvalidInterfaceException
56
-     * @throws InvalidDataTypeException
57
-     */
58
-    public static function set_hooks()
59
-    {
60
-        EED_Recaptcha::$config = EE_Registry::instance()->CFG->registration;
61
-        // use_captcha ?
62
-        if (
63
-            EED_Recaptcha::useRecaptcha()
64
-            && EED_Recaptcha::notPaymentOptionsRevisit()
65
-        ) {
66
-            EED_Recaptcha::set_definitions();
67
-            EED_Recaptcha::enqueue_styles_and_scripts();
68
-            add_action('wp', array('EED_Recaptcha', 'set_late_hooks'), 1, 0);
69
-            add_action(
70
-                'AHEE__before_spco_whats_next_buttons',
71
-                array('EED_Recaptcha', 'display_recaptcha'), 10, 0
72
-            );
73
-            add_filter(
74
-                'FHEE__EED_Single_Page_Checkout__init___continue_reg',
75
-                array('EED_Recaptcha', 'not_a_robot')
76
-            );
77
-            add_filter(
78
-                'FHEE__EE_SPCO_Reg_Step__set_completed___completed',
79
-                array('EED_Recaptcha', 'not_a_robot')
80
-            );
81
-            add_filter(
82
-                'FHEE__EE_SPCO_JSON_Response___toString__JSON_response',
83
-                array('EED_Recaptcha', 'recaptcha_response')
84
-            );
85
-            add_filter(
86
-                'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
87
-                array('EED_Recaptcha', 'bypass_recaptcha_for_spco_load_payment_method')
88
-            );
89
-        }
90
-    }
91
-
92
-
93
-    /**
94
-     * set_hooks_admin - for hooking into EE Admin Core, other modules, etc
95
-     *
96
-     * @return void
97
-     * @throws InvalidArgumentException
98
-     * @throws InvalidInterfaceException
99
-     * @throws InvalidDataTypeException
100
-     */
101
-    public static function set_hooks_admin()
102
-    {
103
-        EED_Recaptcha::$config = EE_Registry::instance()->CFG->registration;
104
-        EED_Recaptcha::set_definitions();
105
-        // use_captcha ?
106
-        if (
107
-            EED_Recaptcha::useRecaptcha()
108
-            && EED_Recaptcha::notPaymentOptionsRevisit()
109
-            && EE_Registry::instance()->REQ->get('step', '') !== ''
110
-        ) {
111
-            EED_Recaptcha::enqueue_styles_and_scripts();
112
-            add_filter(
113
-                'FHEE__EED_Single_Page_Checkout__init___continue_reg',
114
-                array('EED_Recaptcha', 'not_a_robot')
115
-            );
116
-            add_filter(
117
-                'FHEE__EE_SPCO_Reg_Step__set_completed___completed',
118
-                array('EED_Recaptcha', 'not_a_robot')
119
-            );
120
-            add_filter(
121
-                'FHEE__EE_SPCO_JSON_Response___toString__JSON_response',
122
-                array('EED_Recaptcha', 'recaptcha_response')
123
-            );
124
-        }
125
-    }
126
-
127
-
128
-    /**
129
-     * @return void
130
-     */
131
-    public static function set_definitions()
132
-    {
133
-        if (is_user_logged_in()) {
134
-            EED_Recaptcha::$_not_a_robot = true;
135
-        }
136
-        define(
137
-            'RECAPTCHA_BASE_PATH',
138
-            rtrim(str_replace(array('\\', '/'), DS, plugin_dir_path(__FILE__)), DS) . DS
139
-        );
140
-        define('RECAPTCHA_BASE_URL', plugin_dir_url(__FILE__));
141
-    }
142
-
143
-
144
-    /**
145
-     * @return void
146
-     */
147
-    public static function set_late_hooks()
148
-    {
149
-        add_filter(
150
-            'FHEE__Single_Page_Checkout__translate_js_strings__ajax_submit',
151
-            array('EED_Recaptcha', 'not_a_robot')
152
-        );
153
-    }
154
-
155
-
156
-    /**
157
-     * @return boolean
158
-     */
159
-    public static function useRecaptcha()
160
-    {
161
-        return EED_Recaptcha::$config->use_captcha
162
-               && EED_Recaptcha::$config->recaptcha_theme !== 'invisible';
163
-    }
164
-
165
-
166
-    /**
167
-     * @return boolean
168
-     * @throws InvalidArgumentException
169
-     * @throws InvalidInterfaceException
170
-     * @throws InvalidDataTypeException
171
-     */
172
-    public static function notPaymentOptionsRevisit()
173
-    {
174
-        return ! (
175
-            EE_Registry::instance()->REQ->get('step', '') === 'payment_options'
176
-            && (boolean) EE_Registry::instance()->REQ->get('revisit', false) === true
177
-        );
178
-    }
179
-
180
-
181
-    /**
182
-     * @return void
183
-     * @throws InvalidArgumentException
184
-     * @throws InvalidInterfaceException
185
-     * @throws InvalidDataTypeException
186
-     */
187
-    public static function enqueue_styles_and_scripts()
188
-    {
189
-        wp_register_script(
190
-            'espresso_recaptcha',
191
-            RECAPTCHA_BASE_URL . 'scripts' . DS . 'espresso_recaptcha.js',
192
-            array('single_page_checkout'),
193
-            EVENT_ESPRESSO_VERSION,
194
-            true
195
-        );
196
-        wp_register_script(
197
-            'google_recaptcha',
198
-            'https://www.google.com/recaptcha/api.js?hl=' . EED_Recaptcha::$config->recaptcha_language,
199
-            array('espresso_recaptcha'),
200
-            EVENT_ESPRESSO_VERSION,
201
-            true
202
-        );
203
-        EE_Registry::$i18n_js_strings['no_SPCO_error']      = __(
204
-            'It appears the Single Page Checkout javascript was not loaded properly! Please refresh the page and try again or contact support.',
205
-            'event_espresso'
206
-        );
207
-        EE_Registry::$i18n_js_strings['no_recaptcha_error'] = __(
208
-            'There appears to be a problem with the reCAPTCHA configuration! Please check the admin settings or contact support.',
209
-            'event_espresso'
210
-        );
211
-        EE_Registry::$i18n_js_strings['recaptcha_fail']     = __(
212
-            'Please complete the anti-spam test before proceeding.',
213
-            'event_espresso'
214
-        );
215
-    }
216
-
217
-
218
-    /**
219
-     * @param \WP $WP
220
-     */
221
-    public function run($WP)
222
-    {
223
-    }
224
-
225
-
226
-    /**
227
-     * @return boolean
228
-     * @throws InvalidArgumentException
229
-     * @throws InvalidInterfaceException
230
-     * @throws InvalidDataTypeException
231
-     */
232
-    public static function not_a_robot()
233
-    {
234
-        $not_a_robot = is_bool(EED_Recaptcha::$_not_a_robot)
235
-            ? EED_Recaptcha::$_not_a_robot
236
-            : EED_Recaptcha::recaptcha_passed();
237
-        return $not_a_robot;
238
-    }
239
-
240
-
241
-    /**
242
-     * @return void
243
-     * @throws DomainException
244
-     * @throws InvalidArgumentException
245
-     * @throws InvalidInterfaceException
246
-     * @throws InvalidDataTypeException
247
-     */
248
-    public static function display_recaptcha()
249
-    {
250
-        // logged in means you have already passed a turing test of sorts
251
-        if (is_user_logged_in()) {
252
-            return;
253
-        }
254
-        // don't display if not using recaptcha or user is logged in
255
-        if (EED_Recaptcha::useRecaptcha() && ! EED_Recaptcha::$_not_a_robot) {
256
-            // only display if they have NOT passed the test yet
257
-            EEH_Template::display_template(
258
-                RECAPTCHA_BASE_PATH . DS . 'templates' . DS . 'recaptcha.template.php',
259
-                array(
260
-                    'recaptcha_publickey' => EED_Recaptcha::$config->recaptcha_publickey,
261
-                    'recaptcha_theme'     => EED_Recaptcha::$config->recaptcha_theme,
262
-                    'recaptcha_type'      => EED_Recaptcha::$config->recaptcha_type,
263
-                )
264
-            );
265
-            wp_enqueue_script('google_recaptcha');
266
-        }
267
-    }
268
-
269
-
270
-    /**
271
-     * @return array
272
-     * @throws InvalidArgumentException
273
-     * @throws InvalidInterfaceException
274
-     * @throws InvalidDataTypeException
275
-     */
276
-    public static function bypass_recaptcha_for_spco_load_payment_method()
277
-    {
278
-        return array(
279
-            'EESID'  => EE_Registry::instance()->SSN->id(),
280
-            'step'   => 'payment_options',
281
-            'action' => 'switch_spco_billing_form',
282
-        );
283
-    }
284
-
285
-
286
-    /**
287
-     * @return boolean
288
-     * @throws InvalidArgumentException
289
-     * @throws InvalidInterfaceException
290
-     * @throws InvalidDataTypeException
291
-     */
292
-    public static function recaptcha_passed()
293
-    {
294
-        // logged in means you have already passed a turing test of sorts
295
-        if (is_user_logged_in() || EED_Recaptcha::_bypass_recaptcha()) {
296
-            return true;
297
-        }
298
-        // was test already passed?
299
-        $recaptcha_passed = EE_Registry::instance()->SSN->get_session_data('recaptcha_passed');
300
-        $recaptcha_passed = filter_var($recaptcha_passed, FILTER_VALIDATE_BOOLEAN);
301
-        // verify recaptcha
302
-        EED_Recaptcha::_get_recaptcha_response();
303
-        if (! $recaptcha_passed && EED_Recaptcha::$_recaptcha_response) {
304
-            $recaptcha_passed = EED_Recaptcha::_process_recaptcha_response();
305
-            EE_Registry::instance()->SSN->set_session_data(array('recaptcha_passed' => $recaptcha_passed));
306
-        }
307
-        EED_Recaptcha::$_not_a_robot = $recaptcha_passed;
308
-        return $recaptcha_passed;
309
-    }
310
-
311
-
312
-    /**
313
-     * @param array $recaptcha_response
314
-     * @return array
315
-     */
316
-    public static function recaptcha_response($recaptcha_response = array())
317
-    {
318
-        if (EED_Recaptcha::_bypass_recaptcha()) {
319
-            $recaptcha_response['bypass_recaptcha'] = true;
320
-            $recaptcha_response['recaptcha_passed'] = true;
321
-        } else {
322
-            $recaptcha_response['recaptcha_passed'] = EED_Recaptcha::$_not_a_robot;
323
-        }
324
-        return $recaptcha_response;
325
-    }
326
-
327
-
328
-    /**
329
-     * @return boolean
330
-     */
331
-    private static function _bypass_recaptcha()
332
-    {
333
-        // an array of key value pairs that must match exactly with the incoming request,
334
-        // in order to bypass recaptcha for the current request ONLY
335
-        $bypass_request_params_array = (array) apply_filters(
336
-            'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
337
-            array()
338
-        );
339
-        // does $bypass_request_params_array have any values ?
340
-        if (empty($bypass_request_params_array)) {
341
-            return false;
342
-        }
343
-        // initially set bypass to TRUE
344
-        $bypass_recaptcha = true;
345
-        foreach ($bypass_request_params_array as $key => $value) {
346
-            // if $key is not found or value doesn't match exactly, then toggle bypass to FALSE,
347
-            // otherwise carry over it's value. This way, one missed setting results in no bypass
348
-            $bypass_recaptcha = isset($_REQUEST[ $key ]) && $_REQUEST[ $key ] === $value
349
-                ? $bypass_recaptcha
350
-                : false;
351
-        }
352
-        return $bypass_recaptcha;
353
-    }
354
-
355
-
356
-    /**
357
-     * @return void
358
-     * @throws InvalidArgumentException
359
-     * @throws InvalidInterfaceException
360
-     * @throws InvalidDataTypeException
361
-     */
362
-    private static function _get_recaptcha_response()
363
-    {
364
-        EED_Recaptcha::$_recaptcha_response = EE_Registry::instance()->REQ->get(
365
-            'g-recaptcha-response',
366
-            false
367
-        );
368
-    }
369
-
370
-
371
-    /**
372
-     * @return boolean
373
-     * @throws InvalidArgumentException
374
-     * @throws InvalidInterfaceException
375
-     * @throws InvalidDataTypeException
376
-     */
377
-    private static function _process_recaptcha_response()
378
-    {
379
-        // verify library is loaded
380
-        if (! class_exists('\ReCaptcha\ReCaptcha')) {
381
-            require_once RECAPTCHA_BASE_PATH . DS . 'autoload.php';
382
-        }
383
-        // The response from reCAPTCHA
384
-        EED_Recaptcha::_get_recaptcha_response();
385
-        $recaptcha_response = EED_Recaptcha::$_recaptcha_response;
386
-        // Was there a reCAPTCHA response?
387
-        if ($recaptcha_response) {
388
-            // if allow_url_fopen is Off, then set a different request method
389
-            $request_method     = ! ini_get('allow_url_fopen') ? new SocketPost() : null;
390
-            $recaptcha          = new ReCaptcha(
391
-                EED_Recaptcha::$config->recaptcha_privatekey,
392
-                $request_method
393
-            );
394
-            $recaptcha_response = $recaptcha->verify(
395
-                EED_Recaptcha::$_recaptcha_response,
396
-                $_SERVER['REMOTE_ADDR']
397
-            );
398
-        }
399
-        return $recaptcha_response instanceof Response && $recaptcha_response->isSuccess();
400
-    }
25
+	/**
26
+	 * @var EE_Registration_Config $config
27
+	 */
28
+	private static $config;
29
+
30
+	/**
31
+	 * @type bool $_not_a_robot
32
+	 */
33
+	private static $_not_a_robot;
34
+
35
+	/**
36
+	 * @type string $_recaptcha_response
37
+	 */
38
+	private static $_recaptcha_response;
39
+
40
+
41
+	/**
42
+	 * @return EED_Module|EED_Recaptcha
43
+	 */
44
+	public static function instance()
45
+	{
46
+		return parent::get_instance(__CLASS__);
47
+	}
48
+
49
+
50
+	/**
51
+	 * set_hooks - for hooking into EE Core, other modules, etc
52
+	 *
53
+	 * @return void
54
+	 * @throws InvalidArgumentException
55
+	 * @throws InvalidInterfaceException
56
+	 * @throws InvalidDataTypeException
57
+	 */
58
+	public static function set_hooks()
59
+	{
60
+		EED_Recaptcha::$config = EE_Registry::instance()->CFG->registration;
61
+		// use_captcha ?
62
+		if (
63
+			EED_Recaptcha::useRecaptcha()
64
+			&& EED_Recaptcha::notPaymentOptionsRevisit()
65
+		) {
66
+			EED_Recaptcha::set_definitions();
67
+			EED_Recaptcha::enqueue_styles_and_scripts();
68
+			add_action('wp', array('EED_Recaptcha', 'set_late_hooks'), 1, 0);
69
+			add_action(
70
+				'AHEE__before_spco_whats_next_buttons',
71
+				array('EED_Recaptcha', 'display_recaptcha'), 10, 0
72
+			);
73
+			add_filter(
74
+				'FHEE__EED_Single_Page_Checkout__init___continue_reg',
75
+				array('EED_Recaptcha', 'not_a_robot')
76
+			);
77
+			add_filter(
78
+				'FHEE__EE_SPCO_Reg_Step__set_completed___completed',
79
+				array('EED_Recaptcha', 'not_a_robot')
80
+			);
81
+			add_filter(
82
+				'FHEE__EE_SPCO_JSON_Response___toString__JSON_response',
83
+				array('EED_Recaptcha', 'recaptcha_response')
84
+			);
85
+			add_filter(
86
+				'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
87
+				array('EED_Recaptcha', 'bypass_recaptcha_for_spco_load_payment_method')
88
+			);
89
+		}
90
+	}
91
+
92
+
93
+	/**
94
+	 * set_hooks_admin - for hooking into EE Admin Core, other modules, etc
95
+	 *
96
+	 * @return void
97
+	 * @throws InvalidArgumentException
98
+	 * @throws InvalidInterfaceException
99
+	 * @throws InvalidDataTypeException
100
+	 */
101
+	public static function set_hooks_admin()
102
+	{
103
+		EED_Recaptcha::$config = EE_Registry::instance()->CFG->registration;
104
+		EED_Recaptcha::set_definitions();
105
+		// use_captcha ?
106
+		if (
107
+			EED_Recaptcha::useRecaptcha()
108
+			&& EED_Recaptcha::notPaymentOptionsRevisit()
109
+			&& EE_Registry::instance()->REQ->get('step', '') !== ''
110
+		) {
111
+			EED_Recaptcha::enqueue_styles_and_scripts();
112
+			add_filter(
113
+				'FHEE__EED_Single_Page_Checkout__init___continue_reg',
114
+				array('EED_Recaptcha', 'not_a_robot')
115
+			);
116
+			add_filter(
117
+				'FHEE__EE_SPCO_Reg_Step__set_completed___completed',
118
+				array('EED_Recaptcha', 'not_a_robot')
119
+			);
120
+			add_filter(
121
+				'FHEE__EE_SPCO_JSON_Response___toString__JSON_response',
122
+				array('EED_Recaptcha', 'recaptcha_response')
123
+			);
124
+		}
125
+	}
126
+
127
+
128
+	/**
129
+	 * @return void
130
+	 */
131
+	public static function set_definitions()
132
+	{
133
+		if (is_user_logged_in()) {
134
+			EED_Recaptcha::$_not_a_robot = true;
135
+		}
136
+		define(
137
+			'RECAPTCHA_BASE_PATH',
138
+			rtrim(str_replace(array('\\', '/'), DS, plugin_dir_path(__FILE__)), DS) . DS
139
+		);
140
+		define('RECAPTCHA_BASE_URL', plugin_dir_url(__FILE__));
141
+	}
142
+
143
+
144
+	/**
145
+	 * @return void
146
+	 */
147
+	public static function set_late_hooks()
148
+	{
149
+		add_filter(
150
+			'FHEE__Single_Page_Checkout__translate_js_strings__ajax_submit',
151
+			array('EED_Recaptcha', 'not_a_robot')
152
+		);
153
+	}
154
+
155
+
156
+	/**
157
+	 * @return boolean
158
+	 */
159
+	public static function useRecaptcha()
160
+	{
161
+		return EED_Recaptcha::$config->use_captcha
162
+			   && EED_Recaptcha::$config->recaptcha_theme !== 'invisible';
163
+	}
164
+
165
+
166
+	/**
167
+	 * @return boolean
168
+	 * @throws InvalidArgumentException
169
+	 * @throws InvalidInterfaceException
170
+	 * @throws InvalidDataTypeException
171
+	 */
172
+	public static function notPaymentOptionsRevisit()
173
+	{
174
+		return ! (
175
+			EE_Registry::instance()->REQ->get('step', '') === 'payment_options'
176
+			&& (boolean) EE_Registry::instance()->REQ->get('revisit', false) === true
177
+		);
178
+	}
179
+
180
+
181
+	/**
182
+	 * @return void
183
+	 * @throws InvalidArgumentException
184
+	 * @throws InvalidInterfaceException
185
+	 * @throws InvalidDataTypeException
186
+	 */
187
+	public static function enqueue_styles_and_scripts()
188
+	{
189
+		wp_register_script(
190
+			'espresso_recaptcha',
191
+			RECAPTCHA_BASE_URL . 'scripts' . DS . 'espresso_recaptcha.js',
192
+			array('single_page_checkout'),
193
+			EVENT_ESPRESSO_VERSION,
194
+			true
195
+		);
196
+		wp_register_script(
197
+			'google_recaptcha',
198
+			'https://www.google.com/recaptcha/api.js?hl=' . EED_Recaptcha::$config->recaptcha_language,
199
+			array('espresso_recaptcha'),
200
+			EVENT_ESPRESSO_VERSION,
201
+			true
202
+		);
203
+		EE_Registry::$i18n_js_strings['no_SPCO_error']      = __(
204
+			'It appears the Single Page Checkout javascript was not loaded properly! Please refresh the page and try again or contact support.',
205
+			'event_espresso'
206
+		);
207
+		EE_Registry::$i18n_js_strings['no_recaptcha_error'] = __(
208
+			'There appears to be a problem with the reCAPTCHA configuration! Please check the admin settings or contact support.',
209
+			'event_espresso'
210
+		);
211
+		EE_Registry::$i18n_js_strings['recaptcha_fail']     = __(
212
+			'Please complete the anti-spam test before proceeding.',
213
+			'event_espresso'
214
+		);
215
+	}
216
+
217
+
218
+	/**
219
+	 * @param \WP $WP
220
+	 */
221
+	public function run($WP)
222
+	{
223
+	}
224
+
225
+
226
+	/**
227
+	 * @return boolean
228
+	 * @throws InvalidArgumentException
229
+	 * @throws InvalidInterfaceException
230
+	 * @throws InvalidDataTypeException
231
+	 */
232
+	public static function not_a_robot()
233
+	{
234
+		$not_a_robot = is_bool(EED_Recaptcha::$_not_a_robot)
235
+			? EED_Recaptcha::$_not_a_robot
236
+			: EED_Recaptcha::recaptcha_passed();
237
+		return $not_a_robot;
238
+	}
239
+
240
+
241
+	/**
242
+	 * @return void
243
+	 * @throws DomainException
244
+	 * @throws InvalidArgumentException
245
+	 * @throws InvalidInterfaceException
246
+	 * @throws InvalidDataTypeException
247
+	 */
248
+	public static function display_recaptcha()
249
+	{
250
+		// logged in means you have already passed a turing test of sorts
251
+		if (is_user_logged_in()) {
252
+			return;
253
+		}
254
+		// don't display if not using recaptcha or user is logged in
255
+		if (EED_Recaptcha::useRecaptcha() && ! EED_Recaptcha::$_not_a_robot) {
256
+			// only display if they have NOT passed the test yet
257
+			EEH_Template::display_template(
258
+				RECAPTCHA_BASE_PATH . DS . 'templates' . DS . 'recaptcha.template.php',
259
+				array(
260
+					'recaptcha_publickey' => EED_Recaptcha::$config->recaptcha_publickey,
261
+					'recaptcha_theme'     => EED_Recaptcha::$config->recaptcha_theme,
262
+					'recaptcha_type'      => EED_Recaptcha::$config->recaptcha_type,
263
+				)
264
+			);
265
+			wp_enqueue_script('google_recaptcha');
266
+		}
267
+	}
268
+
269
+
270
+	/**
271
+	 * @return array
272
+	 * @throws InvalidArgumentException
273
+	 * @throws InvalidInterfaceException
274
+	 * @throws InvalidDataTypeException
275
+	 */
276
+	public static function bypass_recaptcha_for_spco_load_payment_method()
277
+	{
278
+		return array(
279
+			'EESID'  => EE_Registry::instance()->SSN->id(),
280
+			'step'   => 'payment_options',
281
+			'action' => 'switch_spco_billing_form',
282
+		);
283
+	}
284
+
285
+
286
+	/**
287
+	 * @return boolean
288
+	 * @throws InvalidArgumentException
289
+	 * @throws InvalidInterfaceException
290
+	 * @throws InvalidDataTypeException
291
+	 */
292
+	public static function recaptcha_passed()
293
+	{
294
+		// logged in means you have already passed a turing test of sorts
295
+		if (is_user_logged_in() || EED_Recaptcha::_bypass_recaptcha()) {
296
+			return true;
297
+		}
298
+		// was test already passed?
299
+		$recaptcha_passed = EE_Registry::instance()->SSN->get_session_data('recaptcha_passed');
300
+		$recaptcha_passed = filter_var($recaptcha_passed, FILTER_VALIDATE_BOOLEAN);
301
+		// verify recaptcha
302
+		EED_Recaptcha::_get_recaptcha_response();
303
+		if (! $recaptcha_passed && EED_Recaptcha::$_recaptcha_response) {
304
+			$recaptcha_passed = EED_Recaptcha::_process_recaptcha_response();
305
+			EE_Registry::instance()->SSN->set_session_data(array('recaptcha_passed' => $recaptcha_passed));
306
+		}
307
+		EED_Recaptcha::$_not_a_robot = $recaptcha_passed;
308
+		return $recaptcha_passed;
309
+	}
310
+
311
+
312
+	/**
313
+	 * @param array $recaptcha_response
314
+	 * @return array
315
+	 */
316
+	public static function recaptcha_response($recaptcha_response = array())
317
+	{
318
+		if (EED_Recaptcha::_bypass_recaptcha()) {
319
+			$recaptcha_response['bypass_recaptcha'] = true;
320
+			$recaptcha_response['recaptcha_passed'] = true;
321
+		} else {
322
+			$recaptcha_response['recaptcha_passed'] = EED_Recaptcha::$_not_a_robot;
323
+		}
324
+		return $recaptcha_response;
325
+	}
326
+
327
+
328
+	/**
329
+	 * @return boolean
330
+	 */
331
+	private static function _bypass_recaptcha()
332
+	{
333
+		// an array of key value pairs that must match exactly with the incoming request,
334
+		// in order to bypass recaptcha for the current request ONLY
335
+		$bypass_request_params_array = (array) apply_filters(
336
+			'FHEE__EED_Recaptcha___bypass_recaptcha__bypass_request_params_array',
337
+			array()
338
+		);
339
+		// does $bypass_request_params_array have any values ?
340
+		if (empty($bypass_request_params_array)) {
341
+			return false;
342
+		}
343
+		// initially set bypass to TRUE
344
+		$bypass_recaptcha = true;
345
+		foreach ($bypass_request_params_array as $key => $value) {
346
+			// if $key is not found or value doesn't match exactly, then toggle bypass to FALSE,
347
+			// otherwise carry over it's value. This way, one missed setting results in no bypass
348
+			$bypass_recaptcha = isset($_REQUEST[ $key ]) && $_REQUEST[ $key ] === $value
349
+				? $bypass_recaptcha
350
+				: false;
351
+		}
352
+		return $bypass_recaptcha;
353
+	}
354
+
355
+
356
+	/**
357
+	 * @return void
358
+	 * @throws InvalidArgumentException
359
+	 * @throws InvalidInterfaceException
360
+	 * @throws InvalidDataTypeException
361
+	 */
362
+	private static function _get_recaptcha_response()
363
+	{
364
+		EED_Recaptcha::$_recaptcha_response = EE_Registry::instance()->REQ->get(
365
+			'g-recaptcha-response',
366
+			false
367
+		);
368
+	}
369
+
370
+
371
+	/**
372
+	 * @return boolean
373
+	 * @throws InvalidArgumentException
374
+	 * @throws InvalidInterfaceException
375
+	 * @throws InvalidDataTypeException
376
+	 */
377
+	private static function _process_recaptcha_response()
378
+	{
379
+		// verify library is loaded
380
+		if (! class_exists('\ReCaptcha\ReCaptcha')) {
381
+			require_once RECAPTCHA_BASE_PATH . DS . 'autoload.php';
382
+		}
383
+		// The response from reCAPTCHA
384
+		EED_Recaptcha::_get_recaptcha_response();
385
+		$recaptcha_response = EED_Recaptcha::$_recaptcha_response;
386
+		// Was there a reCAPTCHA response?
387
+		if ($recaptcha_response) {
388
+			// if allow_url_fopen is Off, then set a different request method
389
+			$request_method     = ! ini_get('allow_url_fopen') ? new SocketPost() : null;
390
+			$recaptcha          = new ReCaptcha(
391
+				EED_Recaptcha::$config->recaptcha_privatekey,
392
+				$request_method
393
+			);
394
+			$recaptcha_response = $recaptcha->verify(
395
+				EED_Recaptcha::$_recaptcha_response,
396
+				$_SERVER['REMOTE_ADDR']
397
+			);
398
+		}
399
+		return $recaptcha_response instanceof Response && $recaptcha_response->isSuccess();
400
+	}
401 401
 }
402 402
 // End of file EED_Recaptcha.module.php
403 403
 // Location: /modules/recaptcha/EED_Recaptcha.module.php
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -135,7 +135,7 @@  discard block
 block discarded – undo
135 135
         }
136 136
         define(
137 137
             'RECAPTCHA_BASE_PATH',
138
-            rtrim(str_replace(array('\\', '/'), DS, plugin_dir_path(__FILE__)), DS) . DS
138
+            rtrim(str_replace(array('\\', '/'), DS, plugin_dir_path(__FILE__)), DS).DS
139 139
         );
140 140
         define('RECAPTCHA_BASE_URL', plugin_dir_url(__FILE__));
141 141
     }
@@ -188,19 +188,19 @@  discard block
 block discarded – undo
188 188
     {
189 189
         wp_register_script(
190 190
             'espresso_recaptcha',
191
-            RECAPTCHA_BASE_URL . 'scripts' . DS . 'espresso_recaptcha.js',
191
+            RECAPTCHA_BASE_URL.'scripts'.DS.'espresso_recaptcha.js',
192 192
             array('single_page_checkout'),
193 193
             EVENT_ESPRESSO_VERSION,
194 194
             true
195 195
         );
196 196
         wp_register_script(
197 197
             'google_recaptcha',
198
-            'https://www.google.com/recaptcha/api.js?hl=' . EED_Recaptcha::$config->recaptcha_language,
198
+            'https://www.google.com/recaptcha/api.js?hl='.EED_Recaptcha::$config->recaptcha_language,
199 199
             array('espresso_recaptcha'),
200 200
             EVENT_ESPRESSO_VERSION,
201 201
             true
202 202
         );
203
-        EE_Registry::$i18n_js_strings['no_SPCO_error']      = __(
203
+        EE_Registry::$i18n_js_strings['no_SPCO_error'] = __(
204 204
             'It appears the Single Page Checkout javascript was not loaded properly! Please refresh the page and try again or contact support.',
205 205
             'event_espresso'
206 206
         );
@@ -208,7 +208,7 @@  discard block
 block discarded – undo
208 208
             'There appears to be a problem with the reCAPTCHA configuration! Please check the admin settings or contact support.',
209 209
             'event_espresso'
210 210
         );
211
-        EE_Registry::$i18n_js_strings['recaptcha_fail']     = __(
211
+        EE_Registry::$i18n_js_strings['recaptcha_fail'] = __(
212 212
             'Please complete the anti-spam test before proceeding.',
213 213
             'event_espresso'
214 214
         );
@@ -255,7 +255,7 @@  discard block
 block discarded – undo
255 255
         if (EED_Recaptcha::useRecaptcha() && ! EED_Recaptcha::$_not_a_robot) {
256 256
             // only display if they have NOT passed the test yet
257 257
             EEH_Template::display_template(
258
-                RECAPTCHA_BASE_PATH . DS . 'templates' . DS . 'recaptcha.template.php',
258
+                RECAPTCHA_BASE_PATH.DS.'templates'.DS.'recaptcha.template.php',
259 259
                 array(
260 260
                     'recaptcha_publickey' => EED_Recaptcha::$config->recaptcha_publickey,
261 261
                     'recaptcha_theme'     => EED_Recaptcha::$config->recaptcha_theme,
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
         $recaptcha_passed = filter_var($recaptcha_passed, FILTER_VALIDATE_BOOLEAN);
301 301
         // verify recaptcha
302 302
         EED_Recaptcha::_get_recaptcha_response();
303
-        if (! $recaptcha_passed && EED_Recaptcha::$_recaptcha_response) {
303
+        if ( ! $recaptcha_passed && EED_Recaptcha::$_recaptcha_response) {
304 304
             $recaptcha_passed = EED_Recaptcha::_process_recaptcha_response();
305 305
             EE_Registry::instance()->SSN->set_session_data(array('recaptcha_passed' => $recaptcha_passed));
306 306
         }
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
         foreach ($bypass_request_params_array as $key => $value) {
346 346
             // if $key is not found or value doesn't match exactly, then toggle bypass to FALSE,
347 347
             // otherwise carry over it's value. This way, one missed setting results in no bypass
348
-            $bypass_recaptcha = isset($_REQUEST[ $key ]) && $_REQUEST[ $key ] === $value
348
+            $bypass_recaptcha = isset($_REQUEST[$key]) && $_REQUEST[$key] === $value
349 349
                 ? $bypass_recaptcha
350 350
                 : false;
351 351
         }
@@ -377,8 +377,8 @@  discard block
 block discarded – undo
377 377
     private static function _process_recaptcha_response()
378 378
     {
379 379
         // verify library is loaded
380
-        if (! class_exists('\ReCaptcha\ReCaptcha')) {
381
-            require_once RECAPTCHA_BASE_PATH . DS . 'autoload.php';
380
+        if ( ! class_exists('\ReCaptcha\ReCaptcha')) {
381
+            require_once RECAPTCHA_BASE_PATH.DS.'autoload.php';
382 382
         }
383 383
         // The response from reCAPTCHA
384 384
         EED_Recaptcha::_get_recaptcha_response();
Please login to merge, or discard this patch.
modules/single_page_checkout/inc/EE_SPCO_Reg_Step.class.php 2 patches
Indentation   +633 added lines, -633 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if (! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 
5 5
 
@@ -16,673 +16,673 @@  discard block
 block discarded – undo
16 16
 abstract class EE_SPCO_Reg_Step
17 17
 {
18 18
 
19
-    /**
20
-     *    $_completed - TRUE if this step has fully completed it's duties
21
-     *
22
-     * @access protected
23
-     * @type bool $_completed
24
-     */
25
-    protected $_completed = false;
26
-
27
-    /**
28
-     *    $_is_current_step - TRUE if this is the current step
29
-     *
30
-     * @access protected
31
-     * @type bool $_is_current_step
32
-     */
33
-    protected $_is_current_step = false;
34
-
35
-    /**
36
-     *    $_order - when the reg step should be run relative to other steps
37
-     *
38
-     * @access protected
39
-     * @type int $_template
40
-     */
41
-    protected $_order = 0;
42
-
43
-    /**
44
-     *    $_slug - URL param for this step
45
-     *
46
-     * @access protected
47
-     * @type string $_slug
48
-     */
49
-    protected $_slug;
50
-
51
-    /**
52
-     *    $_name - Step Name - translatable string
53
-     *
54
-     * @access protected
55
-     * @type string $_slug
56
-     */
57
-    protected $_name;
58
-
59
-    /**
60
-     *    $_submit_button_text - translatable string that appears on this step's submit button
61
-     *
62
-     * @access protected
63
-     * @type string $_slug
64
-     */
65
-    protected $_submit_button_text;
66
-
67
-    /**
68
-     *    $_template - template name
69
-     *
70
-     * @access protected
71
-     * @type string $_template
72
-     */
73
-    protected $_template;
74
-
75
-    /**
76
-     *    $_reg_form_name - the form input name and id attribute
77
-     *
78
-     * @access protected
79
-     * @var string $_reg_form_name
80
-     */
81
-    protected $_reg_form_name;
82
-
83
-    /**
84
-     *    $_success_message - text to display upon successful form submission
85
-     *
86
-     * @access private
87
-     * @var string $_success_message
88
-     */
89
-    protected $_success_message;
90
-
91
-    /**
92
-     *    $_instructions - a brief description of how to complete the reg step.
93
-     *    Usually displayed in conjunction with the previous step's success message.
94
-     *
95
-     * @access private
96
-     * @var string $_instructions
97
-     */
98
-    protected $_instructions;
99
-
100
-    /**
101
-     *    $_valid_data - the normalized and validated data for this step
102
-     *
103
-     * @access public
104
-     * @var array $_valid_data
105
-     */
106
-    protected $_valid_data = array();
107
-
108
-    /**
109
-     *    $reg_form - the registration form for this step
110
-     *
111
-     * @access public
112
-     * @var EE_Form_Section_Proper $reg_form
113
-     */
114
-    public $reg_form;
115
-
116
-    /**
117
-     *    $checkout - EE_Checkout object for handling the properties of the current checkout process
118
-     *
119
-     * @access public
120
-     * @var EE_Checkout $checkout
121
-     */
122
-    public $checkout;
123
-
124
-
125
-
126
-    /**
127
-     * @return void
128
-     */
129
-    abstract public function translate_js_strings();
130
-
131
-
19
+	/**
20
+	 *    $_completed - TRUE if this step has fully completed it's duties
21
+	 *
22
+	 * @access protected
23
+	 * @type bool $_completed
24
+	 */
25
+	protected $_completed = false;
26
+
27
+	/**
28
+	 *    $_is_current_step - TRUE if this is the current step
29
+	 *
30
+	 * @access protected
31
+	 * @type bool $_is_current_step
32
+	 */
33
+	protected $_is_current_step = false;
34
+
35
+	/**
36
+	 *    $_order - when the reg step should be run relative to other steps
37
+	 *
38
+	 * @access protected
39
+	 * @type int $_template
40
+	 */
41
+	protected $_order = 0;
42
+
43
+	/**
44
+	 *    $_slug - URL param for this step
45
+	 *
46
+	 * @access protected
47
+	 * @type string $_slug
48
+	 */
49
+	protected $_slug;
50
+
51
+	/**
52
+	 *    $_name - Step Name - translatable string
53
+	 *
54
+	 * @access protected
55
+	 * @type string $_slug
56
+	 */
57
+	protected $_name;
58
+
59
+	/**
60
+	 *    $_submit_button_text - translatable string that appears on this step's submit button
61
+	 *
62
+	 * @access protected
63
+	 * @type string $_slug
64
+	 */
65
+	protected $_submit_button_text;
66
+
67
+	/**
68
+	 *    $_template - template name
69
+	 *
70
+	 * @access protected
71
+	 * @type string $_template
72
+	 */
73
+	protected $_template;
74
+
75
+	/**
76
+	 *    $_reg_form_name - the form input name and id attribute
77
+	 *
78
+	 * @access protected
79
+	 * @var string $_reg_form_name
80
+	 */
81
+	protected $_reg_form_name;
82
+
83
+	/**
84
+	 *    $_success_message - text to display upon successful form submission
85
+	 *
86
+	 * @access private
87
+	 * @var string $_success_message
88
+	 */
89
+	protected $_success_message;
90
+
91
+	/**
92
+	 *    $_instructions - a brief description of how to complete the reg step.
93
+	 *    Usually displayed in conjunction with the previous step's success message.
94
+	 *
95
+	 * @access private
96
+	 * @var string $_instructions
97
+	 */
98
+	protected $_instructions;
99
+
100
+	/**
101
+	 *    $_valid_data - the normalized and validated data for this step
102
+	 *
103
+	 * @access public
104
+	 * @var array $_valid_data
105
+	 */
106
+	protected $_valid_data = array();
107
+
108
+	/**
109
+	 *    $reg_form - the registration form for this step
110
+	 *
111
+	 * @access public
112
+	 * @var EE_Form_Section_Proper $reg_form
113
+	 */
114
+	public $reg_form;
115
+
116
+	/**
117
+	 *    $checkout - EE_Checkout object for handling the properties of the current checkout process
118
+	 *
119
+	 * @access public
120
+	 * @var EE_Checkout $checkout
121
+	 */
122
+	public $checkout;
123
+
124
+
125
+
126
+	/**
127
+	 * @return void
128
+	 */
129
+	abstract public function translate_js_strings();
130
+
131
+
132 132
 
133
-    /**
134
-     * @return void
135
-     */
136
-    abstract public function enqueue_styles_and_scripts();
137
-
138
-
139
-
140
-    /**
141
-     * @return boolean
142
-     */
143
-    abstract public function initialize_reg_step();
144
-
145
-
146
-
147
-    /**
148
-     * @return string
149
-     */
150
-    abstract public function generate_reg_form();
151
-
152
-
153
-
154
-    /**
155
-     * @return boolean
156
-     */
157
-    abstract public function process_reg_step();
158
-
159
-
160
-
161
-    /**
162
-     * @return boolean
163
-     */
164
-    abstract public function update_reg_step();
165
-
166
-
167
-
168
-    /**
169
-     * @return boolean
170
-     */
171
-    public function completed()
172
-    {
173
-        return $this->_completed;
174
-    }
133
+	/**
134
+	 * @return void
135
+	 */
136
+	abstract public function enqueue_styles_and_scripts();
137
+
138
+
139
+
140
+	/**
141
+	 * @return boolean
142
+	 */
143
+	abstract public function initialize_reg_step();
144
+
145
+
146
+
147
+	/**
148
+	 * @return string
149
+	 */
150
+	abstract public function generate_reg_form();
151
+
152
+
153
+
154
+	/**
155
+	 * @return boolean
156
+	 */
157
+	abstract public function process_reg_step();
158
+
159
+
160
+
161
+	/**
162
+	 * @return boolean
163
+	 */
164
+	abstract public function update_reg_step();
165
+
166
+
167
+
168
+	/**
169
+	 * @return boolean
170
+	 */
171
+	public function completed()
172
+	{
173
+		return $this->_completed;
174
+	}
175 175
 
176 176
 
177 177
 
178
-    /**
179
-     * set_completed - toggles $_completed to TRUE
180
-     */
181
-    public function set_completed()
182
-    {
183
-        // DEBUG LOG
184
-        //$this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
185
-        $this->_completed = apply_filters('FHEE__EE_SPCO_Reg_Step__set_completed___completed', true, $this);
186
-    }
178
+	/**
179
+	 * set_completed - toggles $_completed to TRUE
180
+	 */
181
+	public function set_completed()
182
+	{
183
+		// DEBUG LOG
184
+		//$this->checkout->log( __CLASS__, __FUNCTION__, __LINE__ );
185
+		$this->_completed = apply_filters('FHEE__EE_SPCO_Reg_Step__set_completed___completed', true, $this);
186
+	}
187 187
 
188 188
 
189 189
 
190
-    /**
191
-     * set_completed - toggles $_completed to FALSE
192
-     */
193
-    public function set_not_completed()
194
-    {
195
-        $this->_completed = false;
196
-    }
190
+	/**
191
+	 * set_completed - toggles $_completed to FALSE
192
+	 */
193
+	public function set_not_completed()
194
+	{
195
+		$this->_completed = false;
196
+	}
197 197
 
198 198
 
199 199
 
200
-    /**
201
-     * @return string
202
-     */
203
-    public function name()
204
-    {
205
-        return $this->_name;
206
-    }
200
+	/**
201
+	 * @return string
202
+	 */
203
+	public function name()
204
+	{
205
+		return $this->_name;
206
+	}
207 207
 
208 208
 
209 209
 
210
-    /**
211
-     * @return string
212
-     */
213
-    public function slug()
214
-    {
215
-        return $this->_slug;
216
-    }
210
+	/**
211
+	 * @return string
212
+	 */
213
+	public function slug()
214
+	{
215
+		return $this->_slug;
216
+	}
217 217
 
218 218
 
219 219
 
220
-    /**
221
-     * submit_button_text
222
-     * the text that appears on the reg step form submit button
223
-     *
224
-     * @return string
225
-     */
226
-    public function submit_button_text()
227
-    {
228
-        return $this->_submit_button_text;
229
-    }
220
+	/**
221
+	 * submit_button_text
222
+	 * the text that appears on the reg step form submit button
223
+	 *
224
+	 * @return string
225
+	 */
226
+	public function submit_button_text()
227
+	{
228
+		return $this->_submit_button_text;
229
+	}
230 230
 
231 231
 
232 232
 
233
-    /**
234
-     * set_submit_button_text
235
-     * sets the text that appears on the reg step form submit button
236
-     *
237
-     * @param string $submit_button_text
238
-     */
239
-    public function set_submit_button_text($submit_button_text = '')
240
-    {
241
-        if (! empty($submit_button_text)) {
242
-            $this->_submit_button_text = $submit_button_text;
243
-        } else if ($this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
244
-            if ($this->checkout->revisit) {
245
-                $this->_submit_button_text = sprintf(
246
-                    __('Update %s', 'event_espresso'),
247
-                    $this->checkout->current_step->name()
248
-                );
249
-            } else {
250
-                $this->_submit_button_text = sprintf(
251
-                    __('Proceed to %s', 'event_espresso'),
252
-                    $this->checkout->next_step->name()
253
-                );
254
-            }
255
-        }
256
-        // filters the submit button text
257
-        $this->_submit_button_text = apply_filters(
258
-            'FHEE__EE_SPCO_Reg_Step__set_submit_button_text___submit_button_text',
259
-            $this->_submit_button_text,
260
-            $this->checkout
261
-        );
262
-    }
233
+	/**
234
+	 * set_submit_button_text
235
+	 * sets the text that appears on the reg step form submit button
236
+	 *
237
+	 * @param string $submit_button_text
238
+	 */
239
+	public function set_submit_button_text($submit_button_text = '')
240
+	{
241
+		if (! empty($submit_button_text)) {
242
+			$this->_submit_button_text = $submit_button_text;
243
+		} else if ($this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
244
+			if ($this->checkout->revisit) {
245
+				$this->_submit_button_text = sprintf(
246
+					__('Update %s', 'event_espresso'),
247
+					$this->checkout->current_step->name()
248
+				);
249
+			} else {
250
+				$this->_submit_button_text = sprintf(
251
+					__('Proceed to %s', 'event_espresso'),
252
+					$this->checkout->next_step->name()
253
+				);
254
+			}
255
+		}
256
+		// filters the submit button text
257
+		$this->_submit_button_text = apply_filters(
258
+			'FHEE__EE_SPCO_Reg_Step__set_submit_button_text___submit_button_text',
259
+			$this->_submit_button_text,
260
+			$this->checkout
261
+		);
262
+	}
263 263
 
264 264
 
265 265
 
266
-    /**
267
-     * @param boolean $is_current_step
268
-     */
269
-    public function set_is_current_step($is_current_step)
270
-    {
271
-        $this->_is_current_step = $is_current_step;
272
-    }
266
+	/**
267
+	 * @param boolean $is_current_step
268
+	 */
269
+	public function set_is_current_step($is_current_step)
270
+	{
271
+		$this->_is_current_step = $is_current_step;
272
+	}
273 273
 
274 274
 
275 275
 
276
-    /**
277
-     * @return boolean
278
-     */
279
-    public function is_current_step()
280
-    {
281
-        return $this->_is_current_step;
282
-    }
276
+	/**
277
+	 * @return boolean
278
+	 */
279
+	public function is_current_step()
280
+	{
281
+		return $this->_is_current_step;
282
+	}
283 283
 
284 284
 
285 285
 
286
-    /**
287
-     * @return boolean
288
-     */
289
-    public function is_final_step()
290
-    {
291
-        return $this instanceof EE_SPCO_Reg_Step_Finalize_Registration ? true : false;
292
-    }
286
+	/**
287
+	 * @return boolean
288
+	 */
289
+	public function is_final_step()
290
+	{
291
+		return $this instanceof EE_SPCO_Reg_Step_Finalize_Registration ? true : false;
292
+	}
293 293
 
294 294
 
295 295
 
296
-    /**
297
-     * @param int $order
298
-     */
299
-    public function set_order($order)
300
-    {
301
-        $this->_order = $order;
302
-    }
303
-
304
-
296
+	/**
297
+	 * @param int $order
298
+	 */
299
+	public function set_order($order)
300
+	{
301
+		$this->_order = $order;
302
+	}
303
+
304
+
305 305
 
306
-    /**
307
-     * @return int
308
-     */
309
-    public function order()
310
-    {
311
-        return $this->_order;
312
-    }
313
-
314
-
315
-
316
-    /**
317
-     * @return string
318
-     */
319
-    public function template()
320
-    {
321
-        return $this->_template;
322
-    }
323
-
324
-
325
-
326
-    /**
327
-     * @return string
328
-     */
329
-    public function success_message()
330
-    {
331
-        return $this->_success_message;
332
-    }
333
-
334
-
335
-
336
-    /**
337
-     * _set_success_message
338
-     *
339
-     * @param string $success_message
340
-     */
341
-    protected function _set_success_message($success_message)
342
-    {
343
-        $this->_success_message = $success_message;
344
-    }
345
-
346
-
347
-
348
-    /**
349
-     * _reset_success_message
350
-     *
351
-     * @return void
352
-     */
353
-    protected function _reset_success_message()
354
-    {
355
-        $this->_success_message = '';
356
-    }
306
+	/**
307
+	 * @return int
308
+	 */
309
+	public function order()
310
+	{
311
+		return $this->_order;
312
+	}
313
+
314
+
315
+
316
+	/**
317
+	 * @return string
318
+	 */
319
+	public function template()
320
+	{
321
+		return $this->_template;
322
+	}
323
+
324
+
325
+
326
+	/**
327
+	 * @return string
328
+	 */
329
+	public function success_message()
330
+	{
331
+		return $this->_success_message;
332
+	}
333
+
334
+
335
+
336
+	/**
337
+	 * _set_success_message
338
+	 *
339
+	 * @param string $success_message
340
+	 */
341
+	protected function _set_success_message($success_message)
342
+	{
343
+		$this->_success_message = $success_message;
344
+	}
345
+
346
+
347
+
348
+	/**
349
+	 * _reset_success_message
350
+	 *
351
+	 * @return void
352
+	 */
353
+	protected function _reset_success_message()
354
+	{
355
+		$this->_success_message = '';
356
+	}
357 357
 
358 358
 
359 359
 
360
-    /**
361
-     * @return string
362
-     */
363
-    public function _instructions()
364
-    {
365
-        return $this->_instructions;
366
-    }
367
-
368
-
369
-
370
-    /**
371
-     * @param string $instructions
372
-     */
373
-    public function set_instructions($instructions)
374
-    {
375
-        $this->_instructions = apply_filters(
376
-            'FHEE__EE_SPCO_Reg_Step__set_instructions__instructions',
377
-            $instructions,
378
-            $this
379
-        );
380
-    }
381
-
382
-
383
-
384
-    /**
385
-     * @param array $valid_data
386
-     */
387
-    public function set_valid_data($valid_data)
388
-    {
389
-        $this->_valid_data = $valid_data;
390
-    }
391
-
392
-
393
-
394
-    /**
395
-     * @return array
396
-     */
397
-    public function valid_data()
398
-    {
399
-        if (empty($this->_valid_data)) {
400
-            $this->_valid_data = $this->reg_form->valid_data();
401
-        }
402
-        return $this->_valid_data;
403
-    }
404
-
405
-
406
-
407
-    /**
408
-     * @return string
409
-     */
410
-    public function reg_form_name()
411
-    {
412
-        if (empty($this->_reg_form_name)) {
413
-            $this->set_reg_form_name('ee-spco-' . $this->slug() . '-reg-step-form');
414
-        }
415
-        return $this->_reg_form_name;
416
-    }
417
-
418
-
419
-
420
-    /**
421
-     * @param string $reg_form_name
422
-     */
423
-    protected function set_reg_form_name($reg_form_name)
424
-    {
425
-        $this->_reg_form_name = $reg_form_name;
426
-    }
427
-
428
-
429
-
430
-    /**
431
-     * reg_step_url
432
-     *
433
-     * @param string $action
434
-     * @return string
435
-     */
436
-    public function reg_step_url($action = '')
437
-    {
438
-        $query_args = array('step' => $this->slug());
439
-        if (! empty($action)) {
440
-            $query_args['action'] = $action;
441
-        }
442
-        // final step has no display
443
-        if ($this instanceof EE_SPCO_Reg_Step_Finalize_Registration && $action === 'display_spco_reg_step') {
444
-            $query_args['action'] = 'process_reg_step';
445
-        }
446
-        if ($this->checkout->revisit) {
447
-            $query_args['revisit'] = true;
448
-        }
449
-        if ($this->checkout->reg_url_link) {
450
-            $query_args['e_reg_url_link'] = $this->checkout->reg_url_link;
451
-        }
452
-        return add_query_arg($query_args, $this->checkout->reg_page_base_url);
453
-    }
454
-
455
-
456
-
457
-    /**
458
-     * creates the default hidden inputs section
459
-     *
460
-     * @return EE_Form_Section_Proper
461
-     * @throws \EE_Error
462
-     */
463
-    public function reg_step_hidden_inputs()
464
-    {
465
-        // hidden inputs for admin registrations
466
-        if ($this->checkout->admin_request) {
467
-            return new EE_Form_Section_Proper(
468
-                array(
469
-                    'layout_strategy' => new EE_Div_Per_Section_Layout(),
470
-                    'html_id'         => 'ee-' . $this->slug() . '-hidden-inputs',
471
-                    'subsections'     => array(
472
-                        'next_step' => new EE_Fixed_Hidden_Input(
473
-                            array(
474
-                                'html_name' => 'next_step',
475
-                                'html_id'   => 'spco-' . $this->slug() . '-next-step',
476
-                                'default'   => $this->checkout->next_step instanceof EE_SPCO_Reg_Step
477
-                                    ? $this->checkout->next_step->slug()
478
-                                    : '',
479
-                            )
480
-                        ),
481
-                    ),
482
-                )
483
-            );
484
-        }
485
-        // hidden inputs for frontend registrations
486
-        return new EE_Form_Section_Proper(
487
-            array(
488
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
489
-                'html_id'         => 'ee-' . $this->slug() . '-hidden-inputs',
490
-                'subsections'     => array(
491
-                    'action'         => new EE_Fixed_Hidden_Input(
492
-                        array(
493
-                            'html_name' => 'action',
494
-                            'html_id'   => 'spco-' . $this->slug() . '-action',
495
-                            'default'   => apply_filters(
496
-                                'FHEE__EE_SPCO_Reg_Step__reg_step_hidden_inputs__default_form_action',
497
-                                empty($this->checkout->reg_url_link)
498
-                                    ? 'process_reg_step'
499
-                                    : 'update_reg_step',
500
-                                $this
501
-                            ),
502
-                        )
503
-                    ),
504
-                    'next_step'      => new EE_Fixed_Hidden_Input(
505
-                        array(
506
-                            'html_name' => 'next_step',
507
-                            'html_id'   => 'spco-' . $this->slug() . '-next-step',
508
-                            'default'   => $this->checkout->next_step instanceof EE_SPCO_Reg_Step
509
-                                ? $this->checkout->next_step->slug()
510
-                                : '',
511
-                        )
512
-                    ),
513
-                    'e_reg_url_link' => new EE_Fixed_Hidden_Input(
514
-                        array(
515
-                            'html_name' => 'e_reg_url_link',
516
-                            'html_id'   => 'spco-reg_url_link',
517
-                            'default'   => $this->checkout->reg_url_link,
518
-                        )
519
-                    ),
520
-                    'revisit'        => new EE_Fixed_Hidden_Input(
521
-                        array(
522
-                            'html_name' => 'revisit',
523
-                            'html_id'   => 'spco-revisit',
524
-                            'default'   => $this->checkout->revisit,
525
-                        )
526
-                    ),
527
-                ),
528
-            )
529
-        );
530
-    }
531
-
532
-
533
-
534
-    /**
535
-     * generate_reg_form_for_actions
536
-     *
537
-     * @param array $actions
538
-     * @return void
539
-     */
540
-    public function generate_reg_form_for_actions($actions = array())
541
-    {
542
-        $actions = array_merge(
543
-            array(
544
-                'generate_reg_form',
545
-                'display_spco_reg_step',
546
-                'process_reg_step',
547
-                'update_reg_step',
548
-            ),
549
-            $actions
550
-        );
551
-        $this->checkout->generate_reg_form = in_array($this->checkout->action, $actions, true) ? true : false;
552
-    }
553
-
554
-
555
-
556
-    /**
557
-     * @return string
558
-     * @throws \EE_Error
559
-     */
560
-    public function display_reg_form()
561
-    {
562
-        $html = '';
563
-        if ($this->reg_form instanceof EE_Form_Section_Proper) {
564
-            do_action('AHEE__EE_SPCO_Reg_Step__display_reg_form__reg_form', $this->reg_form, $this);
565
-            $html .= ! $this->checkout->admin_request ? $this->reg_form->form_open($this->reg_step_url()) : '';
566
-            if (EE_Registry::instance()->REQ->ajax) {
567
-                $this->reg_form->localize_validation_rules();
568
-                $this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
569
-            }
570
-            $html .= $this->reg_form->get_html();
571
-            $html .= ! $this->checkout->admin_request ? $this->reg_step_submit_button() : '';
572
-            $html .= ! $this->checkout->admin_request ? $this->reg_form->form_close() : '';
573
-        }
574
-        return $html;
575
-    }
576
-
577
-
578
-
579
-    /**
580
-     * div_class - returns nothing for current step, but a css class of "hidden" for others
581
-     *
582
-     * @return string
583
-     * @throws \EE_Error
584
-     */
585
-    public function reg_step_submit_button()
586
-    {
587
-        if (! $this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
588
-            return '';
589
-        }
590
-        ob_start();
591
-        do_action(
592
-            'AHEE__before_spco_whats_next_buttons',
593
-            $this->slug(),
594
-            $this->checkout->next_step->slug(),
595
-            $this->checkout
596
-        );
597
-        $html = ob_get_clean();
598
-        // generate submit button
599
-        $sbmt_btn = new EE_Submit_Input(
600
-            array(
601
-                'html_name'             => 'spco-go-to-step-' . $this->checkout->next_step->slug(),
602
-                'html_id'               => 'spco-go-to-step-' . $this->checkout->next_step->slug(),
603
-                'html_class'            => 'spco-next-step-btn',
604
-                'other_html_attributes' => ' rel="' . $this->slug() . '"',
605
-                'default'               => $this->submit_button_text(),
606
-            )
607
-        );
608
-        $sbmt_btn->set_button_css_attributes(true, 'large');
609
-        $sbmt_btn_html = $sbmt_btn->get_html_for_input();
610
-        $html .= EEH_HTML::div(
611
-            apply_filters('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', $sbmt_btn_html, $this),
612
-            'spco-' . $this->slug() . '-whats-next-buttons-dv',
613
-            'spco-whats-next-buttons'
614
-        );
615
-        return $html;
616
-    }
617
-
618
-
619
-
620
-    /**
621
-     * div_class - returns nothing for current step, but a css class of "hidden" for others
622
-     *
623
-     * @return string
624
-     */
625
-    public function div_class()
626
-    {
627
-        return $this->is_current_step() ? '' : ' hidden';
628
-    }
629
-
630
-
631
-
632
-    /**
633
-     * div_class - returns  a css class of "hidden" for current step, but nothing for others
634
-     *
635
-     * @return string
636
-     */
637
-    public function edit_lnk_url()
638
-    {
639
-        return add_query_arg(array('step' => $this->slug()), $this->checkout->reg_page_base_url);
640
-    }
641
-
642
-
643
-
644
-    /**
645
-     * div_class - returns  a css class of "hidden" for current step, but nothing for others
646
-     *
647
-     * @return string
648
-     */
649
-    public function edit_link_class()
650
-    {
651
-        return $this->is_current_step() ? ' hidden' : '';
652
-    }
653
-
654
-
655
-
656
-    /**
657
-     * update_checkout with changes that have been made to the cart
658
-     *
659
-     * @return void
660
-     * @throws \EE_Error
661
-     */
662
-    public function update_checkout()
663
-    {
664
-        // grab the cart grand total and reset TXN total
665
-        $this->checkout->transaction->set_total($this->checkout->cart->get_cart_grand_total());
666
-        $this->checkout->stash_transaction_and_checkout();
667
-    }
668
-
669
-
670
-
671
-
672
-
673
-    /**
674
-     *    __sleep
675
-     * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
676
-     * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
677
-     * reg form, because if needed, it will be regenerated anyways
678
-     *
679
-     * @return array
680
-     */
681
-    public function __sleep()
682
-    {
683
-        // remove the reg form and the checkout
684
-        return array_diff(array_keys(get_object_vars($this)), array('reg_form', 'checkout'));
685
-    }
360
+	/**
361
+	 * @return string
362
+	 */
363
+	public function _instructions()
364
+	{
365
+		return $this->_instructions;
366
+	}
367
+
368
+
369
+
370
+	/**
371
+	 * @param string $instructions
372
+	 */
373
+	public function set_instructions($instructions)
374
+	{
375
+		$this->_instructions = apply_filters(
376
+			'FHEE__EE_SPCO_Reg_Step__set_instructions__instructions',
377
+			$instructions,
378
+			$this
379
+		);
380
+	}
381
+
382
+
383
+
384
+	/**
385
+	 * @param array $valid_data
386
+	 */
387
+	public function set_valid_data($valid_data)
388
+	{
389
+		$this->_valid_data = $valid_data;
390
+	}
391
+
392
+
393
+
394
+	/**
395
+	 * @return array
396
+	 */
397
+	public function valid_data()
398
+	{
399
+		if (empty($this->_valid_data)) {
400
+			$this->_valid_data = $this->reg_form->valid_data();
401
+		}
402
+		return $this->_valid_data;
403
+	}
404
+
405
+
406
+
407
+	/**
408
+	 * @return string
409
+	 */
410
+	public function reg_form_name()
411
+	{
412
+		if (empty($this->_reg_form_name)) {
413
+			$this->set_reg_form_name('ee-spco-' . $this->slug() . '-reg-step-form');
414
+		}
415
+		return $this->_reg_form_name;
416
+	}
417
+
418
+
419
+
420
+	/**
421
+	 * @param string $reg_form_name
422
+	 */
423
+	protected function set_reg_form_name($reg_form_name)
424
+	{
425
+		$this->_reg_form_name = $reg_form_name;
426
+	}
427
+
428
+
429
+
430
+	/**
431
+	 * reg_step_url
432
+	 *
433
+	 * @param string $action
434
+	 * @return string
435
+	 */
436
+	public function reg_step_url($action = '')
437
+	{
438
+		$query_args = array('step' => $this->slug());
439
+		if (! empty($action)) {
440
+			$query_args['action'] = $action;
441
+		}
442
+		// final step has no display
443
+		if ($this instanceof EE_SPCO_Reg_Step_Finalize_Registration && $action === 'display_spco_reg_step') {
444
+			$query_args['action'] = 'process_reg_step';
445
+		}
446
+		if ($this->checkout->revisit) {
447
+			$query_args['revisit'] = true;
448
+		}
449
+		if ($this->checkout->reg_url_link) {
450
+			$query_args['e_reg_url_link'] = $this->checkout->reg_url_link;
451
+		}
452
+		return add_query_arg($query_args, $this->checkout->reg_page_base_url);
453
+	}
454
+
455
+
456
+
457
+	/**
458
+	 * creates the default hidden inputs section
459
+	 *
460
+	 * @return EE_Form_Section_Proper
461
+	 * @throws \EE_Error
462
+	 */
463
+	public function reg_step_hidden_inputs()
464
+	{
465
+		// hidden inputs for admin registrations
466
+		if ($this->checkout->admin_request) {
467
+			return new EE_Form_Section_Proper(
468
+				array(
469
+					'layout_strategy' => new EE_Div_Per_Section_Layout(),
470
+					'html_id'         => 'ee-' . $this->slug() . '-hidden-inputs',
471
+					'subsections'     => array(
472
+						'next_step' => new EE_Fixed_Hidden_Input(
473
+							array(
474
+								'html_name' => 'next_step',
475
+								'html_id'   => 'spco-' . $this->slug() . '-next-step',
476
+								'default'   => $this->checkout->next_step instanceof EE_SPCO_Reg_Step
477
+									? $this->checkout->next_step->slug()
478
+									: '',
479
+							)
480
+						),
481
+					),
482
+				)
483
+			);
484
+		}
485
+		// hidden inputs for frontend registrations
486
+		return new EE_Form_Section_Proper(
487
+			array(
488
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
489
+				'html_id'         => 'ee-' . $this->slug() . '-hidden-inputs',
490
+				'subsections'     => array(
491
+					'action'         => new EE_Fixed_Hidden_Input(
492
+						array(
493
+							'html_name' => 'action',
494
+							'html_id'   => 'spco-' . $this->slug() . '-action',
495
+							'default'   => apply_filters(
496
+								'FHEE__EE_SPCO_Reg_Step__reg_step_hidden_inputs__default_form_action',
497
+								empty($this->checkout->reg_url_link)
498
+									? 'process_reg_step'
499
+									: 'update_reg_step',
500
+								$this
501
+							),
502
+						)
503
+					),
504
+					'next_step'      => new EE_Fixed_Hidden_Input(
505
+						array(
506
+							'html_name' => 'next_step',
507
+							'html_id'   => 'spco-' . $this->slug() . '-next-step',
508
+							'default'   => $this->checkout->next_step instanceof EE_SPCO_Reg_Step
509
+								? $this->checkout->next_step->slug()
510
+								: '',
511
+						)
512
+					),
513
+					'e_reg_url_link' => new EE_Fixed_Hidden_Input(
514
+						array(
515
+							'html_name' => 'e_reg_url_link',
516
+							'html_id'   => 'spco-reg_url_link',
517
+							'default'   => $this->checkout->reg_url_link,
518
+						)
519
+					),
520
+					'revisit'        => new EE_Fixed_Hidden_Input(
521
+						array(
522
+							'html_name' => 'revisit',
523
+							'html_id'   => 'spco-revisit',
524
+							'default'   => $this->checkout->revisit,
525
+						)
526
+					),
527
+				),
528
+			)
529
+		);
530
+	}
531
+
532
+
533
+
534
+	/**
535
+	 * generate_reg_form_for_actions
536
+	 *
537
+	 * @param array $actions
538
+	 * @return void
539
+	 */
540
+	public function generate_reg_form_for_actions($actions = array())
541
+	{
542
+		$actions = array_merge(
543
+			array(
544
+				'generate_reg_form',
545
+				'display_spco_reg_step',
546
+				'process_reg_step',
547
+				'update_reg_step',
548
+			),
549
+			$actions
550
+		);
551
+		$this->checkout->generate_reg_form = in_array($this->checkout->action, $actions, true) ? true : false;
552
+	}
553
+
554
+
555
+
556
+	/**
557
+	 * @return string
558
+	 * @throws \EE_Error
559
+	 */
560
+	public function display_reg_form()
561
+	{
562
+		$html = '';
563
+		if ($this->reg_form instanceof EE_Form_Section_Proper) {
564
+			do_action('AHEE__EE_SPCO_Reg_Step__display_reg_form__reg_form', $this->reg_form, $this);
565
+			$html .= ! $this->checkout->admin_request ? $this->reg_form->form_open($this->reg_step_url()) : '';
566
+			if (EE_Registry::instance()->REQ->ajax) {
567
+				$this->reg_form->localize_validation_rules();
568
+				$this->checkout->json_response->add_validation_rules(EE_Form_Section_Proper::js_localization());
569
+			}
570
+			$html .= $this->reg_form->get_html();
571
+			$html .= ! $this->checkout->admin_request ? $this->reg_step_submit_button() : '';
572
+			$html .= ! $this->checkout->admin_request ? $this->reg_form->form_close() : '';
573
+		}
574
+		return $html;
575
+	}
576
+
577
+
578
+
579
+	/**
580
+	 * div_class - returns nothing for current step, but a css class of "hidden" for others
581
+	 *
582
+	 * @return string
583
+	 * @throws \EE_Error
584
+	 */
585
+	public function reg_step_submit_button()
586
+	{
587
+		if (! $this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
588
+			return '';
589
+		}
590
+		ob_start();
591
+		do_action(
592
+			'AHEE__before_spco_whats_next_buttons',
593
+			$this->slug(),
594
+			$this->checkout->next_step->slug(),
595
+			$this->checkout
596
+		);
597
+		$html = ob_get_clean();
598
+		// generate submit button
599
+		$sbmt_btn = new EE_Submit_Input(
600
+			array(
601
+				'html_name'             => 'spco-go-to-step-' . $this->checkout->next_step->slug(),
602
+				'html_id'               => 'spco-go-to-step-' . $this->checkout->next_step->slug(),
603
+				'html_class'            => 'spco-next-step-btn',
604
+				'other_html_attributes' => ' rel="' . $this->slug() . '"',
605
+				'default'               => $this->submit_button_text(),
606
+			)
607
+		);
608
+		$sbmt_btn->set_button_css_attributes(true, 'large');
609
+		$sbmt_btn_html = $sbmt_btn->get_html_for_input();
610
+		$html .= EEH_HTML::div(
611
+			apply_filters('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', $sbmt_btn_html, $this),
612
+			'spco-' . $this->slug() . '-whats-next-buttons-dv',
613
+			'spco-whats-next-buttons'
614
+		);
615
+		return $html;
616
+	}
617
+
618
+
619
+
620
+	/**
621
+	 * div_class - returns nothing for current step, but a css class of "hidden" for others
622
+	 *
623
+	 * @return string
624
+	 */
625
+	public function div_class()
626
+	{
627
+		return $this->is_current_step() ? '' : ' hidden';
628
+	}
629
+
630
+
631
+
632
+	/**
633
+	 * div_class - returns  a css class of "hidden" for current step, but nothing for others
634
+	 *
635
+	 * @return string
636
+	 */
637
+	public function edit_lnk_url()
638
+	{
639
+		return add_query_arg(array('step' => $this->slug()), $this->checkout->reg_page_base_url);
640
+	}
641
+
642
+
643
+
644
+	/**
645
+	 * div_class - returns  a css class of "hidden" for current step, but nothing for others
646
+	 *
647
+	 * @return string
648
+	 */
649
+	public function edit_link_class()
650
+	{
651
+		return $this->is_current_step() ? ' hidden' : '';
652
+	}
653
+
654
+
655
+
656
+	/**
657
+	 * update_checkout with changes that have been made to the cart
658
+	 *
659
+	 * @return void
660
+	 * @throws \EE_Error
661
+	 */
662
+	public function update_checkout()
663
+	{
664
+		// grab the cart grand total and reset TXN total
665
+		$this->checkout->transaction->set_total($this->checkout->cart->get_cart_grand_total());
666
+		$this->checkout->stash_transaction_and_checkout();
667
+	}
668
+
669
+
670
+
671
+
672
+
673
+	/**
674
+	 *    __sleep
675
+	 * to conserve db space, let's remove the reg_form and the EE_Checkout object from EE_SPCO_Reg_Step objects upon
676
+	 * serialization EE_Checkout will handle the reimplementation of itself upon waking, but we won't bother with the
677
+	 * reg form, because if needed, it will be regenerated anyways
678
+	 *
679
+	 * @return array
680
+	 */
681
+	public function __sleep()
682
+	{
683
+		// remove the reg form and the checkout
684
+		return array_diff(array_keys(get_object_vars($this)), array('reg_form', 'checkout'));
685
+	}
686 686
 
687 687
 
688 688
 
Please login to merge, or discard this patch.
Spacing   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -1,4 +1,4 @@  discard block
 block discarded – undo
1
-<?php if (! defined('EVENT_ESPRESSO_VERSION')) {
1
+<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2 2
     exit('No direct script access allowed');
3 3
 }
4 4
 
@@ -238,7 +238,7 @@  discard block
 block discarded – undo
238 238
      */
239 239
     public function set_submit_button_text($submit_button_text = '')
240 240
     {
241
-        if (! empty($submit_button_text)) {
241
+        if ( ! empty($submit_button_text)) {
242 242
             $this->_submit_button_text = $submit_button_text;
243 243
         } else if ($this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
244 244
             if ($this->checkout->revisit) {
@@ -410,7 +410,7 @@  discard block
 block discarded – undo
410 410
     public function reg_form_name()
411 411
     {
412 412
         if (empty($this->_reg_form_name)) {
413
-            $this->set_reg_form_name('ee-spco-' . $this->slug() . '-reg-step-form');
413
+            $this->set_reg_form_name('ee-spco-'.$this->slug().'-reg-step-form');
414 414
         }
415 415
         return $this->_reg_form_name;
416 416
     }
@@ -436,7 +436,7 @@  discard block
 block discarded – undo
436 436
     public function reg_step_url($action = '')
437 437
     {
438 438
         $query_args = array('step' => $this->slug());
439
-        if (! empty($action)) {
439
+        if ( ! empty($action)) {
440 440
             $query_args['action'] = $action;
441 441
         }
442 442
         // final step has no display
@@ -467,12 +467,12 @@  discard block
 block discarded – undo
467 467
             return new EE_Form_Section_Proper(
468 468
                 array(
469 469
                     'layout_strategy' => new EE_Div_Per_Section_Layout(),
470
-                    'html_id'         => 'ee-' . $this->slug() . '-hidden-inputs',
470
+                    'html_id'         => 'ee-'.$this->slug().'-hidden-inputs',
471 471
                     'subsections'     => array(
472 472
                         'next_step' => new EE_Fixed_Hidden_Input(
473 473
                             array(
474 474
                                 'html_name' => 'next_step',
475
-                                'html_id'   => 'spco-' . $this->slug() . '-next-step',
475
+                                'html_id'   => 'spco-'.$this->slug().'-next-step',
476 476
                                 'default'   => $this->checkout->next_step instanceof EE_SPCO_Reg_Step
477 477
                                     ? $this->checkout->next_step->slug()
478 478
                                     : '',
@@ -486,12 +486,12 @@  discard block
 block discarded – undo
486 486
         return new EE_Form_Section_Proper(
487 487
             array(
488 488
                 'layout_strategy' => new EE_Div_Per_Section_Layout(),
489
-                'html_id'         => 'ee-' . $this->slug() . '-hidden-inputs',
489
+                'html_id'         => 'ee-'.$this->slug().'-hidden-inputs',
490 490
                 'subsections'     => array(
491 491
                     'action'         => new EE_Fixed_Hidden_Input(
492 492
                         array(
493 493
                             'html_name' => 'action',
494
-                            'html_id'   => 'spco-' . $this->slug() . '-action',
494
+                            'html_id'   => 'spco-'.$this->slug().'-action',
495 495
                             'default'   => apply_filters(
496 496
                                 'FHEE__EE_SPCO_Reg_Step__reg_step_hidden_inputs__default_form_action',
497 497
                                 empty($this->checkout->reg_url_link)
@@ -504,7 +504,7 @@  discard block
 block discarded – undo
504 504
                     'next_step'      => new EE_Fixed_Hidden_Input(
505 505
                         array(
506 506
                             'html_name' => 'next_step',
507
-                            'html_id'   => 'spco-' . $this->slug() . '-next-step',
507
+                            'html_id'   => 'spco-'.$this->slug().'-next-step',
508 508
                             'default'   => $this->checkout->next_step instanceof EE_SPCO_Reg_Step
509 509
                                 ? $this->checkout->next_step->slug()
510 510
                                 : '',
@@ -584,7 +584,7 @@  discard block
 block discarded – undo
584 584
      */
585 585
     public function reg_step_submit_button()
586 586
     {
587
-        if (! $this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
587
+        if ( ! $this->checkout->next_step instanceof EE_SPCO_Reg_Step) {
588 588
             return '';
589 589
         }
590 590
         ob_start();
@@ -598,10 +598,10 @@  discard block
 block discarded – undo
598 598
         // generate submit button
599 599
         $sbmt_btn = new EE_Submit_Input(
600 600
             array(
601
-                'html_name'             => 'spco-go-to-step-' . $this->checkout->next_step->slug(),
602
-                'html_id'               => 'spco-go-to-step-' . $this->checkout->next_step->slug(),
601
+                'html_name'             => 'spco-go-to-step-'.$this->checkout->next_step->slug(),
602
+                'html_id'               => 'spco-go-to-step-'.$this->checkout->next_step->slug(),
603 603
                 'html_class'            => 'spco-next-step-btn',
604
-                'other_html_attributes' => ' rel="' . $this->slug() . '"',
604
+                'other_html_attributes' => ' rel="'.$this->slug().'"',
605 605
                 'default'               => $this->submit_button_text(),
606 606
             )
607 607
         );
@@ -609,7 +609,7 @@  discard block
 block discarded – undo
609 609
         $sbmt_btn_html = $sbmt_btn->get_html_for_input();
610 610
         $html .= EEH_HTML::div(
611 611
             apply_filters('FHEE__EE_SPCO_Reg_Step__reg_step_submit_button__sbmt_btn_html', $sbmt_btn_html, $this),
612
-            'spco-' . $this->slug() . '-whats-next-buttons-dv',
612
+            'spco-'.$this->slug().'-whats-next-buttons-dv',
613 613
             'spco-whats-next-buttons'
614 614
         );
615 615
         return $html;
Please login to merge, or discard this patch.
modules/ticket_selector/DisplayTicketSelector.php 2 patches
Spacing   +29 added lines, -29 removed lines patch added patch discarded remove patch
@@ -19,7 +19,7 @@  discard block
 block discarded – undo
19 19
 use InvalidArgumentException;
20 20
 use WP_Post;
21 21
 
22
-defined('EVENT_ESPRESSO_VERSION')|| exit('No direct script access allowed');
22
+defined('EVENT_ESPRESSO_VERSION') || exit('No direct script access allowed');
23 23
 
24 24
 
25 25
 
@@ -140,11 +140,11 @@  discard block
 block discarded – undo
140 140
             }
141 141
         } else {
142 142
             $user_msg = __('No Event object or an invalid Event object was supplied.', 'event_espresso');
143
-            $dev_msg  = $user_msg . __(
143
+            $dev_msg  = $user_msg.__(
144 144
                     'In order to generate a ticket selector, please ensure you are passing either an EE_Event object or a WP_Post object of the post type "espresso_event" to the EE_Ticket_Selector class constructor.',
145 145
                     'event_espresso'
146 146
                 );
147
-            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
147
+            EE_Error::add_error($user_msg.'||'.$dev_msg, __FILE__, __FUNCTION__, __LINE__);
148 148
             return false;
149 149
         }
150 150
         return true;
@@ -208,7 +208,7 @@  discard block
 block discarded – undo
208 208
         // reset filter for displaying submit button
209 209
         remove_filter('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', '__return_true');
210 210
         // poke and prod incoming event till it tells us what it is
211
-        if (! $this->setEvent($event)) {
211
+        if ( ! $this->setEvent($event)) {
212 212
             return false;
213 213
         }
214 214
         // begin gathering template arguments by getting event status
@@ -238,14 +238,14 @@  discard block
 block discarded – undo
238 238
             return $this->noTicketAvailableMessage();
239 239
         }
240 240
         // redirecting to another site for registration ??
241
-        $external_url = (string)$this->event->external_url();
241
+        $external_url = (string) $this->event->external_url();
242 242
         // if redirecting to another site for registration, then we don't load the TS
243 243
         $ticket_selector = $external_url
244 244
             ? $this->externalEventRegistration()
245 245
             : $this->loadTicketSelector($tickets, $template_args);
246 246
         // now set up the form (but not for the admin)
247 247
         $ticket_selector = $this->display_full_ui()
248
-            ? $this->formOpen($this->event->ID(), $external_url) . $ticket_selector
248
+            ? $this->formOpen($this->event->ID(), $external_url).$ticket_selector
249 249
             : $ticket_selector;
250 250
         // submit button and form close tag
251 251
         $ticket_selector .= $this->display_full_ui() ? $this->displaySubmitButton($external_url) : '';
@@ -295,10 +295,10 @@  discard block
 block discarded – undo
295 295
      */
296 296
     protected function expiredEventMessage()
297 297
     {
298
-        return '<div class="ee-event-expired-notice"><span class="important-notice">' . esc_html__(
298
+        return '<div class="ee-event-expired-notice"><span class="important-notice">'.esc_html__(
299 299
                 'We\'re sorry, but all tickets sales have ended because the event is expired.',
300 300
                 'event_espresso'
301
-            ) . '</span></div><!-- .ee-event-expired-notice -->';
301
+            ).'</span></div><!-- .ee-event-expired-notice -->';
302 302
     }
303 303
 
304 304
 
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
         }
330 330
         return '
331 331
             <div class="ee-event-expired-notice">
332
-                <span class="important-notice">' . $no_ticket_available_msg . '</span>
332
+                <span class="important-notice">' . $no_ticket_available_msg.'</span>
333 333
             </div><!-- .ee-event-expired-notice -->';
334 334
     }
335 335
 
@@ -362,7 +362,7 @@  discard block
 block discarded – undo
362 362
                 '</a></span></div><!-- .ee-attention ticketSalesClosedMessage -->'
363 363
             );
364 364
         }
365
-        return '<p><span class="important-notice">' . $sales_closed_msg . '</span></p>';
365
+        return '<p><span class="important-notice">'.$sales_closed_msg.'</span></p>';
366 366
     }
367 367
 
368 368
 
@@ -430,9 +430,9 @@  discard block
 block discarded – undo
430 430
          * @param     string  '#tkt-slctr-tbl-' . $EVT_ID The html ID to anchor to
431 431
          * @param int $EVT_ID The Event ID
432 432
          */
433
-        $template_args['anchor_id']    = apply_filters(
433
+        $template_args['anchor_id'] = apply_filters(
434 434
             'FHEE__EE_Ticket_Selector__redirect_anchor_id',
435
-            '#tkt-slctr-tbl-' . $this->event->ID(),
435
+            '#tkt-slctr-tbl-'.$this->event->ID(),
436 436
             $this->event->ID()
437 437
         );
438 438
         $template_args['tickets']      = $tickets;
@@ -523,8 +523,8 @@  discard block
 block discarded – undo
523 523
         // if redirecting, we don't need any anything else
524 524
         if ($external_url) {
525 525
             $html = '<form method="GET" ';
526
-            $html .= 'action="' . EEH_URL::refactor_url($external_url) . '" ';
527
-            $html .= 'name="ticket-selector-form-' . $ID . '"';
526
+            $html .= 'action="'.EEH_URL::refactor_url($external_url).'" ';
527
+            $html .= 'name="ticket-selector-form-'.$ID.'"';
528 528
             // open link in new window ?
529 529
             $html .= apply_filters(
530 530
                 'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__formOpen__external_url_target_blank',
@@ -536,17 +536,17 @@  discard block
 block discarded – undo
536 536
             $html .= '>';
537 537
             $query_args = EEH_URL::get_query_string($external_url);
538 538
             foreach ((array) $query_args as $query_arg => $value) {
539
-                $html .= '<input type="hidden" name="' . $query_arg . '" value="' . $value . '">';
539
+                $html .= '<input type="hidden" name="'.$query_arg.'" value="'.$value.'">';
540 540
             }
541 541
             return $html;
542 542
         }
543 543
         // if there is no submit button, then don't start building a form
544 544
         // because the "View Details" button will build its own form
545
-        if (! apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
545
+        if ( ! apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
546 546
             return '';
547 547
         }
548 548
         $checkout_url = EEH_Event_View::event_link_url($ID);
549
-        if (! $checkout_url) {
549
+        if ( ! $checkout_url) {
550 550
             EE_Error::add_error(
551 551
                 esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
552 552
                 __FILE__,
@@ -557,8 +557,8 @@  discard block
 block discarded – undo
557 557
         // set no cache headers and constants
558 558
         EE_System::do_not_cache();
559 559
         $html = '<form method="POST" ';
560
-        $html .= 'action="' . $checkout_url . '" ';
561
-        $html .= 'name="ticket-selector-form-' . $ID . '"';
560
+        $html .= 'action="'.$checkout_url.'" ';
561
+        $html .= 'name="ticket-selector-form-'.$ID.'"';
562 562
         $html .= $this->iframe ? ' target="_blank"' : '';
563 563
         $html .= '>';
564 564
         $html .= '<input type="hidden" name="ee" value="process_ticket_selections">';
@@ -585,7 +585,7 @@  discard block
 block discarded – undo
585 585
                 $html .= empty($external_url)
586 586
                     ? $this->ticketSelectorEndDiv()
587 587
                     : $this->clearTicketSelector();
588
-                $html .= '<br/>' . $this->formClose();
588
+                $html .= '<br/>'.$this->formClose();
589 589
             } elseif ($this->getMaxAttendees() === 1) {
590 590
                 // its a "Dude Where's my Ticket Selector?" (DWMTS) type event (ie: $_max_atndz === 1)
591 591
                 if ($this->event->is_sold_out()) {
@@ -643,7 +643,7 @@  discard block
 block discarded – undo
643 643
                 // no submit or view details button, and no additional content
644 644
                 $html .= $this->ticketSelectorEndDiv();
645 645
             }
646
-            if (! $this->iframe && ! is_archive()) {
646
+            if ( ! $this->iframe && ! is_archive()) {
647 647
                 $html .= EEH_Template::powered_by_event_espresso('', '', array('utm_content' => 'ticket_selector'));
648 648
             }
649 649
         }
@@ -663,7 +663,7 @@  discard block
 block discarded – undo
663 663
      */
664 664
     public function displayRegisterNowButton()
665 665
     {
666
-        $btn_text     = apply_filters(
666
+        $btn_text = apply_filters(
667 667
             'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__btn_text',
668 668
             __('Register Now', 'event_espresso'),
669 669
             $this->event
@@ -671,14 +671,14 @@  discard block
 block discarded – undo
671 671
         $external_url = $this->event->external_url();
672 672
         $html         = EEH_HTML::div(
673 673
             '',
674
-            'ticket-selector-submit-' . $this->event->ID() . '-btn-wrap',
674
+            'ticket-selector-submit-'.$this->event->ID().'-btn-wrap',
675 675
             'ticket-selector-submit-btn-wrap'
676 676
         );
677
-        $html         .= '<input id="ticket-selector-submit-' . $this->event->ID() . '-btn"';
677
+        $html         .= '<input id="ticket-selector-submit-'.$this->event->ID().'-btn"';
678 678
         $html         .= ' class="ticket-selector-submit-btn ';
679 679
         $html         .= empty($external_url) ? 'ticket-selector-submit-ajax"' : '"';
680
-        $html         .= ' type="submit" value="' . $btn_text . '" />';
681
-        $html         .= EEH_HTML::divx() . '<!-- .ticket-selector-submit-btn-wrap -->';
680
+        $html         .= ' type="submit" value="'.$btn_text.'" />';
681
+        $html         .= EEH_HTML::divx().'<!-- .ticket-selector-submit-btn-wrap -->';
682 682
         $html         .= apply_filters(
683 683
             'FHEE__EE_Ticket_Selector__after_ticket_selector_submit',
684 684
             '',
@@ -700,7 +700,7 @@  discard block
 block discarded – undo
700 700
      */
701 701
     public function displayViewDetailsButton($DWMTS = false)
702 702
     {
703
-        if (! $this->event->get_permalink()) {
703
+        if ( ! $this->event->get_permalink()) {
704 704
             EE_Error::add_error(
705 705
                 esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
706 706
                 __FILE__,
@@ -724,7 +724,7 @@  discard block
 block discarded – undo
724 724
             ? ' target="_blank"'
725 725
             : '';
726 726
         $view_details_btn .= '>';
727
-        $btn_text         = apply_filters(
727
+        $btn_text = apply_filters(
728 728
             'FHEE__EE_Ticket_Selector__display_view_details_btn__btn_text',
729 729
             esc_html__('View Details', 'event_espresso'),
730 730
             $this->event
@@ -754,7 +754,7 @@  discard block
 block discarded – undo
754 754
      */
755 755
     public function ticketSelectorEndDiv()
756 756
     {
757
-        return $this->clearTicketSelector() . '</div><!-- ticketSelectorEndDiv -->';
757
+        return $this->clearTicketSelector().'</div><!-- ticketSelectorEndDiv -->';
758 758
     }
759 759
 
760 760
 
Please login to merge, or discard this patch.
Indentation   +740 added lines, -740 removed lines patch added patch discarded remove patch
@@ -36,749 +36,749 @@
 block discarded – undo
36 36
 class DisplayTicketSelector
37 37
 {
38 38
 
39
-    /**
40
-     * event that ticket selector is being generated for
41
-     *
42
-     * @access protected
43
-     * @var EE_Event $event
44
-     */
45
-    protected $event;
46
-
47
-    /**
48
-     * Used to flag when the ticket selector is being called from an external iframe.
49
-     *
50
-     * @var bool $iframe
51
-     */
52
-    protected $iframe = false;
53
-
54
-    /**
55
-     * max attendees that can register for event at one time
56
-     *
57
-     * @var int $max_attendees
58
-     */
59
-    private $max_attendees = EE_INF;
60
-
61
-    /**
62
-     * @var string $date_format
63
-     */
64
-    private $date_format;
65
-
66
-    /**
67
-     * @var string $time_format
68
-     */
69
-    private $time_format;
70
-
71
-    /**
72
-     * @var boolean $display_full_ui
73
-     */
74
-    private $display_full_ui;
75
-
76
-
77
-
78
-    /**
79
-     * DisplayTicketSelector constructor.
80
-     *
81
-     * @param bool $iframe
82
-     */
83
-    public function __construct($iframe = false)
84
-    {
85
-        $this->iframe      = $iframe;
86
-        $this->date_format = apply_filters(
87
-            'FHEE__EED_Ticket_Selector__display_ticket_selector__date_format',
88
-            get_option('date_format')
89
-        );
90
-        $this->time_format = apply_filters(
91
-            'FHEE__EED_Ticket_Selector__display_ticket_selector__time_format',
92
-            get_option('time_format')
93
-        );
94
-    }
95
-
96
-
97
-    /**
98
-     * @return bool
99
-     */
100
-    public function isIframe()
101
-    {
102
-        return $this->iframe;
103
-    }
104
-
105
-
106
-
107
-    /**
108
-     * @param boolean $iframe
109
-     */
110
-    public function setIframe($iframe = true)
111
-    {
112
-        $this->iframe = filter_var($iframe, FILTER_VALIDATE_BOOLEAN);
113
-    }
114
-
115
-
116
-    /**
117
-     * finds and sets the \EE_Event object for use throughout class
118
-     *
119
-     * @param mixed $event
120
-     * @return bool
121
-     * @throws EE_Error
122
-     * @throws InvalidDataTypeException
123
-     * @throws InvalidInterfaceException
124
-     * @throws InvalidArgumentException
125
-     */
126
-    protected function setEvent($event = null)
127
-    {
128
-        if ($event === null) {
129
-            global $post;
130
-            $event = $post;
131
-        }
132
-        if ($event instanceof EE_Event) {
133
-            $this->event = $event;
134
-        } elseif ($event instanceof WP_Post) {
135
-            if (isset($event->EE_Event) && $event->EE_Event instanceof EE_Event) {
136
-                $this->event = $event->EE_Event;
137
-            } elseif ($event->post_type === 'espresso_events') {
138
-                $event->EE_Event = EEM_Event::instance()->instantiate_class_from_post_object($event);
139
-                $this->event     = $event->EE_Event;
140
-            }
141
-        } else {
142
-            $user_msg = __('No Event object or an invalid Event object was supplied.', 'event_espresso');
143
-            $dev_msg  = $user_msg . __(
144
-                    'In order to generate a ticket selector, please ensure you are passing either an EE_Event object or a WP_Post object of the post type "espresso_event" to the EE_Ticket_Selector class constructor.',
145
-                    'event_espresso'
146
-                );
147
-            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
148
-            return false;
149
-        }
150
-        return true;
151
-    }
152
-
153
-
154
-
155
-    /**
156
-     * @return int
157
-     */
158
-    public function getMaxAttendees()
159
-    {
160
-        return $this->max_attendees;
161
-    }
162
-
163
-
164
-
165
-    /**
166
-     * @param int $max_attendees
167
-     */
168
-    public function setMaxAttendees($max_attendees)
169
-    {
170
-        $this->max_attendees = absint(
171
-            apply_filters(
172
-                'FHEE__EE_Ticket_Selector__display_ticket_selector__max_tickets',
173
-                $max_attendees
174
-            )
175
-        );
176
-    }
177
-
178
-
179
-
180
-    /**
181
-     * Returns whether or not the full ticket selector should be shown or not.
182
-     * Currently, it displays on the frontend (including ajax requests) but not the backend
183
-     *
184
-     * @return bool
185
-     */
186
-    private function display_full_ui()
187
-    {
188
-        if ($this->display_full_ui === null) {
189
-            $this->display_full_ui = ! is_admin() || (defined('DOING_AJAX') && DOING_AJAX);
190
-        }
191
-        return $this->display_full_ui;
192
-    }
193
-
194
-
195
-    /**
196
-     * creates buttons for selecting number of attendees for an event
197
-     *
198
-     * @param WP_Post|int $event
199
-     * @param bool        $view_details
200
-     * @return string
201
-     * @throws EE_Error
202
-     * @throws InvalidArgumentException
203
-     * @throws InvalidDataTypeException
204
-     * @throws InvalidInterfaceException
205
-     */
206
-    public function display($event = null, $view_details = false)
207
-    {
208
-        // reset filter for displaying submit button
209
-        remove_filter('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', '__return_true');
210
-        // poke and prod incoming event till it tells us what it is
211
-        if (! $this->setEvent($event)) {
212
-            return false;
213
-        }
214
-        // begin gathering template arguments by getting event status
215
-        $template_args = array('event_status' => $this->event->get_active_status());
216
-        if (
217
-        $this->activeEventAndShowTicketSelector(
218
-            $event,
219
-            $template_args['event_status'],
220
-            $view_details
221
-        )
222
-        ) {
223
-            return ! is_single() ? $this->displayViewDetailsButton() : '';
224
-        }
225
-        // filter the maximum qty that can appear in the Ticket Selector qty dropdowns
226
-        $this->setMaxAttendees($this->event->additional_limit());
227
-        if ($this->getMaxAttendees() < 1) {
228
-            return $this->ticketSalesClosedMessage();
229
-        }
230
-        // is the event expired ?
231
-        $template_args['event_is_expired'] = $this->event->is_expired();
232
-        if ($template_args['event_is_expired']) {
233
-            return $this->expiredEventMessage();
234
-        }
235
-        // get all tickets for this event ordered by the datetime
236
-        $tickets = $this->getTickets();
237
-        if (count($tickets) < 1) {
238
-            return $this->noTicketAvailableMessage();
239
-        }
240
-        // redirecting to another site for registration ??
241
-        $external_url = (string)$this->event->external_url();
242
-        // if redirecting to another site for registration, then we don't load the TS
243
-        $ticket_selector = $external_url
244
-            ? $this->externalEventRegistration()
245
-            : $this->loadTicketSelector($tickets, $template_args);
246
-        // now set up the form (but not for the admin)
247
-        $ticket_selector = $this->display_full_ui()
248
-            ? $this->formOpen($this->event->ID(), $external_url) . $ticket_selector
249
-            : $ticket_selector;
250
-        // submit button and form close tag
251
-        $ticket_selector .= $this->display_full_ui() ? $this->displaySubmitButton($external_url) : '';
252
-        return $ticket_selector;
253
-    }
254
-
255
-
256
-
257
-    /**
258
-     * displayTicketSelector
259
-     * examines the event properties and determines whether a Ticket Selector should be displayed
260
-     *
261
-     * @param WP_Post|int $event
262
-     * @param string      $_event_active_status
263
-     * @param bool        $view_details
264
-     * @return bool
265
-     * @throws EE_Error
266
-     */
267
-    protected function activeEventAndShowTicketSelector($event, $_event_active_status, $view_details)
268
-    {
269
-        $event_post = $this->event instanceof EE_Event ? $this->event->ID() : $event;
270
-        return $this->display_full_ui()
271
-               && (
272
-                   ! $this->event->display_ticket_selector()
273
-                   || $view_details
274
-                   || post_password_required($event_post)
275
-                   || (
276
-                       $_event_active_status !== EE_Datetime::active
277
-                       && $_event_active_status !== EE_Datetime::upcoming
278
-                       && $_event_active_status !== EE_Datetime::sold_out
279
-                       && ! (
280
-                           $_event_active_status === EE_Datetime::inactive
281
-                           && is_user_logged_in()
282
-                       )
283
-                   )
284
-               );
285
-    }
286
-
287
-
288
-
289
-    /**
290
-     * noTicketAvailableMessage
291
-     * notice displayed if event is expired
292
-     *
293
-     * @return string
294
-     * @throws EE_Error
295
-     */
296
-    protected function expiredEventMessage()
297
-    {
298
-        return '<div class="ee-event-expired-notice"><span class="important-notice">' . esc_html__(
299
-                'We\'re sorry, but all tickets sales have ended because the event is expired.',
300
-                'event_espresso'
301
-            ) . '</span></div><!-- .ee-event-expired-notice -->';
302
-    }
303
-
304
-
305
-
306
-    /**
307
-     * noTicketAvailableMessage
308
-     * notice displayed if event has no more tickets available
309
-     *
310
-     * @return string
311
-     * @throws EE_Error
312
-     */
313
-    protected function noTicketAvailableMessage()
314
-    {
315
-        $no_ticket_available_msg = esc_html__('We\'re sorry, but all ticket sales have ended.', 'event_espresso');
316
-        if (current_user_can('edit_post', $this->event->ID())) {
317
-            $no_ticket_available_msg .= sprintf(
318
-                esc_html__(
319
-                    '%1$sNote to Event Admin:%2$sNo tickets were found for this event. This effectively turns off ticket sales. Please ensure that at least one ticket is available for if you want people to be able to register.%3$s(click to edit this event)%4$s',
320
-                    'event_espresso'
321
-                ),
322
-                '<div class="ee-attention" style="text-align: left;"><b>',
323
-                '</b><br />',
324
-                '<span class="edit-link"><a class="post-edit-link" href="'
325
-                . get_edit_post_link($this->event->ID())
326
-                . '">',
327
-                '</a></span></div><!-- .ee-attention noTicketAvailableMessage -->'
328
-            );
329
-        }
330
-        return '
39
+	/**
40
+	 * event that ticket selector is being generated for
41
+	 *
42
+	 * @access protected
43
+	 * @var EE_Event $event
44
+	 */
45
+	protected $event;
46
+
47
+	/**
48
+	 * Used to flag when the ticket selector is being called from an external iframe.
49
+	 *
50
+	 * @var bool $iframe
51
+	 */
52
+	protected $iframe = false;
53
+
54
+	/**
55
+	 * max attendees that can register for event at one time
56
+	 *
57
+	 * @var int $max_attendees
58
+	 */
59
+	private $max_attendees = EE_INF;
60
+
61
+	/**
62
+	 * @var string $date_format
63
+	 */
64
+	private $date_format;
65
+
66
+	/**
67
+	 * @var string $time_format
68
+	 */
69
+	private $time_format;
70
+
71
+	/**
72
+	 * @var boolean $display_full_ui
73
+	 */
74
+	private $display_full_ui;
75
+
76
+
77
+
78
+	/**
79
+	 * DisplayTicketSelector constructor.
80
+	 *
81
+	 * @param bool $iframe
82
+	 */
83
+	public function __construct($iframe = false)
84
+	{
85
+		$this->iframe      = $iframe;
86
+		$this->date_format = apply_filters(
87
+			'FHEE__EED_Ticket_Selector__display_ticket_selector__date_format',
88
+			get_option('date_format')
89
+		);
90
+		$this->time_format = apply_filters(
91
+			'FHEE__EED_Ticket_Selector__display_ticket_selector__time_format',
92
+			get_option('time_format')
93
+		);
94
+	}
95
+
96
+
97
+	/**
98
+	 * @return bool
99
+	 */
100
+	public function isIframe()
101
+	{
102
+		return $this->iframe;
103
+	}
104
+
105
+
106
+
107
+	/**
108
+	 * @param boolean $iframe
109
+	 */
110
+	public function setIframe($iframe = true)
111
+	{
112
+		$this->iframe = filter_var($iframe, FILTER_VALIDATE_BOOLEAN);
113
+	}
114
+
115
+
116
+	/**
117
+	 * finds and sets the \EE_Event object for use throughout class
118
+	 *
119
+	 * @param mixed $event
120
+	 * @return bool
121
+	 * @throws EE_Error
122
+	 * @throws InvalidDataTypeException
123
+	 * @throws InvalidInterfaceException
124
+	 * @throws InvalidArgumentException
125
+	 */
126
+	protected function setEvent($event = null)
127
+	{
128
+		if ($event === null) {
129
+			global $post;
130
+			$event = $post;
131
+		}
132
+		if ($event instanceof EE_Event) {
133
+			$this->event = $event;
134
+		} elseif ($event instanceof WP_Post) {
135
+			if (isset($event->EE_Event) && $event->EE_Event instanceof EE_Event) {
136
+				$this->event = $event->EE_Event;
137
+			} elseif ($event->post_type === 'espresso_events') {
138
+				$event->EE_Event = EEM_Event::instance()->instantiate_class_from_post_object($event);
139
+				$this->event     = $event->EE_Event;
140
+			}
141
+		} else {
142
+			$user_msg = __('No Event object or an invalid Event object was supplied.', 'event_espresso');
143
+			$dev_msg  = $user_msg . __(
144
+					'In order to generate a ticket selector, please ensure you are passing either an EE_Event object or a WP_Post object of the post type "espresso_event" to the EE_Ticket_Selector class constructor.',
145
+					'event_espresso'
146
+				);
147
+			EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
148
+			return false;
149
+		}
150
+		return true;
151
+	}
152
+
153
+
154
+
155
+	/**
156
+	 * @return int
157
+	 */
158
+	public function getMaxAttendees()
159
+	{
160
+		return $this->max_attendees;
161
+	}
162
+
163
+
164
+
165
+	/**
166
+	 * @param int $max_attendees
167
+	 */
168
+	public function setMaxAttendees($max_attendees)
169
+	{
170
+		$this->max_attendees = absint(
171
+			apply_filters(
172
+				'FHEE__EE_Ticket_Selector__display_ticket_selector__max_tickets',
173
+				$max_attendees
174
+			)
175
+		);
176
+	}
177
+
178
+
179
+
180
+	/**
181
+	 * Returns whether or not the full ticket selector should be shown or not.
182
+	 * Currently, it displays on the frontend (including ajax requests) but not the backend
183
+	 *
184
+	 * @return bool
185
+	 */
186
+	private function display_full_ui()
187
+	{
188
+		if ($this->display_full_ui === null) {
189
+			$this->display_full_ui = ! is_admin() || (defined('DOING_AJAX') && DOING_AJAX);
190
+		}
191
+		return $this->display_full_ui;
192
+	}
193
+
194
+
195
+	/**
196
+	 * creates buttons for selecting number of attendees for an event
197
+	 *
198
+	 * @param WP_Post|int $event
199
+	 * @param bool        $view_details
200
+	 * @return string
201
+	 * @throws EE_Error
202
+	 * @throws InvalidArgumentException
203
+	 * @throws InvalidDataTypeException
204
+	 * @throws InvalidInterfaceException
205
+	 */
206
+	public function display($event = null, $view_details = false)
207
+	{
208
+		// reset filter for displaying submit button
209
+		remove_filter('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', '__return_true');
210
+		// poke and prod incoming event till it tells us what it is
211
+		if (! $this->setEvent($event)) {
212
+			return false;
213
+		}
214
+		// begin gathering template arguments by getting event status
215
+		$template_args = array('event_status' => $this->event->get_active_status());
216
+		if (
217
+		$this->activeEventAndShowTicketSelector(
218
+			$event,
219
+			$template_args['event_status'],
220
+			$view_details
221
+		)
222
+		) {
223
+			return ! is_single() ? $this->displayViewDetailsButton() : '';
224
+		}
225
+		// filter the maximum qty that can appear in the Ticket Selector qty dropdowns
226
+		$this->setMaxAttendees($this->event->additional_limit());
227
+		if ($this->getMaxAttendees() < 1) {
228
+			return $this->ticketSalesClosedMessage();
229
+		}
230
+		// is the event expired ?
231
+		$template_args['event_is_expired'] = $this->event->is_expired();
232
+		if ($template_args['event_is_expired']) {
233
+			return $this->expiredEventMessage();
234
+		}
235
+		// get all tickets for this event ordered by the datetime
236
+		$tickets = $this->getTickets();
237
+		if (count($tickets) < 1) {
238
+			return $this->noTicketAvailableMessage();
239
+		}
240
+		// redirecting to another site for registration ??
241
+		$external_url = (string)$this->event->external_url();
242
+		// if redirecting to another site for registration, then we don't load the TS
243
+		$ticket_selector = $external_url
244
+			? $this->externalEventRegistration()
245
+			: $this->loadTicketSelector($tickets, $template_args);
246
+		// now set up the form (but not for the admin)
247
+		$ticket_selector = $this->display_full_ui()
248
+			? $this->formOpen($this->event->ID(), $external_url) . $ticket_selector
249
+			: $ticket_selector;
250
+		// submit button and form close tag
251
+		$ticket_selector .= $this->display_full_ui() ? $this->displaySubmitButton($external_url) : '';
252
+		return $ticket_selector;
253
+	}
254
+
255
+
256
+
257
+	/**
258
+	 * displayTicketSelector
259
+	 * examines the event properties and determines whether a Ticket Selector should be displayed
260
+	 *
261
+	 * @param WP_Post|int $event
262
+	 * @param string      $_event_active_status
263
+	 * @param bool        $view_details
264
+	 * @return bool
265
+	 * @throws EE_Error
266
+	 */
267
+	protected function activeEventAndShowTicketSelector($event, $_event_active_status, $view_details)
268
+	{
269
+		$event_post = $this->event instanceof EE_Event ? $this->event->ID() : $event;
270
+		return $this->display_full_ui()
271
+			   && (
272
+				   ! $this->event->display_ticket_selector()
273
+				   || $view_details
274
+				   || post_password_required($event_post)
275
+				   || (
276
+					   $_event_active_status !== EE_Datetime::active
277
+					   && $_event_active_status !== EE_Datetime::upcoming
278
+					   && $_event_active_status !== EE_Datetime::sold_out
279
+					   && ! (
280
+						   $_event_active_status === EE_Datetime::inactive
281
+						   && is_user_logged_in()
282
+					   )
283
+				   )
284
+			   );
285
+	}
286
+
287
+
288
+
289
+	/**
290
+	 * noTicketAvailableMessage
291
+	 * notice displayed if event is expired
292
+	 *
293
+	 * @return string
294
+	 * @throws EE_Error
295
+	 */
296
+	protected function expiredEventMessage()
297
+	{
298
+		return '<div class="ee-event-expired-notice"><span class="important-notice">' . esc_html__(
299
+				'We\'re sorry, but all tickets sales have ended because the event is expired.',
300
+				'event_espresso'
301
+			) . '</span></div><!-- .ee-event-expired-notice -->';
302
+	}
303
+
304
+
305
+
306
+	/**
307
+	 * noTicketAvailableMessage
308
+	 * notice displayed if event has no more tickets available
309
+	 *
310
+	 * @return string
311
+	 * @throws EE_Error
312
+	 */
313
+	protected function noTicketAvailableMessage()
314
+	{
315
+		$no_ticket_available_msg = esc_html__('We\'re sorry, but all ticket sales have ended.', 'event_espresso');
316
+		if (current_user_can('edit_post', $this->event->ID())) {
317
+			$no_ticket_available_msg .= sprintf(
318
+				esc_html__(
319
+					'%1$sNote to Event Admin:%2$sNo tickets were found for this event. This effectively turns off ticket sales. Please ensure that at least one ticket is available for if you want people to be able to register.%3$s(click to edit this event)%4$s',
320
+					'event_espresso'
321
+				),
322
+				'<div class="ee-attention" style="text-align: left;"><b>',
323
+				'</b><br />',
324
+				'<span class="edit-link"><a class="post-edit-link" href="'
325
+				. get_edit_post_link($this->event->ID())
326
+				. '">',
327
+				'</a></span></div><!-- .ee-attention noTicketAvailableMessage -->'
328
+			);
329
+		}
330
+		return '
331 331
             <div class="ee-event-expired-notice">
332 332
                 <span class="important-notice">' . $no_ticket_available_msg . '</span>
333 333
             </div><!-- .ee-event-expired-notice -->';
334
-    }
335
-
336
-
337
-
338
-    /**
339
-     * ticketSalesClosed
340
-     * notice displayed if event ticket sales are turned off
341
-     *
342
-     * @return string
343
-     * @throws EE_Error
344
-     */
345
-    protected function ticketSalesClosedMessage()
346
-    {
347
-        $sales_closed_msg = esc_html__(
348
-            'We\'re sorry, but ticket sales have been closed at this time. Please check back again later.',
349
-            'event_espresso'
350
-        );
351
-        if (current_user_can('edit_post', $this->event->ID())) {
352
-            $sales_closed_msg .= sprintf(
353
-                esc_html__(
354
-                    '%sNote to Event Admin:%sThe "Maximum number of tickets allowed per order for this event" in the Event Registration Options has been set to "0". This effectively turns off ticket sales. %s(click to edit this event)%s',
355
-                    'event_espresso'
356
-                ),
357
-                '<div class="ee-attention" style="text-align: left;"><b>',
358
-                '</b><br />',
359
-                '<span class="edit-link"><a class="post-edit-link" href="'
360
-                . get_edit_post_link($this->event->ID())
361
-                . '">',
362
-                '</a></span></div><!-- .ee-attention ticketSalesClosedMessage -->'
363
-            );
364
-        }
365
-        return '<p><span class="important-notice">' . $sales_closed_msg . '</span></p>';
366
-    }
367
-
368
-
369
-
370
-    /**
371
-     * getTickets
372
-     *
373
-     * @return \EE_Base_Class[]|\EE_Ticket[]
374
-     * @throws EE_Error
375
-     * @throws InvalidDataTypeException
376
-     * @throws InvalidInterfaceException
377
-     * @throws InvalidArgumentException
378
-     */
379
-    protected function getTickets()
380
-    {
381
-        $ticket_query_args = array(
382
-            array('Datetime.EVT_ID' => $this->event->ID()),
383
-            'order_by' => array(
384
-                'TKT_order'              => 'ASC',
385
-                'TKT_required'           => 'DESC',
386
-                'TKT_start_date'         => 'ASC',
387
-                'TKT_end_date'           => 'ASC',
388
-                'Datetime.DTT_EVT_start' => 'DESC',
389
-            ),
390
-        );
391
-        if (
392
-        ! (
393
-            EE_Registry::instance()->CFG->template_settings->EED_Ticket_Selector instanceof EE_Ticket_Selector_Config
394
-            && EE_Registry::instance()->CFG->template_settings->EED_Ticket_Selector->show_expired_tickets
395
-        )
396
-        ) {
397
-            //use the correct applicable time query depending on what version of core is being run.
398
-            $current_time                         = method_exists('EEM_Datetime', 'current_time_for_query')
399
-                ? time()
400
-                : current_time('timestamp');
401
-            $ticket_query_args[0]['TKT_end_date'] = array('>', $current_time);
402
-        }
403
-        return EEM_Ticket::instance()->get_all($ticket_query_args);
404
-    }
405
-
406
-
407
-
408
-    /**
409
-     * loadTicketSelector
410
-     * begins to assemble template arguments
411
-     * and decides whether to load a "simple" ticket selector, or the standard
412
-     *
413
-     * @param \EE_Ticket[] $tickets
414
-     * @param array        $template_args
415
-     * @return string
416
-     * @throws EE_Error
417
-     */
418
-    protected function loadTicketSelector(array $tickets, array $template_args)
419
-    {
420
-        $template_args['event']            = $this->event;
421
-        $template_args['EVT_ID']           = $this->event->ID();
422
-        $template_args['event_is_expired'] = $this->event->is_expired();
423
-        $template_args['max_atndz']        = $this->getMaxAttendees();
424
-        $template_args['date_format']      = $this->date_format;
425
-        $template_args['time_format']      = $this->time_format;
426
-        /**
427
-         * Filters the anchor ID used when redirecting to the Ticket Selector if no quantity selected
428
-         *
429
-         * @since 4.9.13
430
-         * @param     string  '#tkt-slctr-tbl-' . $EVT_ID The html ID to anchor to
431
-         * @param int $EVT_ID The Event ID
432
-         */
433
-        $template_args['anchor_id']    = apply_filters(
434
-            'FHEE__EE_Ticket_Selector__redirect_anchor_id',
435
-            '#tkt-slctr-tbl-' . $this->event->ID(),
436
-            $this->event->ID()
437
-        );
438
-        $template_args['tickets']      = $tickets;
439
-        $template_args['ticket_count'] = count($tickets);
440
-        $ticket_selector               = $this->simpleTicketSelector($tickets, $template_args);
441
-        return $ticket_selector instanceof TicketSelectorSimple
442
-            ? $ticket_selector
443
-            : new TicketSelectorStandard(
444
-                $this->event,
445
-                $tickets,
446
-                $this->getMaxAttendees(),
447
-                $template_args,
448
-                $this->date_format,
449
-                $this->time_format
450
-            );
451
-    }
452
-
453
-
454
-
455
-    /**
456
-     * simpleTicketSelector
457
-     * there's one ticket, and max attendees is set to one,
458
-     * so if the event is free, then this is a "simple" ticket selector
459
-     * a.k.a. "Dude Where's my Ticket Selector?"
460
-     *
461
-     * @param \EE_Ticket[] $tickets
462
-     * @param array        $template_args
463
-     * @return string
464
-     * @throws EE_Error
465
-     */
466
-    protected function simpleTicketSelector($tickets, array $template_args)
467
-    {
468
-        // if there is only ONE ticket with a max qty of ONE
469
-        if (count($tickets) > 1 || $this->getMaxAttendees() !== 1) {
470
-            return '';
471
-        }
472
-        /** @var \EE_Ticket $ticket */
473
-        $ticket = reset($tickets);
474
-        // if the ticket is free... then not much need for the ticket selector
475
-        if (
476
-        apply_filters(
477
-            'FHEE__ticket_selector_chart_template__hide_ticket_selector',
478
-            $ticket->is_free(),
479
-            $this->event->ID()
480
-        )
481
-        ) {
482
-            return new TicketSelectorSimple(
483
-                $this->event,
484
-                $ticket,
485
-                $this->getMaxAttendees(),
486
-                $template_args
487
-            );
488
-        }
489
-        return '';
490
-    }
491
-
492
-
493
-
494
-    /**
495
-     * externalEventRegistration
496
-     *
497
-     * @return string
498
-     */
499
-    public function externalEventRegistration()
500
-    {
501
-        // if not we still need to trigger the display of the submit button
502
-        add_filter('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', '__return_true');
503
-        //display notice to admin that registration is external
504
-        return $this->display_full_ui()
505
-            ? esc_html__(
506
-                'Registration is at an external URL for this event.',
507
-                'event_espresso'
508
-            )
509
-            : '';
510
-    }
511
-
512
-
513
-
514
-    /**
515
-     * formOpen
516
-     *
517
-     * @param        int    $ID
518
-     * @param        string $external_url
519
-     * @return        string
520
-     */
521
-    public function formOpen($ID = 0, $external_url = '')
522
-    {
523
-        // if redirecting, we don't need any anything else
524
-        if ($external_url) {
525
-            $html = '<form method="GET" ';
526
-            $html .= 'action="' . EEH_URL::refactor_url($external_url) . '" ';
527
-            $html .= 'name="ticket-selector-form-' . $ID . '"';
528
-            // open link in new window ?
529
-            $html .= apply_filters(
530
-                'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__formOpen__external_url_target_blank',
531
-                $this->isIframe(),
532
-                $this
533
-            )
534
-                ? ' target="_blank"'
535
-                : '';
536
-            $html .= '>';
537
-            $query_args = EEH_URL::get_query_string($external_url);
538
-            foreach ((array) $query_args as $query_arg => $value) {
539
-                $html .= '<input type="hidden" name="' . $query_arg . '" value="' . $value . '">';
540
-            }
541
-            return $html;
542
-        }
543
-        // if there is no submit button, then don't start building a form
544
-        // because the "View Details" button will build its own form
545
-        if (! apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
546
-            return '';
547
-        }
548
-        $checkout_url = EEH_Event_View::event_link_url($ID);
549
-        if (! $checkout_url) {
550
-            EE_Error::add_error(
551
-                esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
552
-                __FILE__,
553
-                __FUNCTION__,
554
-                __LINE__
555
-            );
556
-        }
557
-        // set no cache headers and constants
558
-        EE_System::do_not_cache();
559
-        $html = '<form method="POST" ';
560
-        $html .= 'action="' . $checkout_url . '" ';
561
-        $html .= 'name="ticket-selector-form-' . $ID . '"';
562
-        $html .= $this->iframe ? ' target="_blank"' : '';
563
-        $html .= '>';
564
-        $html .= '<input type="hidden" name="ee" value="process_ticket_selections">';
565
-        $html = apply_filters('FHEE__EE_Ticket_Selector__ticket_selector_form_open__html', $html, $this->event);
566
-        return $html;
567
-    }
568
-
569
-
570
-
571
-    /**
572
-     * displaySubmitButton
573
-     *
574
-     * @param  string $external_url
575
-     * @return string
576
-     * @throws EE_Error
577
-     */
578
-    public function displaySubmitButton($external_url = '')
579
-    {
580
-        $html = '';
581
-        if ($this->display_full_ui()) {
582
-            // standard TS displayed with submit button, ie: "Register Now"
583
-            if (apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
584
-                $html .= $this->displayRegisterNowButton();
585
-                $html .= empty($external_url)
586
-                    ? $this->ticketSelectorEndDiv()
587
-                    : $this->clearTicketSelector();
588
-                $html .= '<br/>' . $this->formClose();
589
-            } elseif ($this->getMaxAttendees() === 1) {
590
-                // its a "Dude Where's my Ticket Selector?" (DWMTS) type event (ie: $_max_atndz === 1)
591
-                if ($this->event->is_sold_out()) {
592
-                    // then instead of a View Details or Submit button, just display a "Sold Out" message
593
-                    $html .= apply_filters(
594
-                        'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__sold_out_msg',
595
-                        sprintf(
596
-                            __(
597
-                                '%1$s"%2$s" is currently sold out.%4$sPlease check back again later, as spots may become available.%3$s',
598
-                                'event_espresso'
599
-                            ),
600
-                            '<p class="no-ticket-selector-msg clear-float">',
601
-                            $this->event->name(),
602
-                            '</p>',
603
-                            '<br />'
604
-                        ),
605
-                        $this->event
606
-                    );
607
-                    if (
608
-                    apply_filters(
609
-                        'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__no_tickets_but_display_register_now_button',
610
-                        false,
611
-                        $this->event
612
-                    )
613
-                    ) {
614
-                        $html .= $this->displayRegisterNowButton();
615
-                    }
616
-                    // sold out DWMTS event, no TS, no submit or view details button, but has additional content
617
-                    $html .= $this->ticketSelectorEndDiv();
618
-                } elseif (
619
-                    apply_filters('FHEE__EE_Ticket_Selector__hide_ticket_selector', false)
620
-                    && ! is_single()
621
-                ) {
622
-                    // this is a "Dude Where's my Ticket Selector?" (DWMTS) type event,
623
-                    // but no tickets are available, so display event's "View Details" button.
624
-                    // it is being viewed via somewhere other than a single post
625
-                    $html .= $this->displayViewDetailsButton(true);
626
-                } else {
627
-                    $html .= $this->ticketSelectorEndDiv();
628
-                }
629
-            } elseif (is_archive()) {
630
-                // event list, no tickets available so display event's "View Details" button
631
-                $html .= $this->ticketSelectorEndDiv();
632
-                $html .= $this->displayViewDetailsButton();
633
-            } else {
634
-                if (
635
-                apply_filters(
636
-                    'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__no_tickets_but_display_register_now_button',
637
-                    false,
638
-                    $this->event
639
-                )
640
-                ) {
641
-                    $html .= $this->displayRegisterNowButton();
642
-                }
643
-                // no submit or view details button, and no additional content
644
-                $html .= $this->ticketSelectorEndDiv();
645
-            }
646
-            if (! $this->iframe && ! is_archive()) {
647
-                $html .= EEH_Template::powered_by_event_espresso('', '', array('utm_content' => 'ticket_selector'));
648
-            }
649
-        }
650
-        return apply_filters(
651
-            'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__displaySubmitButton__html',
652
-            $html,
653
-            $this->event,
654
-            $this
655
-        );
656
-    }
657
-
658
-
659
-
660
-    /**
661
-     * @return string
662
-     * @throws EE_Error
663
-     */
664
-    public function displayRegisterNowButton()
665
-    {
666
-        $btn_text     = apply_filters(
667
-            'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__btn_text',
668
-            __('Register Now', 'event_espresso'),
669
-            $this->event
670
-        );
671
-        $external_url = $this->event->external_url();
672
-        $html         = EEH_HTML::div(
673
-            '',
674
-            'ticket-selector-submit-' . $this->event->ID() . '-btn-wrap',
675
-            'ticket-selector-submit-btn-wrap'
676
-        );
677
-        $html         .= '<input id="ticket-selector-submit-' . $this->event->ID() . '-btn"';
678
-        $html         .= ' class="ticket-selector-submit-btn ';
679
-        $html         .= empty($external_url) ? 'ticket-selector-submit-ajax"' : '"';
680
-        $html         .= ' type="submit" value="' . $btn_text . '" />';
681
-        $html         .= EEH_HTML::divx() . '<!-- .ticket-selector-submit-btn-wrap -->';
682
-        $html         .= apply_filters(
683
-            'FHEE__EE_Ticket_Selector__after_ticket_selector_submit',
684
-            '',
685
-            $this->event,
686
-            $this->iframe
687
-        );
688
-        return $html;
689
-    }
690
-
691
-
692
-    /**
693
-     * displayViewDetailsButton
694
-     *
695
-     * @param bool $DWMTS indicates a "Dude Where's my Ticket Selector?" (DWMTS) type event
696
-     *                    (ie: $_max_atndz === 1) where there are no available tickets,
697
-     *                    either because they are sold out, expired, or not yet on sale.
698
-     *                    In this case, we need to close the form BEFORE adding any closing divs
699
-     * @return string
700
-     * @throws EE_Error
701
-     */
702
-    public function displayViewDetailsButton($DWMTS = false)
703
-    {
704
-        if (! $this->event->get_permalink()) {
705
-            EE_Error::add_error(
706
-                esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
707
-                __FILE__,
708
-                __FUNCTION__,
709
-                __LINE__
710
-            );
711
-        }
712
-        $view_details_btn = '<form method="POST" action="';
713
-        $view_details_btn .= apply_filters(
714
-            'FHEE__EE_Ticket_Selector__display_view_details_btn__btn_url',
715
-            $this->event->get_permalink(),
716
-            $this->event
717
-        );
718
-        $view_details_btn .= '"';
719
-        // open link in new window ?
720
-        $view_details_btn .= apply_filters(
721
-            'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__displayViewDetailsButton__url_target_blank',
722
-            $this->isIframe(),
723
-            $this
724
-        )
725
-            ? ' target="_blank"'
726
-            : '';
727
-        $view_details_btn .= '>';
728
-        $btn_text         = apply_filters(
729
-            'FHEE__EE_Ticket_Selector__display_view_details_btn__btn_text',
730
-            esc_html__('View Details', 'event_espresso'),
731
-            $this->event
732
-        );
733
-        $view_details_btn .= '<input id="ticket-selector-submit-'
734
-                             . $this->event->ID()
735
-                             . '-btn" class="ticket-selector-submit-btn view-details-btn" type="submit" value="'
736
-                             . $btn_text
737
-                             . '" />';
738
-        $view_details_btn .= apply_filters('FHEE__EE_Ticket_Selector__after_view_details_btn', '', $this->event);
739
-        if ($DWMTS) {
740
-            $view_details_btn .= $this->formClose();
741
-            $view_details_btn .= $this->ticketSelectorEndDiv();
742
-            $view_details_btn .= '<br/>';
743
-        } else {
744
-            $view_details_btn .= $this->clearTicketSelector();
745
-            $view_details_btn .= '<br/>';
746
-            $view_details_btn .= $this->formClose();
747
-        }
748
-        return $view_details_btn;
749
-    }
750
-
751
-
752
-
753
-    /**
754
-     * @return string
755
-     */
756
-    public function ticketSelectorEndDiv()
757
-    {
758
-        return $this->clearTicketSelector() . '</div><!-- ticketSelectorEndDiv -->';
759
-    }
760
-
761
-
762
-
763
-    /**
764
-     * @return string
765
-     */
766
-    public function clearTicketSelector()
767
-    {
768
-        // standard TS displayed, appears after a "Register Now" or "view Details" button
769
-        return '<div class="clear"></div><!-- clearTicketSelector -->';
770
-    }
771
-
772
-
773
-
774
-    /**
775
-     * @access        public
776
-     * @return        string
777
-     */
778
-    public function formClose()
779
-    {
780
-        return '</form>';
781
-    }
334
+	}
335
+
336
+
337
+
338
+	/**
339
+	 * ticketSalesClosed
340
+	 * notice displayed if event ticket sales are turned off
341
+	 *
342
+	 * @return string
343
+	 * @throws EE_Error
344
+	 */
345
+	protected function ticketSalesClosedMessage()
346
+	{
347
+		$sales_closed_msg = esc_html__(
348
+			'We\'re sorry, but ticket sales have been closed at this time. Please check back again later.',
349
+			'event_espresso'
350
+		);
351
+		if (current_user_can('edit_post', $this->event->ID())) {
352
+			$sales_closed_msg .= sprintf(
353
+				esc_html__(
354
+					'%sNote to Event Admin:%sThe "Maximum number of tickets allowed per order for this event" in the Event Registration Options has been set to "0". This effectively turns off ticket sales. %s(click to edit this event)%s',
355
+					'event_espresso'
356
+				),
357
+				'<div class="ee-attention" style="text-align: left;"><b>',
358
+				'</b><br />',
359
+				'<span class="edit-link"><a class="post-edit-link" href="'
360
+				. get_edit_post_link($this->event->ID())
361
+				. '">',
362
+				'</a></span></div><!-- .ee-attention ticketSalesClosedMessage -->'
363
+			);
364
+		}
365
+		return '<p><span class="important-notice">' . $sales_closed_msg . '</span></p>';
366
+	}
367
+
368
+
369
+
370
+	/**
371
+	 * getTickets
372
+	 *
373
+	 * @return \EE_Base_Class[]|\EE_Ticket[]
374
+	 * @throws EE_Error
375
+	 * @throws InvalidDataTypeException
376
+	 * @throws InvalidInterfaceException
377
+	 * @throws InvalidArgumentException
378
+	 */
379
+	protected function getTickets()
380
+	{
381
+		$ticket_query_args = array(
382
+			array('Datetime.EVT_ID' => $this->event->ID()),
383
+			'order_by' => array(
384
+				'TKT_order'              => 'ASC',
385
+				'TKT_required'           => 'DESC',
386
+				'TKT_start_date'         => 'ASC',
387
+				'TKT_end_date'           => 'ASC',
388
+				'Datetime.DTT_EVT_start' => 'DESC',
389
+			),
390
+		);
391
+		if (
392
+		! (
393
+			EE_Registry::instance()->CFG->template_settings->EED_Ticket_Selector instanceof EE_Ticket_Selector_Config
394
+			&& EE_Registry::instance()->CFG->template_settings->EED_Ticket_Selector->show_expired_tickets
395
+		)
396
+		) {
397
+			//use the correct applicable time query depending on what version of core is being run.
398
+			$current_time                         = method_exists('EEM_Datetime', 'current_time_for_query')
399
+				? time()
400
+				: current_time('timestamp');
401
+			$ticket_query_args[0]['TKT_end_date'] = array('>', $current_time);
402
+		}
403
+		return EEM_Ticket::instance()->get_all($ticket_query_args);
404
+	}
405
+
406
+
407
+
408
+	/**
409
+	 * loadTicketSelector
410
+	 * begins to assemble template arguments
411
+	 * and decides whether to load a "simple" ticket selector, or the standard
412
+	 *
413
+	 * @param \EE_Ticket[] $tickets
414
+	 * @param array        $template_args
415
+	 * @return string
416
+	 * @throws EE_Error
417
+	 */
418
+	protected function loadTicketSelector(array $tickets, array $template_args)
419
+	{
420
+		$template_args['event']            = $this->event;
421
+		$template_args['EVT_ID']           = $this->event->ID();
422
+		$template_args['event_is_expired'] = $this->event->is_expired();
423
+		$template_args['max_atndz']        = $this->getMaxAttendees();
424
+		$template_args['date_format']      = $this->date_format;
425
+		$template_args['time_format']      = $this->time_format;
426
+		/**
427
+		 * Filters the anchor ID used when redirecting to the Ticket Selector if no quantity selected
428
+		 *
429
+		 * @since 4.9.13
430
+		 * @param     string  '#tkt-slctr-tbl-' . $EVT_ID The html ID to anchor to
431
+		 * @param int $EVT_ID The Event ID
432
+		 */
433
+		$template_args['anchor_id']    = apply_filters(
434
+			'FHEE__EE_Ticket_Selector__redirect_anchor_id',
435
+			'#tkt-slctr-tbl-' . $this->event->ID(),
436
+			$this->event->ID()
437
+		);
438
+		$template_args['tickets']      = $tickets;
439
+		$template_args['ticket_count'] = count($tickets);
440
+		$ticket_selector               = $this->simpleTicketSelector($tickets, $template_args);
441
+		return $ticket_selector instanceof TicketSelectorSimple
442
+			? $ticket_selector
443
+			: new TicketSelectorStandard(
444
+				$this->event,
445
+				$tickets,
446
+				$this->getMaxAttendees(),
447
+				$template_args,
448
+				$this->date_format,
449
+				$this->time_format
450
+			);
451
+	}
452
+
453
+
454
+
455
+	/**
456
+	 * simpleTicketSelector
457
+	 * there's one ticket, and max attendees is set to one,
458
+	 * so if the event is free, then this is a "simple" ticket selector
459
+	 * a.k.a. "Dude Where's my Ticket Selector?"
460
+	 *
461
+	 * @param \EE_Ticket[] $tickets
462
+	 * @param array        $template_args
463
+	 * @return string
464
+	 * @throws EE_Error
465
+	 */
466
+	protected function simpleTicketSelector($tickets, array $template_args)
467
+	{
468
+		// if there is only ONE ticket with a max qty of ONE
469
+		if (count($tickets) > 1 || $this->getMaxAttendees() !== 1) {
470
+			return '';
471
+		}
472
+		/** @var \EE_Ticket $ticket */
473
+		$ticket = reset($tickets);
474
+		// if the ticket is free... then not much need for the ticket selector
475
+		if (
476
+		apply_filters(
477
+			'FHEE__ticket_selector_chart_template__hide_ticket_selector',
478
+			$ticket->is_free(),
479
+			$this->event->ID()
480
+		)
481
+		) {
482
+			return new TicketSelectorSimple(
483
+				$this->event,
484
+				$ticket,
485
+				$this->getMaxAttendees(),
486
+				$template_args
487
+			);
488
+		}
489
+		return '';
490
+	}
491
+
492
+
493
+
494
+	/**
495
+	 * externalEventRegistration
496
+	 *
497
+	 * @return string
498
+	 */
499
+	public function externalEventRegistration()
500
+	{
501
+		// if not we still need to trigger the display of the submit button
502
+		add_filter('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', '__return_true');
503
+		//display notice to admin that registration is external
504
+		return $this->display_full_ui()
505
+			? esc_html__(
506
+				'Registration is at an external URL for this event.',
507
+				'event_espresso'
508
+			)
509
+			: '';
510
+	}
511
+
512
+
513
+
514
+	/**
515
+	 * formOpen
516
+	 *
517
+	 * @param        int    $ID
518
+	 * @param        string $external_url
519
+	 * @return        string
520
+	 */
521
+	public function formOpen($ID = 0, $external_url = '')
522
+	{
523
+		// if redirecting, we don't need any anything else
524
+		if ($external_url) {
525
+			$html = '<form method="GET" ';
526
+			$html .= 'action="' . EEH_URL::refactor_url($external_url) . '" ';
527
+			$html .= 'name="ticket-selector-form-' . $ID . '"';
528
+			// open link in new window ?
529
+			$html .= apply_filters(
530
+				'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__formOpen__external_url_target_blank',
531
+				$this->isIframe(),
532
+				$this
533
+			)
534
+				? ' target="_blank"'
535
+				: '';
536
+			$html .= '>';
537
+			$query_args = EEH_URL::get_query_string($external_url);
538
+			foreach ((array) $query_args as $query_arg => $value) {
539
+				$html .= '<input type="hidden" name="' . $query_arg . '" value="' . $value . '">';
540
+			}
541
+			return $html;
542
+		}
543
+		// if there is no submit button, then don't start building a form
544
+		// because the "View Details" button will build its own form
545
+		if (! apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
546
+			return '';
547
+		}
548
+		$checkout_url = EEH_Event_View::event_link_url($ID);
549
+		if (! $checkout_url) {
550
+			EE_Error::add_error(
551
+				esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
552
+				__FILE__,
553
+				__FUNCTION__,
554
+				__LINE__
555
+			);
556
+		}
557
+		// set no cache headers and constants
558
+		EE_System::do_not_cache();
559
+		$html = '<form method="POST" ';
560
+		$html .= 'action="' . $checkout_url . '" ';
561
+		$html .= 'name="ticket-selector-form-' . $ID . '"';
562
+		$html .= $this->iframe ? ' target="_blank"' : '';
563
+		$html .= '>';
564
+		$html .= '<input type="hidden" name="ee" value="process_ticket_selections">';
565
+		$html = apply_filters('FHEE__EE_Ticket_Selector__ticket_selector_form_open__html', $html, $this->event);
566
+		return $html;
567
+	}
568
+
569
+
570
+
571
+	/**
572
+	 * displaySubmitButton
573
+	 *
574
+	 * @param  string $external_url
575
+	 * @return string
576
+	 * @throws EE_Error
577
+	 */
578
+	public function displaySubmitButton($external_url = '')
579
+	{
580
+		$html = '';
581
+		if ($this->display_full_ui()) {
582
+			// standard TS displayed with submit button, ie: "Register Now"
583
+			if (apply_filters('FHEE__EE_Ticket_Selector__display_ticket_selector_submit', false)) {
584
+				$html .= $this->displayRegisterNowButton();
585
+				$html .= empty($external_url)
586
+					? $this->ticketSelectorEndDiv()
587
+					: $this->clearTicketSelector();
588
+				$html .= '<br/>' . $this->formClose();
589
+			} elseif ($this->getMaxAttendees() === 1) {
590
+				// its a "Dude Where's my Ticket Selector?" (DWMTS) type event (ie: $_max_atndz === 1)
591
+				if ($this->event->is_sold_out()) {
592
+					// then instead of a View Details or Submit button, just display a "Sold Out" message
593
+					$html .= apply_filters(
594
+						'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__sold_out_msg',
595
+						sprintf(
596
+							__(
597
+								'%1$s"%2$s" is currently sold out.%4$sPlease check back again later, as spots may become available.%3$s',
598
+								'event_espresso'
599
+							),
600
+							'<p class="no-ticket-selector-msg clear-float">',
601
+							$this->event->name(),
602
+							'</p>',
603
+							'<br />'
604
+						),
605
+						$this->event
606
+					);
607
+					if (
608
+					apply_filters(
609
+						'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__no_tickets_but_display_register_now_button',
610
+						false,
611
+						$this->event
612
+					)
613
+					) {
614
+						$html .= $this->displayRegisterNowButton();
615
+					}
616
+					// sold out DWMTS event, no TS, no submit or view details button, but has additional content
617
+					$html .= $this->ticketSelectorEndDiv();
618
+				} elseif (
619
+					apply_filters('FHEE__EE_Ticket_Selector__hide_ticket_selector', false)
620
+					&& ! is_single()
621
+				) {
622
+					// this is a "Dude Where's my Ticket Selector?" (DWMTS) type event,
623
+					// but no tickets are available, so display event's "View Details" button.
624
+					// it is being viewed via somewhere other than a single post
625
+					$html .= $this->displayViewDetailsButton(true);
626
+				} else {
627
+					$html .= $this->ticketSelectorEndDiv();
628
+				}
629
+			} elseif (is_archive()) {
630
+				// event list, no tickets available so display event's "View Details" button
631
+				$html .= $this->ticketSelectorEndDiv();
632
+				$html .= $this->displayViewDetailsButton();
633
+			} else {
634
+				if (
635
+				apply_filters(
636
+					'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__no_tickets_but_display_register_now_button',
637
+					false,
638
+					$this->event
639
+				)
640
+				) {
641
+					$html .= $this->displayRegisterNowButton();
642
+				}
643
+				// no submit or view details button, and no additional content
644
+				$html .= $this->ticketSelectorEndDiv();
645
+			}
646
+			if (! $this->iframe && ! is_archive()) {
647
+				$html .= EEH_Template::powered_by_event_espresso('', '', array('utm_content' => 'ticket_selector'));
648
+			}
649
+		}
650
+		return apply_filters(
651
+			'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__displaySubmitButton__html',
652
+			$html,
653
+			$this->event,
654
+			$this
655
+		);
656
+	}
657
+
658
+
659
+
660
+	/**
661
+	 * @return string
662
+	 * @throws EE_Error
663
+	 */
664
+	public function displayRegisterNowButton()
665
+	{
666
+		$btn_text     = apply_filters(
667
+			'FHEE__EE_Ticket_Selector__display_ticket_selector_submit__btn_text',
668
+			__('Register Now', 'event_espresso'),
669
+			$this->event
670
+		);
671
+		$external_url = $this->event->external_url();
672
+		$html         = EEH_HTML::div(
673
+			'',
674
+			'ticket-selector-submit-' . $this->event->ID() . '-btn-wrap',
675
+			'ticket-selector-submit-btn-wrap'
676
+		);
677
+		$html         .= '<input id="ticket-selector-submit-' . $this->event->ID() . '-btn"';
678
+		$html         .= ' class="ticket-selector-submit-btn ';
679
+		$html         .= empty($external_url) ? 'ticket-selector-submit-ajax"' : '"';
680
+		$html         .= ' type="submit" value="' . $btn_text . '" />';
681
+		$html         .= EEH_HTML::divx() . '<!-- .ticket-selector-submit-btn-wrap -->';
682
+		$html         .= apply_filters(
683
+			'FHEE__EE_Ticket_Selector__after_ticket_selector_submit',
684
+			'',
685
+			$this->event,
686
+			$this->iframe
687
+		);
688
+		return $html;
689
+	}
690
+
691
+
692
+	/**
693
+	 * displayViewDetailsButton
694
+	 *
695
+	 * @param bool $DWMTS indicates a "Dude Where's my Ticket Selector?" (DWMTS) type event
696
+	 *                    (ie: $_max_atndz === 1) where there are no available tickets,
697
+	 *                    either because they are sold out, expired, or not yet on sale.
698
+	 *                    In this case, we need to close the form BEFORE adding any closing divs
699
+	 * @return string
700
+	 * @throws EE_Error
701
+	 */
702
+	public function displayViewDetailsButton($DWMTS = false)
703
+	{
704
+		if (! $this->event->get_permalink()) {
705
+			EE_Error::add_error(
706
+				esc_html__('The URL for the Event Details page could not be retrieved.', 'event_espresso'),
707
+				__FILE__,
708
+				__FUNCTION__,
709
+				__LINE__
710
+			);
711
+		}
712
+		$view_details_btn = '<form method="POST" action="';
713
+		$view_details_btn .= apply_filters(
714
+			'FHEE__EE_Ticket_Selector__display_view_details_btn__btn_url',
715
+			$this->event->get_permalink(),
716
+			$this->event
717
+		);
718
+		$view_details_btn .= '"';
719
+		// open link in new window ?
720
+		$view_details_btn .= apply_filters(
721
+			'FHEE__EventEspresso_modules_ticket_selector_DisplayTicketSelector__displayViewDetailsButton__url_target_blank',
722
+			$this->isIframe(),
723
+			$this
724
+		)
725
+			? ' target="_blank"'
726
+			: '';
727
+		$view_details_btn .= '>';
728
+		$btn_text         = apply_filters(
729
+			'FHEE__EE_Ticket_Selector__display_view_details_btn__btn_text',
730
+			esc_html__('View Details', 'event_espresso'),
731
+			$this->event
732
+		);
733
+		$view_details_btn .= '<input id="ticket-selector-submit-'
734
+							 . $this->event->ID()
735
+							 . '-btn" class="ticket-selector-submit-btn view-details-btn" type="submit" value="'
736
+							 . $btn_text
737
+							 . '" />';
738
+		$view_details_btn .= apply_filters('FHEE__EE_Ticket_Selector__after_view_details_btn', '', $this->event);
739
+		if ($DWMTS) {
740
+			$view_details_btn .= $this->formClose();
741
+			$view_details_btn .= $this->ticketSelectorEndDiv();
742
+			$view_details_btn .= '<br/>';
743
+		} else {
744
+			$view_details_btn .= $this->clearTicketSelector();
745
+			$view_details_btn .= '<br/>';
746
+			$view_details_btn .= $this->formClose();
747
+		}
748
+		return $view_details_btn;
749
+	}
750
+
751
+
752
+
753
+	/**
754
+	 * @return string
755
+	 */
756
+	public function ticketSelectorEndDiv()
757
+	{
758
+		return $this->clearTicketSelector() . '</div><!-- ticketSelectorEndDiv -->';
759
+	}
760
+
761
+
762
+
763
+	/**
764
+	 * @return string
765
+	 */
766
+	public function clearTicketSelector()
767
+	{
768
+		// standard TS displayed, appears after a "Register Now" or "view Details" button
769
+		return '<div class="clear"></div><!-- clearTicketSelector -->';
770
+	}
771
+
772
+
773
+
774
+	/**
775
+	 * @access        public
776
+	 * @return        string
777
+	 */
778
+	public function formClose()
779
+	{
780
+		return '</form>';
781
+	}
782 782
 
783 783
 
784 784
 
Please login to merge, or discard this patch.
core/helpers/EEH_URL.helper.php 1 patch
Indentation   +238 added lines, -238 removed lines patch added patch discarded remove patch
@@ -14,262 +14,262 @@
 block discarded – undo
14 14
 class EEH_URL
15 15
 {
16 16
 
17
-    /**
18
-     * _add_query_arg
19
-     * adds nonce to array of arguments then calls WP add_query_arg function
20
-     *
21
-     * @access public
22
-     * @param array  $args
23
-     * @param string $url
24
-     * @param bool   $exclude_nonce If true then the nonce will be excluded from the generated url.
25
-     * @return string
26
-     */
27
-    public static function add_query_args_and_nonce($args = array(), $url = '', $exclude_nonce = false)
28
-    {
29
-        if (empty($url)) {
30
-            $user_msg = esc_html__(
31
-                'An error occurred. A URL is a required parameter for the add_query_args_and_nonce method.',
32
-                'event_espresso'
33
-            );
34
-            $dev_msg  = $user_msg . "\n"
35
-                . sprintf(
36
-                    esc_html__(
37
-                        'In order to dynamically generate nonces for your actions, you need to supply a valid URL as a second parameter for the %s method.',
38
-                        'event_espresso'
39
-                    ),
40
-                    __CLASS__ . '::add_query_args_and_nonce'
41
-                );
42
-            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
43
-        }
44
-        // check that an action exists and add nonce
45
-        if (! $exclude_nonce) {
46
-            if (isset($args['action']) && ! empty($args['action'])) {
47
-                $args = array_merge(
48
-                    $args,
49
-                    array(
50
-                        $args['action'] . '_nonce' => wp_create_nonce($args['action'] . '_nonce')
51
-                    )
52
-                );
53
-            } else {
54
-                $args = array_merge(
55
-                    $args,
56
-                    array(
57
-                        'action' => 'default', 'default_nonce' => wp_create_nonce('default_nonce')
58
-                    )
59
-                );
60
-            }
61
-        }
17
+	/**
18
+	 * _add_query_arg
19
+	 * adds nonce to array of arguments then calls WP add_query_arg function
20
+	 *
21
+	 * @access public
22
+	 * @param array  $args
23
+	 * @param string $url
24
+	 * @param bool   $exclude_nonce If true then the nonce will be excluded from the generated url.
25
+	 * @return string
26
+	 */
27
+	public static function add_query_args_and_nonce($args = array(), $url = '', $exclude_nonce = false)
28
+	{
29
+		if (empty($url)) {
30
+			$user_msg = esc_html__(
31
+				'An error occurred. A URL is a required parameter for the add_query_args_and_nonce method.',
32
+				'event_espresso'
33
+			);
34
+			$dev_msg  = $user_msg . "\n"
35
+				. sprintf(
36
+					esc_html__(
37
+						'In order to dynamically generate nonces for your actions, you need to supply a valid URL as a second parameter for the %s method.',
38
+						'event_espresso'
39
+					),
40
+					__CLASS__ . '::add_query_args_and_nonce'
41
+				);
42
+			EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
43
+		}
44
+		// check that an action exists and add nonce
45
+		if (! $exclude_nonce) {
46
+			if (isset($args['action']) && ! empty($args['action'])) {
47
+				$args = array_merge(
48
+					$args,
49
+					array(
50
+						$args['action'] . '_nonce' => wp_create_nonce($args['action'] . '_nonce')
51
+					)
52
+				);
53
+			} else {
54
+				$args = array_merge(
55
+					$args,
56
+					array(
57
+						'action' => 'default', 'default_nonce' => wp_create_nonce('default_nonce')
58
+					)
59
+				);
60
+			}
61
+		}
62 62
 
63
-        //finally, let's always add a return address (if present) :)
64
-        $args = ! empty($_REQUEST['action']) && ! isset($_REQUEST['return'])
65
-            ? array_merge($args, array('return' => $_REQUEST['action']))
66
-            : $args;
63
+		//finally, let's always add a return address (if present) :)
64
+		$args = ! empty($_REQUEST['action']) && ! isset($_REQUEST['return'])
65
+			? array_merge($args, array('return' => $_REQUEST['action']))
66
+			: $args;
67 67
 
68
-        return add_query_arg($args, $url);
69
-    }
68
+		return add_query_arg($args, $url);
69
+	}
70 70
 
71 71
 
72
-    /**
73
-     * Returns whether not the remote file exists.
74
-     * Checking via GET because HEAD requests are blocked on some server configurations.
75
-     *
76
-     * @param string  $url
77
-     * @param array $args  the arguments that should be passed through to the wp_remote_request call.
78
-     * @return boolean
79
-     */
80
-    public static function remote_file_exists($url, $args = array())
81
-    {
82
-        $results = wp_remote_request(
83
-            $url,
84
-            array_merge(
85
-                array(
86
-                    'method'      => 'GET',
87
-                    'redirection' => 1,
88
-                ),
89
-                $args
90
-            )
91
-        );
92
-        if (! $results instanceof WP_Error &&
93
-            isset($results['response']) &&
94
-            isset($results['response']['code']) &&
95
-            $results['response']['code'] == '200') {
96
-            return true;
97
-        } else {
98
-            return false;
99
-        }
100
-    }
72
+	/**
73
+	 * Returns whether not the remote file exists.
74
+	 * Checking via GET because HEAD requests are blocked on some server configurations.
75
+	 *
76
+	 * @param string  $url
77
+	 * @param array $args  the arguments that should be passed through to the wp_remote_request call.
78
+	 * @return boolean
79
+	 */
80
+	public static function remote_file_exists($url, $args = array())
81
+	{
82
+		$results = wp_remote_request(
83
+			$url,
84
+			array_merge(
85
+				array(
86
+					'method'      => 'GET',
87
+					'redirection' => 1,
88
+				),
89
+				$args
90
+			)
91
+		);
92
+		if (! $results instanceof WP_Error &&
93
+			isset($results['response']) &&
94
+			isset($results['response']['code']) &&
95
+			$results['response']['code'] == '200') {
96
+			return true;
97
+		} else {
98
+			return false;
99
+		}
100
+	}
101 101
 
102 102
 
103
-    /**
104
-     * refactor_url
105
-     * primarily used for removing the query string from a URL
106
-     *
107
-     * @param string $url
108
-     * @param bool   $remove_query  - TRUE (default) will strip off any URL params, ie: ?this=1&that=2
109
-     * @param bool   $base_url_only - TRUE will only return the scheme and host with no other parameters
110
-     * @return string
111
-     */
112
-    public static function refactor_url($url = '', $remove_query = true, $base_url_only = false)
113
-    {
114
-        // break apart incoming URL
115
-        $url_bits = parse_url($url);
116
-        // HTTP or HTTPS ?
117
-        $scheme = isset($url_bits['scheme']) ? $url_bits['scheme'] . '://' : 'http://';
118
-        // domain
119
-        $host = isset($url_bits['host']) ? $url_bits['host'] : '';
120
-        // if only the base URL is requested, then return that now
121
-        if ($base_url_only) {
122
-            return $scheme . $host;
123
-        }
124
-        $port = isset($url_bits['port']) ? ':' . $url_bits['port'] : '';
125
-        $user = isset($url_bits['user']) ? $url_bits['user'] : '';
126
-        $pass = isset($url_bits['pass']) ? ':' . $url_bits['pass'] : '';
127
-        $pass = ($user || $pass) ? $pass . '@' : '';
128
-        $path = isset($url_bits['path']) ? $url_bits['path'] : '';
129
-        // if the query string is not required, then return what we have so far
130
-        if ($remove_query) {
131
-            return $scheme . $user . $pass . $host . $port . $path;
132
-        }
133
-        $query    = isset($url_bits['query']) ? '?' . $url_bits['query'] : '';
134
-        $fragment = isset($url_bits['fragment']) ? '#' . $url_bits['fragment'] : '';
135
-        return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;
136
-    }
103
+	/**
104
+	 * refactor_url
105
+	 * primarily used for removing the query string from a URL
106
+	 *
107
+	 * @param string $url
108
+	 * @param bool   $remove_query  - TRUE (default) will strip off any URL params, ie: ?this=1&that=2
109
+	 * @param bool   $base_url_only - TRUE will only return the scheme and host with no other parameters
110
+	 * @return string
111
+	 */
112
+	public static function refactor_url($url = '', $remove_query = true, $base_url_only = false)
113
+	{
114
+		// break apart incoming URL
115
+		$url_bits = parse_url($url);
116
+		// HTTP or HTTPS ?
117
+		$scheme = isset($url_bits['scheme']) ? $url_bits['scheme'] . '://' : 'http://';
118
+		// domain
119
+		$host = isset($url_bits['host']) ? $url_bits['host'] : '';
120
+		// if only the base URL is requested, then return that now
121
+		if ($base_url_only) {
122
+			return $scheme . $host;
123
+		}
124
+		$port = isset($url_bits['port']) ? ':' . $url_bits['port'] : '';
125
+		$user = isset($url_bits['user']) ? $url_bits['user'] : '';
126
+		$pass = isset($url_bits['pass']) ? ':' . $url_bits['pass'] : '';
127
+		$pass = ($user || $pass) ? $pass . '@' : '';
128
+		$path = isset($url_bits['path']) ? $url_bits['path'] : '';
129
+		// if the query string is not required, then return what we have so far
130
+		if ($remove_query) {
131
+			return $scheme . $user . $pass . $host . $port . $path;
132
+		}
133
+		$query    = isset($url_bits['query']) ? '?' . $url_bits['query'] : '';
134
+		$fragment = isset($url_bits['fragment']) ? '#' . $url_bits['fragment'] : '';
135
+		return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;
136
+	}
137 137
 
138 138
 
139
-    /**
140
-     * get_query_string
141
-     * returns just the query string from a URL, formatted by default into an array of key value pairs
142
-     *
143
-     * @param string $url
144
-     * @param bool   $as_array TRUE (default) will return query params as an array of key value pairs, FALSE will
145
-     *                         simply return the query string
146
-     * @return string|array
147
-     */
148
-    public static function get_query_string($url = '', $as_array = true)
149
-    {
150
-        // decode, then break apart incoming URL
151
-        $url_bits = parse_url(html_entity_decode($url));
152
-        // grab query string from URL
153
-        $query = isset($url_bits['query']) ? $url_bits['query'] : '';
154
-        // if we don't want the query string formatted into an array of key => value pairs, then just return it as is
155
-        if (! $as_array) {
156
-            return $query;
157
-        }
158
-        // if no query string exists then just return an empty array now
159
-        if (empty($query)) {
160
-            return array();
161
-        }
162
-        // empty array to hold results
163
-        $query_params = array();
164
-        // now break apart the query string into separate params
165
-        $query = explode('&', $query);
166
-        // loop thru our query params
167
-        foreach ($query as $query_args) {
168
-            // break apart the key value pairs
169
-            $query_args = explode('=', $query_args);
170
-            // and add to our results array
171
-            $query_params[$query_args[0]] = $query_args[1];
172
-        }
173
-        return $query_params;
174
-    }
139
+	/**
140
+	 * get_query_string
141
+	 * returns just the query string from a URL, formatted by default into an array of key value pairs
142
+	 *
143
+	 * @param string $url
144
+	 * @param bool   $as_array TRUE (default) will return query params as an array of key value pairs, FALSE will
145
+	 *                         simply return the query string
146
+	 * @return string|array
147
+	 */
148
+	public static function get_query_string($url = '', $as_array = true)
149
+	{
150
+		// decode, then break apart incoming URL
151
+		$url_bits = parse_url(html_entity_decode($url));
152
+		// grab query string from URL
153
+		$query = isset($url_bits['query']) ? $url_bits['query'] : '';
154
+		// if we don't want the query string formatted into an array of key => value pairs, then just return it as is
155
+		if (! $as_array) {
156
+			return $query;
157
+		}
158
+		// if no query string exists then just return an empty array now
159
+		if (empty($query)) {
160
+			return array();
161
+		}
162
+		// empty array to hold results
163
+		$query_params = array();
164
+		// now break apart the query string into separate params
165
+		$query = explode('&', $query);
166
+		// loop thru our query params
167
+		foreach ($query as $query_args) {
168
+			// break apart the key value pairs
169
+			$query_args = explode('=', $query_args);
170
+			// and add to our results array
171
+			$query_params[$query_args[0]] = $query_args[1];
172
+		}
173
+		return $query_params;
174
+	}
175 175
 
176 176
 
177
-    /**
178
-     * prevent_prefetching
179
-     *
180
-     * @return void
181
-     */
182
-    public static function prevent_prefetching()
183
-    {
184
-        // prevent browsers from prefetching of the rel='next' link, because it may contain content that interferes
185
-        // with the registration process
186
-        remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
187
-    }
177
+	/**
178
+	 * prevent_prefetching
179
+	 *
180
+	 * @return void
181
+	 */
182
+	public static function prevent_prefetching()
183
+	{
184
+		// prevent browsers from prefetching of the rel='next' link, because it may contain content that interferes
185
+		// with the registration process
186
+		remove_action('wp_head', 'adjacent_posts_rel_link_wp_head');
187
+	}
188 188
 
189 189
 
190
-    /**
191
-     * This generates a unique site-specific string.
192
-     * An example usage for this string would be to save as a unique identifier for a record in the db for usage in
193
-     * urls.
194
-     *
195
-     * @param   string $prefix Use this to prefix the string with something.
196
-     * @return string
197
-     */
198
-    public static function generate_unique_token($prefix = '')
199
-    {
200
-        $token = md5(uniqid() . mt_rand());
201
-        return $prefix ? $prefix . '_' . $token : $token;
202
-    }
190
+	/**
191
+	 * This generates a unique site-specific string.
192
+	 * An example usage for this string would be to save as a unique identifier for a record in the db for usage in
193
+	 * urls.
194
+	 *
195
+	 * @param   string $prefix Use this to prefix the string with something.
196
+	 * @return string
197
+	 */
198
+	public static function generate_unique_token($prefix = '')
199
+	{
200
+		$token = md5(uniqid() . mt_rand());
201
+		return $prefix ? $prefix . '_' . $token : $token;
202
+	}
203 203
 
204 204
 
205
-    /**
206
-     * filter_input_server_url
207
-     * uses filter_input() to sanitize one of the INPUT_SERVER URL values
208
-     * but adds a backup in case filter_input() returns nothing, which can erringly happen on some servers
209
-     *
210
-     * @param string $server_variable
211
-     * @return string
212
-     */
213
-    public static function filter_input_server_url($server_variable = 'REQUEST_URI')
214
-    {
215
-        $URL              = '';
216
-        $server_variables = array(
217
-            'REQUEST_URI' => 1,
218
-            'HTTP_HOST'   => 1,
219
-            'PHP_SELF'    => 1,
220
-        );
221
-        $server_variable  = strtoupper($server_variable);
222
-        // whitelist INPUT_SERVER var
223
-        if (isset($server_variables[$server_variable])) {
224
-            $URL = filter_input(INPUT_SERVER, $server_variable, FILTER_SANITIZE_URL, FILTER_NULL_ON_FAILURE);
225
-            if (empty($URL)) {
226
-                // fallback sanitization if the above fails
227
-                $URL = wp_sanitize_redirect($_SERVER[$server_variable]);
228
-            }
229
-        }
230
-        return $URL;
231
-    }
205
+	/**
206
+	 * filter_input_server_url
207
+	 * uses filter_input() to sanitize one of the INPUT_SERVER URL values
208
+	 * but adds a backup in case filter_input() returns nothing, which can erringly happen on some servers
209
+	 *
210
+	 * @param string $server_variable
211
+	 * @return string
212
+	 */
213
+	public static function filter_input_server_url($server_variable = 'REQUEST_URI')
214
+	{
215
+		$URL              = '';
216
+		$server_variables = array(
217
+			'REQUEST_URI' => 1,
218
+			'HTTP_HOST'   => 1,
219
+			'PHP_SELF'    => 1,
220
+		);
221
+		$server_variable  = strtoupper($server_variable);
222
+		// whitelist INPUT_SERVER var
223
+		if (isset($server_variables[$server_variable])) {
224
+			$URL = filter_input(INPUT_SERVER, $server_variable, FILTER_SANITIZE_URL, FILTER_NULL_ON_FAILURE);
225
+			if (empty($URL)) {
226
+				// fallback sanitization if the above fails
227
+				$URL = wp_sanitize_redirect($_SERVER[$server_variable]);
228
+			}
229
+		}
230
+		return $URL;
231
+	}
232 232
 
233 233
 
234
-    /**
235
-     * Gets the current page's full URL.
236
-     *
237
-     * @return string
238
-     */
239
-    public static function current_url()
240
-    {
241
-        $url = '';
242
-        if (isset($_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'])) {
243
-            $url = is_ssl() ? 'https://' : 'http://';
244
-            $url .= \EEH_URL::filter_input_server_url('HTTP_HOST');
245
-            $url .= \EEH_URL::filter_input_server_url('REQUEST_URI');
246
-        }
247
-        return $url;
248
-    }
234
+	/**
235
+	 * Gets the current page's full URL.
236
+	 *
237
+	 * @return string
238
+	 */
239
+	public static function current_url()
240
+	{
241
+		$url = '';
242
+		if (isset($_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'])) {
243
+			$url = is_ssl() ? 'https://' : 'http://';
244
+			$url .= \EEH_URL::filter_input_server_url('HTTP_HOST');
245
+			$url .= \EEH_URL::filter_input_server_url('REQUEST_URI');
246
+		}
247
+		return $url;
248
+	}
249 249
 
250 250
 
251
-    /**
252
-     * Identical in functionality to EEH_current_url except it removes any provided query_parameters from it.
253
-     *
254
-     * @param array $query_parameters An array of query_parameters to remove from the current url.
255
-     * @since 4.9.46.rc.029
256
-     * @return string
257
-     */
258
-    public static function current_url_without_query_paramaters(array $query_parameters)
259
-    {
260
-        return remove_query_arg($query_parameters, EEH_URL::current_url());
261
-    }
251
+	/**
252
+	 * Identical in functionality to EEH_current_url except it removes any provided query_parameters from it.
253
+	 *
254
+	 * @param array $query_parameters An array of query_parameters to remove from the current url.
255
+	 * @since 4.9.46.rc.029
256
+	 * @return string
257
+	 */
258
+	public static function current_url_without_query_paramaters(array $query_parameters)
259
+	{
260
+		return remove_query_arg($query_parameters, EEH_URL::current_url());
261
+	}
262 262
 
263 263
 
264
-    /**
265
-     * @param string $location
266
-     * @param int    $status
267
-     * @param string $exit_notice
268
-     */
269
-    public static function safeRedirectAndExit($location, $status = 302, $exit_notice = '')
270
-    {
271
-        EE_Error::get_notices(false, true);
272
-        wp_safe_redirect($location, $status);
273
-        exit($exit_notice);
274
-    }
264
+	/**
265
+	 * @param string $location
266
+	 * @param int    $status
267
+	 * @param string $exit_notice
268
+	 */
269
+	public static function safeRedirectAndExit($location, $status = 302, $exit_notice = '')
270
+	{
271
+		EE_Error::get_notices(false, true);
272
+		wp_safe_redirect($location, $status);
273
+		exit($exit_notice);
274
+	}
275 275
 }
Please login to merge, or discard this patch.
strategies/display/EE_Invisible_Recaptcha_Display_Strategy.strategy.php 1 patch
Indentation   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -18,27 +18,27 @@  discard block
 block discarded – undo
18 18
 class EE_Invisible_Recaptcha_Display_Strategy extends EE_Display_Strategy_Base
19 19
 {
20 20
 
21
-    /**
22
-     * @return EE_Form_Input_Base|EE_Invisible_Recaptcha_Input
23
-     */
24
-    public function input()
25
-    {
26
-        return $this->_input;
27
-    }
28
-
29
-
30
-    /**
31
-     * returns HTML and javascript related to the displaying of this input
32
-     *
33
-     * @return string
34
-     * @throws InvalidInterfaceException
35
-     * @throws InvalidDataTypeException
36
-     * @throws InvalidArgumentException
37
-     */
38
-    public function display()
39
-    {
40
-        wp_enqueue_script(EE_Invisible_Recaptcha_Input::SCRIPT_HANDLE_GOOGLE_INVISIBLE_RECAPTCHA);
41
-        return <<<EOD
21
+	/**
22
+	 * @return EE_Form_Input_Base|EE_Invisible_Recaptcha_Input
23
+	 */
24
+	public function input()
25
+	{
26
+		return $this->_input;
27
+	}
28
+
29
+
30
+	/**
31
+	 * returns HTML and javascript related to the displaying of this input
32
+	 *
33
+	 * @return string
34
+	 * @throws InvalidInterfaceException
35
+	 * @throws InvalidDataTypeException
36
+	 * @throws InvalidArgumentException
37
+	 */
38
+	public function display()
39
+	{
40
+		wp_enqueue_script(EE_Invisible_Recaptcha_Input::SCRIPT_HANDLE_GOOGLE_INVISIBLE_RECAPTCHA);
41
+		return <<<EOD
42 42
     <div id="g-recaptcha-{$this->input()->recaptchaId()}"
43 43
         class="g-recaptcha"
44 44
         data-sitekey="{$this->input()->siteKey()}"
@@ -48,5 +48,5 @@  discard block
 block discarded – undo
48 48
         >
49 49
     </div>
50 50
 EOD;
51
-    }
51
+	}
52 52
 }
Please login to merge, or discard this patch.
form_sections/strategies/layout/EE_Div_Per_Section_Layout.strategy.php 2 patches
Indentation   +132 added lines, -132 removed lines patch added patch discarded remove patch
@@ -14,138 +14,138 @@
 block discarded – undo
14 14
 class EE_Div_Per_Section_Layout extends EE_Form_Section_Layout_Base
15 15
 {
16 16
 
17
-    /**
18
-     * opening div tag for a form
19
-     *
20
-     * @return string
21
-     */
22
-    public function layout_form_begin()
23
-    {
24
-        return EEH_HTML::div(
25
-            '',
26
-            $this->_form_section->html_id(),
27
-            $this->_form_section->html_class(),
28
-            $this->_form_section->html_style()
29
-        );
30
-    }
31
-
32
-
33
-
34
-    /**
35
-     * Lays out the row for the input, including label and errors
36
-     *
37
-     * @param EE_Form_Input_Base $input
38
-     * @return string
39
-     * @throws \EE_Error
40
-     */
41
-    public function layout_input($input)
42
-    {
43
-        $html = '';
44
-        // set something unique for the id
45
-        $html_id = (string)$input->html_id() !== ''
46
-            ? (string)$input->html_id()
47
-            : spl_object_hash($input);
48
-        // and add a generic input type class
49
-        $html_class = sanitize_key(str_replace('_', '-', get_class($input))) . '-dv';
50
-        if ($input instanceof EE_Hidden_Input) {
51
-            $html .= EEH_HTML::nl() . $input->get_html_for_input();
52
-        } else if ($input instanceof EE_Submit_Input) {
53
-            $html .= EEH_HTML::div(
54
-                $input->get_html_for_input(),
55
-                $html_id . '-submit-dv',
56
-                "{$input->html_class()}-submit-dv {$html_class}"
57
-            );
58
-        } else if ($input instanceof EE_Select_Input) {
59
-            $html .= EEH_HTML::div(
60
-                EEH_HTML::nl(1) . $input->get_html_for_label() .
61
-                EEH_HTML::nl() . $input->get_html_for_errors() .
62
-                EEH_HTML::nl() . $input->get_html_for_input() .
63
-                EEH_HTML::nl() . $input->get_html_for_help(),
64
-                $html_id . '-input-dv',
65
-                "{$input->html_class()}-input-dv {$html_class}"
66
-            );
67
-        } else if ($input instanceof EE_Form_Input_With_Options_Base) {
68
-            $html .= EEH_HTML::div(
69
-                EEH_HTML::nl() . $this->_display_label_for_option_type_question($input) .
70
-                EEH_HTML::nl() . $input->get_html_for_errors() .
71
-                EEH_HTML::nl() . $input->get_html_for_input() .
72
-                EEH_HTML::nl() . $input->get_html_for_help(),
73
-                $html_id . '-input-dv',
74
-                "{$input->html_class()}-input-dv {$html_class}"
75
-            );
76
-        } else {
77
-            $html .= EEH_HTML::div(
78
-                EEH_HTML::nl(1) . $input->get_html_for_label() .
79
-                EEH_HTML::nl() . $input->get_html_for_errors() .
80
-                EEH_HTML::nl() . $input->get_html_for_input() .
81
-                EEH_HTML::nl() . $input->get_html_for_help(),
82
-                $html_id . '-input-dv',
83
-                "{$input->html_class()}-input-dv {$html_class}"
84
-            );
85
-        }
86
-        return $html;
87
-    }
88
-
89
-
90
-
91
-    /**
92
-     *
93
-     * _display_label_for_option_type_question
94
-     * Gets the HTML for the 'label', which is just text for this (because labels
95
-     * should be for each input)
96
-     *
97
-     * @param EE_Form_Input_With_Options_Base $input
98
-     * @return string
99
-     */
100
-    protected function _display_label_for_option_type_question(EE_Form_Input_With_Options_Base $input)
101
-    {
102
-        if ($input->display_html_label_text()) {
103
-            $html_label_text = $input->html_label_text();
104
-            $label_html = EEH_HTML::div(
105
-                $input->required()
106
-                    ? $html_label_text . EEH_HTML::span('*', '', 'ee-asterisk')
107
-                    : $html_label_text,
108
-                $input->html_label_id(),
109
-                $input->required()
110
-                    ? 'ee-required-label ' . $input->html_label_class()
111
-                    : $input->html_label_class(),
112
-                $input->html_label_style(),
113
-                $input->html_other_attributes()
114
-            );
115
-            // if no content was provided to EEH_HTML::div() above (ie: an empty label),
116
-            // then we need to close the div manually
117
-            if(empty($html_label_text)){
118
-                $label_html .= EEH_HTML::divx($input->html_label_id(), $input->html_label_class());
119
-            }
120
-            return $label_html;
121
-        }
122
-        return '';
123
-    }
124
-
125
-
126
-
127
-    /**
128
-     * Lays out a row for the subsection
129
-     *
130
-     * @param EE_Form_Section_Proper $form_section
131
-     * @return string
132
-     */
133
-    public function layout_subsection($form_section)
134
-    {
135
-        return EEH_HTML::nl(1) . $form_section->get_html() . EEH_HTML::nl(-1);
136
-    }
137
-
138
-
139
-
140
-    /**
141
-     * closing div tag for a form
142
-     *
143
-     * @return string
144
-     */
145
-    public function layout_form_end()
146
-    {
147
-        return EEH_HTML::divx($this->_form_section->html_id(), $this->_form_section->html_class());
148
-    }
17
+	/**
18
+	 * opening div tag for a form
19
+	 *
20
+	 * @return string
21
+	 */
22
+	public function layout_form_begin()
23
+	{
24
+		return EEH_HTML::div(
25
+			'',
26
+			$this->_form_section->html_id(),
27
+			$this->_form_section->html_class(),
28
+			$this->_form_section->html_style()
29
+		);
30
+	}
31
+
32
+
33
+
34
+	/**
35
+	 * Lays out the row for the input, including label and errors
36
+	 *
37
+	 * @param EE_Form_Input_Base $input
38
+	 * @return string
39
+	 * @throws \EE_Error
40
+	 */
41
+	public function layout_input($input)
42
+	{
43
+		$html = '';
44
+		// set something unique for the id
45
+		$html_id = (string)$input->html_id() !== ''
46
+			? (string)$input->html_id()
47
+			: spl_object_hash($input);
48
+		// and add a generic input type class
49
+		$html_class = sanitize_key(str_replace('_', '-', get_class($input))) . '-dv';
50
+		if ($input instanceof EE_Hidden_Input) {
51
+			$html .= EEH_HTML::nl() . $input->get_html_for_input();
52
+		} else if ($input instanceof EE_Submit_Input) {
53
+			$html .= EEH_HTML::div(
54
+				$input->get_html_for_input(),
55
+				$html_id . '-submit-dv',
56
+				"{$input->html_class()}-submit-dv {$html_class}"
57
+			);
58
+		} else if ($input instanceof EE_Select_Input) {
59
+			$html .= EEH_HTML::div(
60
+				EEH_HTML::nl(1) . $input->get_html_for_label() .
61
+				EEH_HTML::nl() . $input->get_html_for_errors() .
62
+				EEH_HTML::nl() . $input->get_html_for_input() .
63
+				EEH_HTML::nl() . $input->get_html_for_help(),
64
+				$html_id . '-input-dv',
65
+				"{$input->html_class()}-input-dv {$html_class}"
66
+			);
67
+		} else if ($input instanceof EE_Form_Input_With_Options_Base) {
68
+			$html .= EEH_HTML::div(
69
+				EEH_HTML::nl() . $this->_display_label_for_option_type_question($input) .
70
+				EEH_HTML::nl() . $input->get_html_for_errors() .
71
+				EEH_HTML::nl() . $input->get_html_for_input() .
72
+				EEH_HTML::nl() . $input->get_html_for_help(),
73
+				$html_id . '-input-dv',
74
+				"{$input->html_class()}-input-dv {$html_class}"
75
+			);
76
+		} else {
77
+			$html .= EEH_HTML::div(
78
+				EEH_HTML::nl(1) . $input->get_html_for_label() .
79
+				EEH_HTML::nl() . $input->get_html_for_errors() .
80
+				EEH_HTML::nl() . $input->get_html_for_input() .
81
+				EEH_HTML::nl() . $input->get_html_for_help(),
82
+				$html_id . '-input-dv',
83
+				"{$input->html_class()}-input-dv {$html_class}"
84
+			);
85
+		}
86
+		return $html;
87
+	}
88
+
89
+
90
+
91
+	/**
92
+	 *
93
+	 * _display_label_for_option_type_question
94
+	 * Gets the HTML for the 'label', which is just text for this (because labels
95
+	 * should be for each input)
96
+	 *
97
+	 * @param EE_Form_Input_With_Options_Base $input
98
+	 * @return string
99
+	 */
100
+	protected function _display_label_for_option_type_question(EE_Form_Input_With_Options_Base $input)
101
+	{
102
+		if ($input->display_html_label_text()) {
103
+			$html_label_text = $input->html_label_text();
104
+			$label_html = EEH_HTML::div(
105
+				$input->required()
106
+					? $html_label_text . EEH_HTML::span('*', '', 'ee-asterisk')
107
+					: $html_label_text,
108
+				$input->html_label_id(),
109
+				$input->required()
110
+					? 'ee-required-label ' . $input->html_label_class()
111
+					: $input->html_label_class(),
112
+				$input->html_label_style(),
113
+				$input->html_other_attributes()
114
+			);
115
+			// if no content was provided to EEH_HTML::div() above (ie: an empty label),
116
+			// then we need to close the div manually
117
+			if(empty($html_label_text)){
118
+				$label_html .= EEH_HTML::divx($input->html_label_id(), $input->html_label_class());
119
+			}
120
+			return $label_html;
121
+		}
122
+		return '';
123
+	}
124
+
125
+
126
+
127
+	/**
128
+	 * Lays out a row for the subsection
129
+	 *
130
+	 * @param EE_Form_Section_Proper $form_section
131
+	 * @return string
132
+	 */
133
+	public function layout_subsection($form_section)
134
+	{
135
+		return EEH_HTML::nl(1) . $form_section->get_html() . EEH_HTML::nl(-1);
136
+	}
137
+
138
+
139
+
140
+	/**
141
+	 * closing div tag for a form
142
+	 *
143
+	 * @return string
144
+	 */
145
+	public function layout_form_end()
146
+	{
147
+		return EEH_HTML::divx($this->_form_section->html_id(), $this->_form_section->html_class());
148
+	}
149 149
 
150 150
 
151 151
 
Please login to merge, or discard this patch.
Spacing   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -42,44 +42,44 @@  discard block
 block discarded – undo
42 42
     {
43 43
         $html = '';
44 44
         // set something unique for the id
45
-        $html_id = (string)$input->html_id() !== ''
46
-            ? (string)$input->html_id()
45
+        $html_id = (string) $input->html_id() !== ''
46
+            ? (string) $input->html_id()
47 47
             : spl_object_hash($input);
48 48
         // and add a generic input type class
49
-        $html_class = sanitize_key(str_replace('_', '-', get_class($input))) . '-dv';
49
+        $html_class = sanitize_key(str_replace('_', '-', get_class($input))).'-dv';
50 50
         if ($input instanceof EE_Hidden_Input) {
51
-            $html .= EEH_HTML::nl() . $input->get_html_for_input();
51
+            $html .= EEH_HTML::nl().$input->get_html_for_input();
52 52
         } else if ($input instanceof EE_Submit_Input) {
53 53
             $html .= EEH_HTML::div(
54 54
                 $input->get_html_for_input(),
55
-                $html_id . '-submit-dv',
55
+                $html_id.'-submit-dv',
56 56
                 "{$input->html_class()}-submit-dv {$html_class}"
57 57
             );
58 58
         } else if ($input instanceof EE_Select_Input) {
59 59
             $html .= EEH_HTML::div(
60
-                EEH_HTML::nl(1) . $input->get_html_for_label() .
61
-                EEH_HTML::nl() . $input->get_html_for_errors() .
62
-                EEH_HTML::nl() . $input->get_html_for_input() .
63
-                EEH_HTML::nl() . $input->get_html_for_help(),
64
-                $html_id . '-input-dv',
60
+                EEH_HTML::nl(1).$input->get_html_for_label().
61
+                EEH_HTML::nl().$input->get_html_for_errors().
62
+                EEH_HTML::nl().$input->get_html_for_input().
63
+                EEH_HTML::nl().$input->get_html_for_help(),
64
+                $html_id.'-input-dv',
65 65
                 "{$input->html_class()}-input-dv {$html_class}"
66 66
             );
67 67
         } else if ($input instanceof EE_Form_Input_With_Options_Base) {
68 68
             $html .= EEH_HTML::div(
69
-                EEH_HTML::nl() . $this->_display_label_for_option_type_question($input) .
70
-                EEH_HTML::nl() . $input->get_html_for_errors() .
71
-                EEH_HTML::nl() . $input->get_html_for_input() .
72
-                EEH_HTML::nl() . $input->get_html_for_help(),
73
-                $html_id . '-input-dv',
69
+                EEH_HTML::nl().$this->_display_label_for_option_type_question($input).
70
+                EEH_HTML::nl().$input->get_html_for_errors().
71
+                EEH_HTML::nl().$input->get_html_for_input().
72
+                EEH_HTML::nl().$input->get_html_for_help(),
73
+                $html_id.'-input-dv',
74 74
                 "{$input->html_class()}-input-dv {$html_class}"
75 75
             );
76 76
         } else {
77 77
             $html .= EEH_HTML::div(
78
-                EEH_HTML::nl(1) . $input->get_html_for_label() .
79
-                EEH_HTML::nl() . $input->get_html_for_errors() .
80
-                EEH_HTML::nl() . $input->get_html_for_input() .
81
-                EEH_HTML::nl() . $input->get_html_for_help(),
82
-                $html_id . '-input-dv',
78
+                EEH_HTML::nl(1).$input->get_html_for_label().
79
+                EEH_HTML::nl().$input->get_html_for_errors().
80
+                EEH_HTML::nl().$input->get_html_for_input().
81
+                EEH_HTML::nl().$input->get_html_for_help(),
82
+                $html_id.'-input-dv',
83 83
                 "{$input->html_class()}-input-dv {$html_class}"
84 84
             );
85 85
         }
@@ -103,18 +103,18 @@  discard block
 block discarded – undo
103 103
             $html_label_text = $input->html_label_text();
104 104
             $label_html = EEH_HTML::div(
105 105
                 $input->required()
106
-                    ? $html_label_text . EEH_HTML::span('*', '', 'ee-asterisk')
106
+                    ? $html_label_text.EEH_HTML::span('*', '', 'ee-asterisk')
107 107
                     : $html_label_text,
108 108
                 $input->html_label_id(),
109 109
                 $input->required()
110
-                    ? 'ee-required-label ' . $input->html_label_class()
110
+                    ? 'ee-required-label '.$input->html_label_class()
111 111
                     : $input->html_label_class(),
112 112
                 $input->html_label_style(),
113 113
                 $input->html_other_attributes()
114 114
             );
115 115
             // if no content was provided to EEH_HTML::div() above (ie: an empty label),
116 116
             // then we need to close the div manually
117
-            if(empty($html_label_text)){
117
+            if (empty($html_label_text)) {
118 118
                 $label_html .= EEH_HTML::divx($input->html_label_id(), $input->html_label_class());
119 119
             }
120 120
             return $label_html;
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
      */
133 133
     public function layout_subsection($form_section)
134 134
     {
135
-        return EEH_HTML::nl(1) . $form_section->get_html() . EEH_HTML::nl(-1);
135
+        return EEH_HTML::nl(1).$form_section->get_html().EEH_HTML::nl(-1);
136 136
     }
137 137
 
138 138
 
Please login to merge, or discard this patch.