Completed
Branch BUG/reg-status-change-recursio... (fbd515)
by
unknown
09:28 queued 36s
created
caffeinated/payment_methods/Paypal_Pro/EEG_Paypal_Pro.gateway.php 1 patch
Indentation   +602 added lines, -602 removed lines patch added patch discarded remove patch
@@ -11,606 +11,606 @@
 block discarded – undo
11 11
 class EEG_Paypal_Pro extends EE_Onsite_Gateway
12 12
 {
13 13
 
14
-    /**
15
-     * @var $_paypal_api_username string
16
-     */
17
-    protected $_username = null;
18
-
19
-    /**
20
-     * @var $_password string
21
-     */
22
-    protected $_password = null;
23
-
24
-    /**
25
-     * @var $_signature string
26
-     */
27
-    protected $_signature = null;
28
-
29
-    /**
30
-     * @var $_credit_card_types array with the keys for credit card types accepted on this account
31
-     */
32
-    protected $_credit_card_types    = null;
33
-
34
-    protected $_currencies_supported = array(
35
-        'USD',
36
-        'GBP',
37
-        'CAD',
38
-        'AUD',
39
-        'BRL',
40
-        'CHF',
41
-        'CZK',
42
-        'DKK',
43
-        'EUR',
44
-        'HKD',
45
-        'HUF',
46
-        'ILS',
47
-        'JPY',
48
-        'MXN',
49
-        'MYR',
50
-        'NOK',
51
-        'NZD',
52
-        'PHP',
53
-        'PLN',
54
-        'SEK',
55
-        'SGD',
56
-        'THB',
57
-        'TRY',
58
-        'TWD',
59
-        'RUB',
60
-        'INR',
61
-    );
62
-
63
-
64
-
65
-    /**
66
-     * @param EEI_Payment $payment
67
-     * @param array       $billing_info {
68
-     * @type string $credit_card
69
-     * @type string $credit_card_type
70
-     * @type string $exp_month always 2 characters
71
-     * @type string $exp_year always 4 characters
72
-     * @type string $cvv
73
-     * }
74
-     * @see      parent::do_direct_payment for more info
75
-     * @return EE_Payment|EEI_Payment
76
-     * @throws EE_Error
77
-     */
78
-    public function do_direct_payment($payment, $billing_info = null)
79
-    {
80
-        $transaction = $payment->transaction();
81
-        if (! $transaction instanceof EEI_Transaction) {
82
-            throw new EE_Error(
83
-                esc_html__('No transaction for payment while paying with PayPal Pro.', 'event_espresso')
84
-            );
85
-        }
86
-        $primary_registrant = $transaction->primary_registration();
87
-        if (! $primary_registrant instanceof EEI_Registration) {
88
-            throw new EE_Error(
89
-                esc_html__(
90
-                    'No primary registration on transaction while paying with PayPal Pro.',
91
-                    'event_espresso'
92
-                )
93
-            );
94
-        }
95
-        $attendee = $primary_registrant->attendee();
96
-        if (! $attendee instanceof EEI_Attendee) {
97
-            throw new EE_Error(
98
-                esc_html__(
99
-                    'No attendee on primary registration while paying with PayPal Pro.',
100
-                    'event_espresso'
101
-                )
102
-            );
103
-        }
104
-        $gateway_formatter = $this->_get_gateway_formatter();
105
-        $order_description = substr($gateway_formatter->formatOrderDescription($payment), 0, 127);
106
-        // charge for the full amount. Show itemized list
107
-        if ($this->_money->compare_floats($payment->amount(), $transaction->total(), '==')) {
108
-            $item_num = 1;
109
-            $total_line_item = $transaction->total_line_item();
110
-            $order_items = array();
111
-            foreach ($total_line_item->get_items() as $line_item) {
112
-                // ignore line items with a quantity of 0
113
-                if ($line_item->quantity() == 0) {
114
-                    continue;
115
-                }
116
-                // For percent items, whose unit_price is 0, use the total instead.
117
-                if ($line_item->is_percent()) {
118
-                    $unit_price = $line_item->total();
119
-                    $line_item_quantity = 1;
120
-                } else {
121
-                    $unit_price = $line_item->unit_price();
122
-                    $line_item_quantity = $line_item->quantity();
123
-                }
124
-                $item = array(
125
-                    // Item Name.  127 char max.
126
-                    'l_name'                 => substr(
127
-                        $gateway_formatter->formatLineItemName($line_item, $payment),
128
-                        0,
129
-                        127
130
-                    ),
131
-                    // Item description.  127 char max.
132
-                    'l_desc'                 => substr(
133
-                        $gateway_formatter->formatLineItemDesc($line_item, $payment),
134
-                        0,
135
-                        127
136
-                    ),
137
-                    // Cost of individual item.
138
-                    'l_amt'                  => $unit_price,
139
-                    // Item Number.  127 char max.
140
-                    'l_number'               => $item_num++,
141
-                    // Item quantity.  Must be any positive integer.
142
-                    'l_qty'                  => $line_item_quantity,
143
-                    // Item's sales tax amount.
144
-                    'l_taxamt'               => '',
145
-                    // eBay auction number of item.
146
-                    'l_ebayitemnumber'       => '',
147
-                    // eBay transaction ID of purchased item.
148
-                    'l_ebayitemauctiontxnid' => '',
149
-                    // eBay order ID for the item.
150
-                    'l_ebayitemorderid'      => '',
151
-                );
152
-                // add to array of all items
153
-                array_push($order_items, $item);
154
-            }
155
-            $item_amount = $total_line_item->get_items_total();
156
-            $tax_amount = $total_line_item->get_total_tax();
157
-        } else {
158
-            $order_items = array();
159
-            $item_amount = $payment->amount();
160
-            $tax_amount = 0;
161
-            array_push($order_items, array(
162
-                // Item Name.  127 char max.
163
-                'l_name'   => substr(
164
-                    $gateway_formatter->formatPartialPaymentLineItemName($payment),
165
-                    0,
166
-                    127
167
-                ),
168
-                // Item description.  127 char max.
169
-                'l_desc'   => substr(
170
-                    $gateway_formatter->formatPartialPaymentLineItemDesc($payment),
171
-                    0,
172
-                    127
173
-                ),
174
-                // Cost of individual item.
175
-                'l_amt'    => $payment->amount(),
176
-                // Item Number.  127 char max.
177
-                'l_number' => 1,
178
-                // Item quantity.  Must be any positive integer.
179
-                'l_qty'    => 1,
180
-            ));
181
-        }
182
-        // Populate data arrays with order data.
183
-        $DPFields = array(
184
-            // How you want to obtain payment ?
185
-            // Authorization indicates the payment is a basic auth subject to settlement with Auth & Capture.
186
-            // Sale indicates that this is a final sale for which you are requesting payment.  Default is Sale.
187
-            'paymentaction'    => 'Sale',
188
-            // Required.  IP address of the payer's browser.
189
-            'ipaddress'        => $_SERVER['REMOTE_ADDR'],
190
-            // Flag to determine whether you want the results returned by FMF.  1 or 0.  Default is 0.
191
-            'returnfmfdetails' => '1',
192
-        );
193
-        $CCDetails = array(
194
-            // Required. Type of credit card.  Visa, MasterCard, Discover, Amex, Maestro, Solo.
195
-            // If Maestro or Solo, the currency code must be GBP.
196
-            //  In addition, either start date or issue number must be specified.
197
-            'creditcardtype' => $billing_info['credit_card_type'],
198
-            // Required.  Credit card number.  No spaces or punctuation.
199
-            'acct'           => $billing_info['credit_card'],
200
-            // Required.  Credit card expiration date.  Format is MMYYYY
201
-            'expdate'        => $billing_info['exp_month'] . $billing_info['exp_year'],
202
-            // Requirements determined by your PayPal account settings.  Security digits for credit card.
203
-            'cvv2'           => $billing_info['cvv'],
204
-        );
205
-        $PayerInfo = array(
206
-            // Email address of payer.
207
-            'email'       => $billing_info['email'],
208
-            // Unique PayPal customer ID for payer.
209
-            'payerid'     => '',
210
-            // Status of payer.  Values are verified or unverified
211
-            'payerstatus' => '',
212
-            // Payer's business name.
213
-            'business'    => '',
214
-        );
215
-        $PayerName = array(
216
-            // Payer's salutation.  20 char max.
217
-            'salutation' => '',
218
-            // Payer's first name.  25 char max.
219
-            'firstname'  => substr($billing_info['first_name'], 0, 25),
220
-            // Payer's middle name.  25 char max.
221
-            'middlename' => '',
222
-            // Payer's last name.  25 char max.
223
-            'lastname'   => substr($billing_info['last_name'], 0, 25),
224
-            // Payer's suffix.  12 char max.
225
-            'suffix'     => '',
226
-        );
227
-        $BillingAddress = array(
228
-            // Required.  First street address.
229
-            'street'      => $billing_info['address'],
230
-            // Second street address.
231
-            'street2'     => $billing_info['address2'],
232
-            // Required.  Name of City.
233
-            'city'        => $billing_info['city'],
234
-            // Required. Name of State or Province.
235
-            'state'       => substr($billing_info['state'], 0, 40),
236
-            // Required.  Country code.
237
-            'countrycode' => $billing_info['country'],
238
-            // Required.  Postal code of payer.
239
-            'zip'         => $billing_info['zip'],
240
-        );
241
-        // check if the registration info contains the needed fields for paypal pro
242
-        // (see https://developer.paypal.com/docs/classic/api/merchant/DoDirectPayment_API_Operation_NVP/)
243
-        if ($attendee->address() && $attendee->city() && $attendee->country_ID()) {
244
-            $use_registration_address_info = true;
245
-        } else {
246
-            $use_registration_address_info = false;
247
-        }
248
-        // so if the attendee has enough data to fill out PayPal Pro's shipping info, use it.
249
-        // If not, use the billing info again
250
-        $ShippingAddress = array(
251
-            'shiptoname'     => substr($use_registration_address_info
252
-                ? $attendee->full_name()
253
-                : $billing_info['first_name'] . ' ' . $billing_info['last_name'], 0, 32),
254
-            'shiptostreet'   => substr($use_registration_address_info
255
-                ? $attendee->address()
256
-                : $billing_info['address'], 0, 100),
257
-            'shiptostreet2'  => substr($use_registration_address_info
258
-                ? $attendee->address2() : $billing_info['address2'], 0, 100),
259
-            'shiptocity'     => substr($use_registration_address_info
260
-                ? $attendee->city()
261
-                : $billing_info['city'], 0, 40),
262
-            'state'          => substr($use_registration_address_info
263
-                ? $attendee->state_name()
264
-                : $billing_info['state'], 0, 40),
265
-            'shiptocountry'  => $use_registration_address_info
266
-                ? $attendee->country_ID()
267
-                : $billing_info['country'],
268
-            'shiptozip'      => substr($use_registration_address_info
269
-                ? $attendee->zip()
270
-                : $billing_info['zip'], 0, 20),
271
-            'shiptophonenum' => substr($use_registration_address_info
272
-                ? $attendee->phone()
273
-                : $billing_info['phone'], 0, 20),
274
-        );
275
-        $PaymentDetails = array(
276
-            // Required.  Total amount of order, including shipping, handling, and tax.
277
-            'amt'          => $gateway_formatter->formatCurrency($payment->amount()),
278
-            // Required.  Three-letter currency code.  Default is USD.
279
-            'currencycode' => $payment->currency_code(),
280
-            // Required if you include itemized cart details. (L_AMTn, etc.)
281
-            // Subtotal of items not including S&H, or tax.
282
-            'itemamt'      => $gateway_formatter->formatCurrency($item_amount),//
283
-            // Total shipping costs for the order.  If you specify shippingamt, you must also specify itemamt.
284
-            'shippingamt'  => '',
285
-            // Total handling costs for the order.  If you specify handlingamt, you must also specify itemamt.
286
-            'handlingamt'  => '',
287
-            // Required if you specify itemized cart tax details.
288
-            // Sum of tax for all items on the order.  Total sales tax.
289
-            'taxamt'       => $gateway_formatter->formatCurrency($tax_amount),
290
-            // Description of the order the customer is purchasing.  127 char max.
291
-            'desc'         => $order_description,
292
-            // Free-form field for your own use.  256 char max.
293
-            'custom'       => $primary_registrant ? $primary_registrant->ID() : '',
294
-            // Your own invoice or tracking number
295
-            'invnum'       => wp_generate_password(12, false),// $transaction->ID(),
296
-            // URL for receiving Instant Payment Notifications.  This overrides what your profile is set to use.
297
-            'notifyurl'    => '',
298
-            'buttonsource' => 'EventEspresso_SP',// EE will blow up if you change this
299
-        );
300
-        // Wrap all data arrays into a single, "master" array which will be passed into the class function.
301
-        $PayPalRequestData = array(
302
-            'DPFields'        => $DPFields,
303
-            'CCDetails'       => $CCDetails,
304
-            'PayerInfo'       => $PayerInfo,
305
-            'PayerName'       => $PayerName,
306
-            'BillingAddress'  => $BillingAddress,
307
-            'ShippingAddress' => $ShippingAddress,
308
-            'PaymentDetails'  => $PaymentDetails,
309
-            'OrderItems'      => $order_items,
310
-        );
311
-        $this->_log_clean_request($PayPalRequestData, $payment);
312
-        try {
313
-            $PayPalResult = $this->prep_and_curl_request($PayPalRequestData);
314
-            // remove PCI-sensitive data so it doesn't get stored
315
-            $PayPalResult = $this->_log_clean_response($PayPalResult, $payment);
316
-            if (isset($PayPalResult['L_ERRORCODE0']) && $PayPalResult['L_ERRORCODE0'] === '10002') {
317
-                $message = esc_html__('PayPal did not accept your API username, password, or signature. Please double-check these credentials and if debug mode is on.', 'event_espresso');
318
-            } elseif (isset($PayPalResult['L_LONGMESSAGE0'])) {
319
-                $message = $PayPalResult['L_LONGMESSAGE0'];
320
-            } else {
321
-                $message = $PayPalResult['ACK'];
322
-            }
323
-            if (empty($PayPalResult['RAWRESPONSE'])) {
324
-                $payment->set_status($this->_pay_model->failed_status());
325
-                $payment->set_gateway_response(__('No response received from Paypal Pro', 'event_espresso'));
326
-                $payment->set_details($PayPalResult);
327
-            } else {
328
-                if ($this->_APICallSuccessful($PayPalResult)) {
329
-                    $payment->set_status($this->_pay_model->approved_status());
330
-                } else {
331
-                    $payment->set_status($this->_pay_model->declined_status());
332
-                }
333
-                // make sure we interpret the AMT as a float, not an international string
334
-                // (where periods are thousand separators)
335
-                $payment->set_amount(isset($PayPalResult['AMT']) ? floatval($PayPalResult['AMT']) : 0);
336
-                $payment->set_gateway_response($message);
337
-                $payment->set_txn_id_chq_nmbr(isset($PayPalResult['TRANSACTIONID'])
338
-                    ? $PayPalResult['TRANSACTIONID']
339
-                    : null);
340
-                $primary_registration_code = $primary_registrant instanceof EE_Registration
341
-                    ? $primary_registrant->reg_code()
342
-                    : '';
343
-                $payment->set_extra_accntng($primary_registration_code);
344
-                $payment->set_details($PayPalResult);
345
-            }
346
-        } catch (Exception $e) {
347
-            $payment->set_status($this->_pay_model->failed_status());
348
-            $payment->set_gateway_response($e->getMessage());
349
-        }
350
-        // $payment->set_status( $this->_pay_model->declined_status() );
351
-        // $payment->set_gateway_response( '' );
352
-        return $payment;
353
-    }
354
-
355
-
356
-
357
-    /**
358
-     * CLeans out sensitive CC data and then logs it, and returns the cleaned request
359
-     *
360
-     * @param array       $request
361
-     * @param EEI_Payment $payment
362
-     * @return void
363
-     */
364
-    private function _log_clean_request($request, $payment)
365
-    {
366
-        $cleaned_request_data = $request;
367
-        unset($cleaned_request_data['CCDetails']['acct']);
368
-        unset($cleaned_request_data['CCDetails']['cvv2']);
369
-        unset($cleaned_request_data['CCDetails']['expdate']);
370
-        $this->log(array('Paypal Request' => $cleaned_request_data), $payment);
371
-    }
372
-
373
-
374
-
375
-    /**
376
-     * Cleans the response, logs it, and returns it
377
-     *
378
-     * @param array       $response
379
-     * @param EEI_Payment $payment
380
-     * @return array cleaned
381
-     */
382
-    private function _log_clean_response($response, $payment)
383
-    {
384
-        unset($response['REQUESTDATA']['CREDITCARDTYPE']);
385
-        unset($response['REQUESTDATA']['ACCT']);
386
-        unset($response['REQUESTDATA']['EXPDATE']);
387
-        unset($response['REQUESTDATA']['CVV2']);
388
-        unset($response['RAWREQUEST']);
389
-        $this->log(array('Paypal Response' => $response), $payment);
390
-        return $response;
391
-    }
392
-
393
-
394
-
395
-    /**
396
-     * @param $DataArray
397
-     * @return array
398
-     */
399
-    private function prep_and_curl_request($DataArray)
400
-    {
401
-        // Create empty holders for each portion of the NVP string
402
-        $DPFieldsNVP = '&METHOD=DoDirectPayment&BUTTONSOURCE=AngellEYE_PHP_Class_DDP';
403
-        $CCDetailsNVP = '';
404
-        $PayerInfoNVP = '';
405
-        $PayerNameNVP = '';
406
-        $BillingAddressNVP = '';
407
-        $ShippingAddressNVP = '';
408
-        $PaymentDetailsNVP = '';
409
-        $OrderItemsNVP = '';
410
-        $Secure3DNVP = '';
411
-        // DP Fields
412
-        $DPFields = isset($DataArray['DPFields']) ? $DataArray['DPFields'] : array();
413
-        foreach ($DPFields as $DPFieldsVar => $DPFieldsVal) {
414
-            $DPFieldsNVP .= '&' . strtoupper($DPFieldsVar) . '=' . urlencode($DPFieldsVal);
415
-        }
416
-        // CC Details Fields
417
-        $CCDetails = isset($DataArray['CCDetails']) ? $DataArray['CCDetails'] : array();
418
-        foreach ($CCDetails as $CCDetailsVar => $CCDetailsVal) {
419
-            $CCDetailsNVP .= '&' . strtoupper($CCDetailsVar) . '=' . urlencode($CCDetailsVal);
420
-        }
421
-        // PayerInfo Type Fields
422
-        $PayerInfo = isset($DataArray['PayerInfo']) ? $DataArray['PayerInfo'] : array();
423
-        foreach ($PayerInfo as $PayerInfoVar => $PayerInfoVal) {
424
-            $PayerInfoNVP .= '&' . strtoupper($PayerInfoVar) . '=' . urlencode($PayerInfoVal);
425
-        }
426
-        // Payer Name Fields
427
-        $PayerName = isset($DataArray['PayerName']) ? $DataArray['PayerName'] : array();
428
-        foreach ($PayerName as $PayerNameVar => $PayerNameVal) {
429
-            $PayerNameNVP .= '&' . strtoupper($PayerNameVar) . '=' . urlencode($PayerNameVal);
430
-        }
431
-        // Address Fields (Billing)
432
-        $BillingAddress = isset($DataArray['BillingAddress']) ? $DataArray['BillingAddress'] : array();
433
-        foreach ($BillingAddress as $BillingAddressVar => $BillingAddressVal) {
434
-            $BillingAddressNVP .= '&' . strtoupper($BillingAddressVar) . '=' . urlencode($BillingAddressVal);
435
-        }
436
-        // Payment Details Type Fields
437
-        $PaymentDetails = isset($DataArray['PaymentDetails']) ? $DataArray['PaymentDetails'] : array();
438
-        foreach ($PaymentDetails as $PaymentDetailsVar => $PaymentDetailsVal) {
439
-            $PaymentDetailsNVP .= '&' . strtoupper($PaymentDetailsVar) . '=' . urlencode($PaymentDetailsVal);
440
-        }
441
-        // Payment Details Item Type Fields
442
-        $OrderItems = isset($DataArray['OrderItems']) ? $DataArray['OrderItems'] : array();
443
-        $n = 0;
444
-        foreach ($OrderItems as $OrderItemsVar => $OrderItemsVal) {
445
-            $CurrentItem = $OrderItems[ $OrderItemsVar ];
446
-            foreach ($CurrentItem as $CurrentItemVar => $CurrentItemVal) {
447
-                $OrderItemsNVP .= '&' . strtoupper($CurrentItemVar) . $n . '=' . urlencode($CurrentItemVal);
448
-            }
449
-            $n++;
450
-        }
451
-        // Ship To Address Fields
452
-        $ShippingAddress = isset($DataArray['ShippingAddress']) ? $DataArray['ShippingAddress'] : array();
453
-        foreach ($ShippingAddress as $ShippingAddressVar => $ShippingAddressVal) {
454
-            $ShippingAddressNVP .= '&' . strtoupper($ShippingAddressVar) . '=' . urlencode($ShippingAddressVal);
455
-        }
456
-        // 3D Secure Fields
457
-        $Secure3D = isset($DataArray['Secure3D']) ? $DataArray['Secure3D'] : array();
458
-        foreach ($Secure3D as $Secure3DVar => $Secure3DVal) {
459
-            $Secure3DNVP .= '&' . strtoupper($Secure3DVar) . '=' . urlencode($Secure3DVal);
460
-        }
461
-        // Now that we have each chunk we need to go ahead and append them all together for our entire NVP string
462
-        $NVPRequest = 'USER='
463
-                      . $this->_username
464
-                      . '&PWD='
465
-                      . $this->_password
466
-                      . '&VERSION=64.0'
467
-                      . '&SIGNATURE='
468
-                      . $this->_signature
469
-                      . $DPFieldsNVP
470
-                      . $CCDetailsNVP
471
-                      . $PayerInfoNVP
472
-                      . $PayerNameNVP
473
-                      . $BillingAddressNVP
474
-                      . $PaymentDetailsNVP
475
-                      . $OrderItemsNVP
476
-                      . $ShippingAddressNVP
477
-                      . $Secure3DNVP;
478
-        $NVPResponse = $this->_CURLRequest($NVPRequest);
479
-        $NVPRequestArray = $this->_NVPToArray($NVPRequest);
480
-        $NVPResponseArray = $this->_NVPToArray($NVPResponse);
481
-        $Errors = $this->_GetErrors($NVPResponseArray);
482
-        $NVPResponseArray['ERRORS'] = $Errors;
483
-        $NVPResponseArray['REQUESTDATA'] = $NVPRequestArray;
484
-        $NVPResponseArray['RAWREQUEST'] = $NVPRequest;
485
-        $NVPResponseArray['RAWRESPONSE'] = $NVPResponse;
486
-        return $NVPResponseArray;
487
-    }
488
-
489
-
490
-
491
-    /**
492
-     * @param $Request
493
-     * @return mixed
494
-     */
495
-    private function _CURLRequest($Request)
496
-    {
497
-        $EndPointURL = $this->_debug_mode ? 'https://api-3t.sandbox.paypal.com/nvp' : 'https://api-3t.paypal.com/nvp';
498
-        $curl = curl_init();
499
-        curl_setopt($curl, CURLOPT_VERBOSE, apply_filters('FHEE__EEG_Paypal_Pro__CurlRequest__CURLOPT_VERBOSE', true));
500
-        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
501
-        curl_setopt($curl, CURLOPT_TIMEOUT, 60);
502
-        curl_setopt($curl, CURLOPT_URL, $EndPointURL);
503
-        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
504
-        curl_setopt($curl, CURLOPT_POSTFIELDS, $Request);
505
-        curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
506
-        // execute the curl POST
507
-        $Response = curl_exec($curl);
508
-        curl_close($curl);
509
-        return $Response;
510
-    }
511
-
512
-
513
-
514
-    /**
515
-     * @param $NVPString
516
-     * @return array
517
-     */
518
-    private function _NVPToArray($NVPString)
519
-    {
520
-        // prepare responses into array
521
-        $proArray = array();
522
-        while (strlen($NVPString)) {
523
-            // name
524
-            $keypos = strpos($NVPString, '=');
525
-            $keyval = substr($NVPString, 0, $keypos);
526
-            // value
527
-            $valuepos = strpos($NVPString, '&') ? strpos($NVPString, '&') : strlen($NVPString);
528
-            $valval = substr($NVPString, $keypos + 1, $valuepos - $keypos - 1);
529
-            // decoding the response
530
-            $proArray[ $keyval ] = urldecode($valval);
531
-            $NVPString = substr($NVPString, $valuepos + 1, strlen($NVPString));
532
-        }
533
-        return $proArray;
534
-    }
535
-
536
-
537
-
538
-    /**
539
-     * @param array $PayPalResult
540
-     * @return bool
541
-     */
542
-    private function _APICallSuccessful($PayPalResult)
543
-    {
544
-        $approved = false;
545
-        // check main response message from PayPal
546
-        if (isset($PayPalResult['ACK']) && ! empty($PayPalResult['ACK'])) {
547
-            $ack = strtoupper($PayPalResult['ACK']);
548
-            $approved = ($ack == 'SUCCESS' || $ack == 'SUCCESSWITHWARNING' || $ack == 'PARTIALSUCCESS') ? true : false;
549
-        }
550
-        return $approved;
551
-    }
552
-
553
-
554
-
555
-    /**
556
-     * @param $DataArray
557
-     * @return array
558
-     */
559
-    private function _GetErrors($DataArray)
560
-    {
561
-        $Errors = array();
562
-        $n = 0;
563
-        while (isset($DataArray[ 'L_ERRORCODE' . $n . '' ])) {
564
-            $LErrorCode = isset($DataArray[ 'L_ERRORCODE' . $n . '' ]) ? $DataArray[ 'L_ERRORCODE' . $n . '' ] : '';
565
-            $LShortMessage = isset($DataArray[ 'L_SHORTMESSAGE' . $n . '' ])
566
-                ? $DataArray[ 'L_SHORTMESSAGE' . $n . '' ]
567
-                : '';
568
-            $LLongMessage = isset($DataArray[ 'L_LONGMESSAGE' . $n . '' ])
569
-                ? $DataArray[ 'L_LONGMESSAGE' . $n . '' ]
570
-                : '';
571
-            $LSeverityCode = isset($DataArray[ 'L_SEVERITYCODE' . $n . '' ])
572
-                ? $DataArray[ 'L_SEVERITYCODE' . $n . '' ]
573
-                : '';
574
-            $CurrentItem = array(
575
-                'L_ERRORCODE'    => $LErrorCode,
576
-                'L_SHORTMESSAGE' => $LShortMessage,
577
-                'L_LONGMESSAGE'  => $LLongMessage,
578
-                'L_SEVERITYCODE' => $LSeverityCode,
579
-            );
580
-            array_push($Errors, $CurrentItem);
581
-            $n++;
582
-        }
583
-        return $Errors;
584
-    }
585
-
586
-
587
-
588
-    /**
589
-     *        nothing to see here...  move along....
590
-     *
591
-     * @access protected
592
-     * @param $Errors
593
-     * @return string
594
-     */
595
-    private function _DisplayErrors($Errors)
596
-    {
597
-        $error = '';
598
-        foreach ($Errors as $ErrorVar => $ErrorVal) {
599
-            $CurrentError = $Errors[ $ErrorVar ];
600
-            foreach ($CurrentError as $CurrentErrorVar => $CurrentErrorVal) {
601
-                $CurrentVarName = '';
602
-                if ($CurrentErrorVar == 'L_ERRORCODE') {
603
-                    $CurrentVarName = 'Error Code';
604
-                } elseif ($CurrentErrorVar == 'L_SHORTMESSAGE') {
605
-                    $CurrentVarName = 'Short Message';
606
-                } elseif ($CurrentErrorVar == 'L_LONGMESSAGE') {
607
-                    $CurrentVarName = 'Long Message';
608
-                } elseif ($CurrentErrorVar == 'L_SEVERITYCODE') {
609
-                    $CurrentVarName = 'Severity Code';
610
-                }
611
-                $error .= '<br />' . $CurrentVarName . ': ' . $CurrentErrorVal;
612
-            }
613
-        }
614
-        return $error;
615
-    }
14
+	/**
15
+	 * @var $_paypal_api_username string
16
+	 */
17
+	protected $_username = null;
18
+
19
+	/**
20
+	 * @var $_password string
21
+	 */
22
+	protected $_password = null;
23
+
24
+	/**
25
+	 * @var $_signature string
26
+	 */
27
+	protected $_signature = null;
28
+
29
+	/**
30
+	 * @var $_credit_card_types array with the keys for credit card types accepted on this account
31
+	 */
32
+	protected $_credit_card_types    = null;
33
+
34
+	protected $_currencies_supported = array(
35
+		'USD',
36
+		'GBP',
37
+		'CAD',
38
+		'AUD',
39
+		'BRL',
40
+		'CHF',
41
+		'CZK',
42
+		'DKK',
43
+		'EUR',
44
+		'HKD',
45
+		'HUF',
46
+		'ILS',
47
+		'JPY',
48
+		'MXN',
49
+		'MYR',
50
+		'NOK',
51
+		'NZD',
52
+		'PHP',
53
+		'PLN',
54
+		'SEK',
55
+		'SGD',
56
+		'THB',
57
+		'TRY',
58
+		'TWD',
59
+		'RUB',
60
+		'INR',
61
+	);
62
+
63
+
64
+
65
+	/**
66
+	 * @param EEI_Payment $payment
67
+	 * @param array       $billing_info {
68
+	 * @type string $credit_card
69
+	 * @type string $credit_card_type
70
+	 * @type string $exp_month always 2 characters
71
+	 * @type string $exp_year always 4 characters
72
+	 * @type string $cvv
73
+	 * }
74
+	 * @see      parent::do_direct_payment for more info
75
+	 * @return EE_Payment|EEI_Payment
76
+	 * @throws EE_Error
77
+	 */
78
+	public function do_direct_payment($payment, $billing_info = null)
79
+	{
80
+		$transaction = $payment->transaction();
81
+		if (! $transaction instanceof EEI_Transaction) {
82
+			throw new EE_Error(
83
+				esc_html__('No transaction for payment while paying with PayPal Pro.', 'event_espresso')
84
+			);
85
+		}
86
+		$primary_registrant = $transaction->primary_registration();
87
+		if (! $primary_registrant instanceof EEI_Registration) {
88
+			throw new EE_Error(
89
+				esc_html__(
90
+					'No primary registration on transaction while paying with PayPal Pro.',
91
+					'event_espresso'
92
+				)
93
+			);
94
+		}
95
+		$attendee = $primary_registrant->attendee();
96
+		if (! $attendee instanceof EEI_Attendee) {
97
+			throw new EE_Error(
98
+				esc_html__(
99
+					'No attendee on primary registration while paying with PayPal Pro.',
100
+					'event_espresso'
101
+				)
102
+			);
103
+		}
104
+		$gateway_formatter = $this->_get_gateway_formatter();
105
+		$order_description = substr($gateway_formatter->formatOrderDescription($payment), 0, 127);
106
+		// charge for the full amount. Show itemized list
107
+		if ($this->_money->compare_floats($payment->amount(), $transaction->total(), '==')) {
108
+			$item_num = 1;
109
+			$total_line_item = $transaction->total_line_item();
110
+			$order_items = array();
111
+			foreach ($total_line_item->get_items() as $line_item) {
112
+				// ignore line items with a quantity of 0
113
+				if ($line_item->quantity() == 0) {
114
+					continue;
115
+				}
116
+				// For percent items, whose unit_price is 0, use the total instead.
117
+				if ($line_item->is_percent()) {
118
+					$unit_price = $line_item->total();
119
+					$line_item_quantity = 1;
120
+				} else {
121
+					$unit_price = $line_item->unit_price();
122
+					$line_item_quantity = $line_item->quantity();
123
+				}
124
+				$item = array(
125
+					// Item Name.  127 char max.
126
+					'l_name'                 => substr(
127
+						$gateway_formatter->formatLineItemName($line_item, $payment),
128
+						0,
129
+						127
130
+					),
131
+					// Item description.  127 char max.
132
+					'l_desc'                 => substr(
133
+						$gateway_formatter->formatLineItemDesc($line_item, $payment),
134
+						0,
135
+						127
136
+					),
137
+					// Cost of individual item.
138
+					'l_amt'                  => $unit_price,
139
+					// Item Number.  127 char max.
140
+					'l_number'               => $item_num++,
141
+					// Item quantity.  Must be any positive integer.
142
+					'l_qty'                  => $line_item_quantity,
143
+					// Item's sales tax amount.
144
+					'l_taxamt'               => '',
145
+					// eBay auction number of item.
146
+					'l_ebayitemnumber'       => '',
147
+					// eBay transaction ID of purchased item.
148
+					'l_ebayitemauctiontxnid' => '',
149
+					// eBay order ID for the item.
150
+					'l_ebayitemorderid'      => '',
151
+				);
152
+				// add to array of all items
153
+				array_push($order_items, $item);
154
+			}
155
+			$item_amount = $total_line_item->get_items_total();
156
+			$tax_amount = $total_line_item->get_total_tax();
157
+		} else {
158
+			$order_items = array();
159
+			$item_amount = $payment->amount();
160
+			$tax_amount = 0;
161
+			array_push($order_items, array(
162
+				// Item Name.  127 char max.
163
+				'l_name'   => substr(
164
+					$gateway_formatter->formatPartialPaymentLineItemName($payment),
165
+					0,
166
+					127
167
+				),
168
+				// Item description.  127 char max.
169
+				'l_desc'   => substr(
170
+					$gateway_formatter->formatPartialPaymentLineItemDesc($payment),
171
+					0,
172
+					127
173
+				),
174
+				// Cost of individual item.
175
+				'l_amt'    => $payment->amount(),
176
+				// Item Number.  127 char max.
177
+				'l_number' => 1,
178
+				// Item quantity.  Must be any positive integer.
179
+				'l_qty'    => 1,
180
+			));
181
+		}
182
+		// Populate data arrays with order data.
183
+		$DPFields = array(
184
+			// How you want to obtain payment ?
185
+			// Authorization indicates the payment is a basic auth subject to settlement with Auth & Capture.
186
+			// Sale indicates that this is a final sale for which you are requesting payment.  Default is Sale.
187
+			'paymentaction'    => 'Sale',
188
+			// Required.  IP address of the payer's browser.
189
+			'ipaddress'        => $_SERVER['REMOTE_ADDR'],
190
+			// Flag to determine whether you want the results returned by FMF.  1 or 0.  Default is 0.
191
+			'returnfmfdetails' => '1',
192
+		);
193
+		$CCDetails = array(
194
+			// Required. Type of credit card.  Visa, MasterCard, Discover, Amex, Maestro, Solo.
195
+			// If Maestro or Solo, the currency code must be GBP.
196
+			//  In addition, either start date or issue number must be specified.
197
+			'creditcardtype' => $billing_info['credit_card_type'],
198
+			// Required.  Credit card number.  No spaces or punctuation.
199
+			'acct'           => $billing_info['credit_card'],
200
+			// Required.  Credit card expiration date.  Format is MMYYYY
201
+			'expdate'        => $billing_info['exp_month'] . $billing_info['exp_year'],
202
+			// Requirements determined by your PayPal account settings.  Security digits for credit card.
203
+			'cvv2'           => $billing_info['cvv'],
204
+		);
205
+		$PayerInfo = array(
206
+			// Email address of payer.
207
+			'email'       => $billing_info['email'],
208
+			// Unique PayPal customer ID for payer.
209
+			'payerid'     => '',
210
+			// Status of payer.  Values are verified or unverified
211
+			'payerstatus' => '',
212
+			// Payer's business name.
213
+			'business'    => '',
214
+		);
215
+		$PayerName = array(
216
+			// Payer's salutation.  20 char max.
217
+			'salutation' => '',
218
+			// Payer's first name.  25 char max.
219
+			'firstname'  => substr($billing_info['first_name'], 0, 25),
220
+			// Payer's middle name.  25 char max.
221
+			'middlename' => '',
222
+			// Payer's last name.  25 char max.
223
+			'lastname'   => substr($billing_info['last_name'], 0, 25),
224
+			// Payer's suffix.  12 char max.
225
+			'suffix'     => '',
226
+		);
227
+		$BillingAddress = array(
228
+			// Required.  First street address.
229
+			'street'      => $billing_info['address'],
230
+			// Second street address.
231
+			'street2'     => $billing_info['address2'],
232
+			// Required.  Name of City.
233
+			'city'        => $billing_info['city'],
234
+			// Required. Name of State or Province.
235
+			'state'       => substr($billing_info['state'], 0, 40),
236
+			// Required.  Country code.
237
+			'countrycode' => $billing_info['country'],
238
+			// Required.  Postal code of payer.
239
+			'zip'         => $billing_info['zip'],
240
+		);
241
+		// check if the registration info contains the needed fields for paypal pro
242
+		// (see https://developer.paypal.com/docs/classic/api/merchant/DoDirectPayment_API_Operation_NVP/)
243
+		if ($attendee->address() && $attendee->city() && $attendee->country_ID()) {
244
+			$use_registration_address_info = true;
245
+		} else {
246
+			$use_registration_address_info = false;
247
+		}
248
+		// so if the attendee has enough data to fill out PayPal Pro's shipping info, use it.
249
+		// If not, use the billing info again
250
+		$ShippingAddress = array(
251
+			'shiptoname'     => substr($use_registration_address_info
252
+				? $attendee->full_name()
253
+				: $billing_info['first_name'] . ' ' . $billing_info['last_name'], 0, 32),
254
+			'shiptostreet'   => substr($use_registration_address_info
255
+				? $attendee->address()
256
+				: $billing_info['address'], 0, 100),
257
+			'shiptostreet2'  => substr($use_registration_address_info
258
+				? $attendee->address2() : $billing_info['address2'], 0, 100),
259
+			'shiptocity'     => substr($use_registration_address_info
260
+				? $attendee->city()
261
+				: $billing_info['city'], 0, 40),
262
+			'state'          => substr($use_registration_address_info
263
+				? $attendee->state_name()
264
+				: $billing_info['state'], 0, 40),
265
+			'shiptocountry'  => $use_registration_address_info
266
+				? $attendee->country_ID()
267
+				: $billing_info['country'],
268
+			'shiptozip'      => substr($use_registration_address_info
269
+				? $attendee->zip()
270
+				: $billing_info['zip'], 0, 20),
271
+			'shiptophonenum' => substr($use_registration_address_info
272
+				? $attendee->phone()
273
+				: $billing_info['phone'], 0, 20),
274
+		);
275
+		$PaymentDetails = array(
276
+			// Required.  Total amount of order, including shipping, handling, and tax.
277
+			'amt'          => $gateway_formatter->formatCurrency($payment->amount()),
278
+			// Required.  Three-letter currency code.  Default is USD.
279
+			'currencycode' => $payment->currency_code(),
280
+			// Required if you include itemized cart details. (L_AMTn, etc.)
281
+			// Subtotal of items not including S&H, or tax.
282
+			'itemamt'      => $gateway_formatter->formatCurrency($item_amount),//
283
+			// Total shipping costs for the order.  If you specify shippingamt, you must also specify itemamt.
284
+			'shippingamt'  => '',
285
+			// Total handling costs for the order.  If you specify handlingamt, you must also specify itemamt.
286
+			'handlingamt'  => '',
287
+			// Required if you specify itemized cart tax details.
288
+			// Sum of tax for all items on the order.  Total sales tax.
289
+			'taxamt'       => $gateway_formatter->formatCurrency($tax_amount),
290
+			// Description of the order the customer is purchasing.  127 char max.
291
+			'desc'         => $order_description,
292
+			// Free-form field for your own use.  256 char max.
293
+			'custom'       => $primary_registrant ? $primary_registrant->ID() : '',
294
+			// Your own invoice or tracking number
295
+			'invnum'       => wp_generate_password(12, false),// $transaction->ID(),
296
+			// URL for receiving Instant Payment Notifications.  This overrides what your profile is set to use.
297
+			'notifyurl'    => '',
298
+			'buttonsource' => 'EventEspresso_SP',// EE will blow up if you change this
299
+		);
300
+		// Wrap all data arrays into a single, "master" array which will be passed into the class function.
301
+		$PayPalRequestData = array(
302
+			'DPFields'        => $DPFields,
303
+			'CCDetails'       => $CCDetails,
304
+			'PayerInfo'       => $PayerInfo,
305
+			'PayerName'       => $PayerName,
306
+			'BillingAddress'  => $BillingAddress,
307
+			'ShippingAddress' => $ShippingAddress,
308
+			'PaymentDetails'  => $PaymentDetails,
309
+			'OrderItems'      => $order_items,
310
+		);
311
+		$this->_log_clean_request($PayPalRequestData, $payment);
312
+		try {
313
+			$PayPalResult = $this->prep_and_curl_request($PayPalRequestData);
314
+			// remove PCI-sensitive data so it doesn't get stored
315
+			$PayPalResult = $this->_log_clean_response($PayPalResult, $payment);
316
+			if (isset($PayPalResult['L_ERRORCODE0']) && $PayPalResult['L_ERRORCODE0'] === '10002') {
317
+				$message = esc_html__('PayPal did not accept your API username, password, or signature. Please double-check these credentials and if debug mode is on.', 'event_espresso');
318
+			} elseif (isset($PayPalResult['L_LONGMESSAGE0'])) {
319
+				$message = $PayPalResult['L_LONGMESSAGE0'];
320
+			} else {
321
+				$message = $PayPalResult['ACK'];
322
+			}
323
+			if (empty($PayPalResult['RAWRESPONSE'])) {
324
+				$payment->set_status($this->_pay_model->failed_status());
325
+				$payment->set_gateway_response(__('No response received from Paypal Pro', 'event_espresso'));
326
+				$payment->set_details($PayPalResult);
327
+			} else {
328
+				if ($this->_APICallSuccessful($PayPalResult)) {
329
+					$payment->set_status($this->_pay_model->approved_status());
330
+				} else {
331
+					$payment->set_status($this->_pay_model->declined_status());
332
+				}
333
+				// make sure we interpret the AMT as a float, not an international string
334
+				// (where periods are thousand separators)
335
+				$payment->set_amount(isset($PayPalResult['AMT']) ? floatval($PayPalResult['AMT']) : 0);
336
+				$payment->set_gateway_response($message);
337
+				$payment->set_txn_id_chq_nmbr(isset($PayPalResult['TRANSACTIONID'])
338
+					? $PayPalResult['TRANSACTIONID']
339
+					: null);
340
+				$primary_registration_code = $primary_registrant instanceof EE_Registration
341
+					? $primary_registrant->reg_code()
342
+					: '';
343
+				$payment->set_extra_accntng($primary_registration_code);
344
+				$payment->set_details($PayPalResult);
345
+			}
346
+		} catch (Exception $e) {
347
+			$payment->set_status($this->_pay_model->failed_status());
348
+			$payment->set_gateway_response($e->getMessage());
349
+		}
350
+		// $payment->set_status( $this->_pay_model->declined_status() );
351
+		// $payment->set_gateway_response( '' );
352
+		return $payment;
353
+	}
354
+
355
+
356
+
357
+	/**
358
+	 * CLeans out sensitive CC data and then logs it, and returns the cleaned request
359
+	 *
360
+	 * @param array       $request
361
+	 * @param EEI_Payment $payment
362
+	 * @return void
363
+	 */
364
+	private function _log_clean_request($request, $payment)
365
+	{
366
+		$cleaned_request_data = $request;
367
+		unset($cleaned_request_data['CCDetails']['acct']);
368
+		unset($cleaned_request_data['CCDetails']['cvv2']);
369
+		unset($cleaned_request_data['CCDetails']['expdate']);
370
+		$this->log(array('Paypal Request' => $cleaned_request_data), $payment);
371
+	}
372
+
373
+
374
+
375
+	/**
376
+	 * Cleans the response, logs it, and returns it
377
+	 *
378
+	 * @param array       $response
379
+	 * @param EEI_Payment $payment
380
+	 * @return array cleaned
381
+	 */
382
+	private function _log_clean_response($response, $payment)
383
+	{
384
+		unset($response['REQUESTDATA']['CREDITCARDTYPE']);
385
+		unset($response['REQUESTDATA']['ACCT']);
386
+		unset($response['REQUESTDATA']['EXPDATE']);
387
+		unset($response['REQUESTDATA']['CVV2']);
388
+		unset($response['RAWREQUEST']);
389
+		$this->log(array('Paypal Response' => $response), $payment);
390
+		return $response;
391
+	}
392
+
393
+
394
+
395
+	/**
396
+	 * @param $DataArray
397
+	 * @return array
398
+	 */
399
+	private function prep_and_curl_request($DataArray)
400
+	{
401
+		// Create empty holders for each portion of the NVP string
402
+		$DPFieldsNVP = '&METHOD=DoDirectPayment&BUTTONSOURCE=AngellEYE_PHP_Class_DDP';
403
+		$CCDetailsNVP = '';
404
+		$PayerInfoNVP = '';
405
+		$PayerNameNVP = '';
406
+		$BillingAddressNVP = '';
407
+		$ShippingAddressNVP = '';
408
+		$PaymentDetailsNVP = '';
409
+		$OrderItemsNVP = '';
410
+		$Secure3DNVP = '';
411
+		// DP Fields
412
+		$DPFields = isset($DataArray['DPFields']) ? $DataArray['DPFields'] : array();
413
+		foreach ($DPFields as $DPFieldsVar => $DPFieldsVal) {
414
+			$DPFieldsNVP .= '&' . strtoupper($DPFieldsVar) . '=' . urlencode($DPFieldsVal);
415
+		}
416
+		// CC Details Fields
417
+		$CCDetails = isset($DataArray['CCDetails']) ? $DataArray['CCDetails'] : array();
418
+		foreach ($CCDetails as $CCDetailsVar => $CCDetailsVal) {
419
+			$CCDetailsNVP .= '&' . strtoupper($CCDetailsVar) . '=' . urlencode($CCDetailsVal);
420
+		}
421
+		// PayerInfo Type Fields
422
+		$PayerInfo = isset($DataArray['PayerInfo']) ? $DataArray['PayerInfo'] : array();
423
+		foreach ($PayerInfo as $PayerInfoVar => $PayerInfoVal) {
424
+			$PayerInfoNVP .= '&' . strtoupper($PayerInfoVar) . '=' . urlencode($PayerInfoVal);
425
+		}
426
+		// Payer Name Fields
427
+		$PayerName = isset($DataArray['PayerName']) ? $DataArray['PayerName'] : array();
428
+		foreach ($PayerName as $PayerNameVar => $PayerNameVal) {
429
+			$PayerNameNVP .= '&' . strtoupper($PayerNameVar) . '=' . urlencode($PayerNameVal);
430
+		}
431
+		// Address Fields (Billing)
432
+		$BillingAddress = isset($DataArray['BillingAddress']) ? $DataArray['BillingAddress'] : array();
433
+		foreach ($BillingAddress as $BillingAddressVar => $BillingAddressVal) {
434
+			$BillingAddressNVP .= '&' . strtoupper($BillingAddressVar) . '=' . urlencode($BillingAddressVal);
435
+		}
436
+		// Payment Details Type Fields
437
+		$PaymentDetails = isset($DataArray['PaymentDetails']) ? $DataArray['PaymentDetails'] : array();
438
+		foreach ($PaymentDetails as $PaymentDetailsVar => $PaymentDetailsVal) {
439
+			$PaymentDetailsNVP .= '&' . strtoupper($PaymentDetailsVar) . '=' . urlencode($PaymentDetailsVal);
440
+		}
441
+		// Payment Details Item Type Fields
442
+		$OrderItems = isset($DataArray['OrderItems']) ? $DataArray['OrderItems'] : array();
443
+		$n = 0;
444
+		foreach ($OrderItems as $OrderItemsVar => $OrderItemsVal) {
445
+			$CurrentItem = $OrderItems[ $OrderItemsVar ];
446
+			foreach ($CurrentItem as $CurrentItemVar => $CurrentItemVal) {
447
+				$OrderItemsNVP .= '&' . strtoupper($CurrentItemVar) . $n . '=' . urlencode($CurrentItemVal);
448
+			}
449
+			$n++;
450
+		}
451
+		// Ship To Address Fields
452
+		$ShippingAddress = isset($DataArray['ShippingAddress']) ? $DataArray['ShippingAddress'] : array();
453
+		foreach ($ShippingAddress as $ShippingAddressVar => $ShippingAddressVal) {
454
+			$ShippingAddressNVP .= '&' . strtoupper($ShippingAddressVar) . '=' . urlencode($ShippingAddressVal);
455
+		}
456
+		// 3D Secure Fields
457
+		$Secure3D = isset($DataArray['Secure3D']) ? $DataArray['Secure3D'] : array();
458
+		foreach ($Secure3D as $Secure3DVar => $Secure3DVal) {
459
+			$Secure3DNVP .= '&' . strtoupper($Secure3DVar) . '=' . urlencode($Secure3DVal);
460
+		}
461
+		// Now that we have each chunk we need to go ahead and append them all together for our entire NVP string
462
+		$NVPRequest = 'USER='
463
+					  . $this->_username
464
+					  . '&PWD='
465
+					  . $this->_password
466
+					  . '&VERSION=64.0'
467
+					  . '&SIGNATURE='
468
+					  . $this->_signature
469
+					  . $DPFieldsNVP
470
+					  . $CCDetailsNVP
471
+					  . $PayerInfoNVP
472
+					  . $PayerNameNVP
473
+					  . $BillingAddressNVP
474
+					  . $PaymentDetailsNVP
475
+					  . $OrderItemsNVP
476
+					  . $ShippingAddressNVP
477
+					  . $Secure3DNVP;
478
+		$NVPResponse = $this->_CURLRequest($NVPRequest);
479
+		$NVPRequestArray = $this->_NVPToArray($NVPRequest);
480
+		$NVPResponseArray = $this->_NVPToArray($NVPResponse);
481
+		$Errors = $this->_GetErrors($NVPResponseArray);
482
+		$NVPResponseArray['ERRORS'] = $Errors;
483
+		$NVPResponseArray['REQUESTDATA'] = $NVPRequestArray;
484
+		$NVPResponseArray['RAWREQUEST'] = $NVPRequest;
485
+		$NVPResponseArray['RAWRESPONSE'] = $NVPResponse;
486
+		return $NVPResponseArray;
487
+	}
488
+
489
+
490
+
491
+	/**
492
+	 * @param $Request
493
+	 * @return mixed
494
+	 */
495
+	private function _CURLRequest($Request)
496
+	{
497
+		$EndPointURL = $this->_debug_mode ? 'https://api-3t.sandbox.paypal.com/nvp' : 'https://api-3t.paypal.com/nvp';
498
+		$curl = curl_init();
499
+		curl_setopt($curl, CURLOPT_VERBOSE, apply_filters('FHEE__EEG_Paypal_Pro__CurlRequest__CURLOPT_VERBOSE', true));
500
+		curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
501
+		curl_setopt($curl, CURLOPT_TIMEOUT, 60);
502
+		curl_setopt($curl, CURLOPT_URL, $EndPointURL);
503
+		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
504
+		curl_setopt($curl, CURLOPT_POSTFIELDS, $Request);
505
+		curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
506
+		// execute the curl POST
507
+		$Response = curl_exec($curl);
508
+		curl_close($curl);
509
+		return $Response;
510
+	}
511
+
512
+
513
+
514
+	/**
515
+	 * @param $NVPString
516
+	 * @return array
517
+	 */
518
+	private function _NVPToArray($NVPString)
519
+	{
520
+		// prepare responses into array
521
+		$proArray = array();
522
+		while (strlen($NVPString)) {
523
+			// name
524
+			$keypos = strpos($NVPString, '=');
525
+			$keyval = substr($NVPString, 0, $keypos);
526
+			// value
527
+			$valuepos = strpos($NVPString, '&') ? strpos($NVPString, '&') : strlen($NVPString);
528
+			$valval = substr($NVPString, $keypos + 1, $valuepos - $keypos - 1);
529
+			// decoding the response
530
+			$proArray[ $keyval ] = urldecode($valval);
531
+			$NVPString = substr($NVPString, $valuepos + 1, strlen($NVPString));
532
+		}
533
+		return $proArray;
534
+	}
535
+
536
+
537
+
538
+	/**
539
+	 * @param array $PayPalResult
540
+	 * @return bool
541
+	 */
542
+	private function _APICallSuccessful($PayPalResult)
543
+	{
544
+		$approved = false;
545
+		// check main response message from PayPal
546
+		if (isset($PayPalResult['ACK']) && ! empty($PayPalResult['ACK'])) {
547
+			$ack = strtoupper($PayPalResult['ACK']);
548
+			$approved = ($ack == 'SUCCESS' || $ack == 'SUCCESSWITHWARNING' || $ack == 'PARTIALSUCCESS') ? true : false;
549
+		}
550
+		return $approved;
551
+	}
552
+
553
+
554
+
555
+	/**
556
+	 * @param $DataArray
557
+	 * @return array
558
+	 */
559
+	private function _GetErrors($DataArray)
560
+	{
561
+		$Errors = array();
562
+		$n = 0;
563
+		while (isset($DataArray[ 'L_ERRORCODE' . $n . '' ])) {
564
+			$LErrorCode = isset($DataArray[ 'L_ERRORCODE' . $n . '' ]) ? $DataArray[ 'L_ERRORCODE' . $n . '' ] : '';
565
+			$LShortMessage = isset($DataArray[ 'L_SHORTMESSAGE' . $n . '' ])
566
+				? $DataArray[ 'L_SHORTMESSAGE' . $n . '' ]
567
+				: '';
568
+			$LLongMessage = isset($DataArray[ 'L_LONGMESSAGE' . $n . '' ])
569
+				? $DataArray[ 'L_LONGMESSAGE' . $n . '' ]
570
+				: '';
571
+			$LSeverityCode = isset($DataArray[ 'L_SEVERITYCODE' . $n . '' ])
572
+				? $DataArray[ 'L_SEVERITYCODE' . $n . '' ]
573
+				: '';
574
+			$CurrentItem = array(
575
+				'L_ERRORCODE'    => $LErrorCode,
576
+				'L_SHORTMESSAGE' => $LShortMessage,
577
+				'L_LONGMESSAGE'  => $LLongMessage,
578
+				'L_SEVERITYCODE' => $LSeverityCode,
579
+			);
580
+			array_push($Errors, $CurrentItem);
581
+			$n++;
582
+		}
583
+		return $Errors;
584
+	}
585
+
586
+
587
+
588
+	/**
589
+	 *        nothing to see here...  move along....
590
+	 *
591
+	 * @access protected
592
+	 * @param $Errors
593
+	 * @return string
594
+	 */
595
+	private function _DisplayErrors($Errors)
596
+	{
597
+		$error = '';
598
+		foreach ($Errors as $ErrorVar => $ErrorVal) {
599
+			$CurrentError = $Errors[ $ErrorVar ];
600
+			foreach ($CurrentError as $CurrentErrorVar => $CurrentErrorVal) {
601
+				$CurrentVarName = '';
602
+				if ($CurrentErrorVar == 'L_ERRORCODE') {
603
+					$CurrentVarName = 'Error Code';
604
+				} elseif ($CurrentErrorVar == 'L_SHORTMESSAGE') {
605
+					$CurrentVarName = 'Short Message';
606
+				} elseif ($CurrentErrorVar == 'L_LONGMESSAGE') {
607
+					$CurrentVarName = 'Long Message';
608
+				} elseif ($CurrentErrorVar == 'L_SEVERITYCODE') {
609
+					$CurrentVarName = 'Severity Code';
610
+				}
611
+				$error .= '<br />' . $CurrentVarName . ': ' . $CurrentErrorVal;
612
+			}
613
+		}
614
+		return $error;
615
+	}
616 616
 }
Please login to merge, or discard this patch.
admin_pages/messages/Messages_Template_List_Table.class.php 2 patches
Indentation   +350 added lines, -350 removed lines patch added patch discarded remove patch
@@ -13,354 +13,354 @@
 block discarded – undo
13 13
 {
14 14
 
15 15
 
16
-    /**
17
-     * @return Messages_Admin_Page
18
-     */
19
-    public function get_admin_page()
20
-    {
21
-        return $this->_admin_page;
22
-    }
23
-
24
-
25
-    /**
26
-     * Setup data object
27
-     */
28
-    protected function _setup_data()
29
-    {
30
-        $this->_data = $this->get_admin_page()->get_message_templates(
31
-            $this->_per_page,
32
-            $this->_view,
33
-            false
34
-        );
35
-        $this->_all_data_count = $this->get_admin_page()->get_message_templates(
36
-            $this->_per_page,
37
-            $this->_view,
38
-            true,
39
-            true
40
-        );
41
-    }
42
-
43
-
44
-    /**
45
-     * Set internal properties
46
-     */
47
-    protected function _set_properties()
48
-    {
49
-        $this->_wp_list_args = array(
50
-            'singular' => esc_html__('Message Template Group', 'event_espresso'),
51
-            'plural'   => esc_html__('Message Template', 'event_espresso'),
52
-            'ajax'     => true, // for now,
53
-            'screen'   => $this->get_admin_page()->get_current_screen()->id,
54
-        );
55
-        $this->_columns = array(
56
-            // 'cb' => '<input type="checkbox" />', //no deleting default (global) templates!
57
-            'message_type' => esc_html__('Message Type', 'event_espresso'),
58
-            'messenger'    => esc_html__('Messenger', 'event_espresso'),
59
-            'description'  => esc_html__('Description', 'event_espresso'),
60
-        );
61
-
62
-        $this->_sortable_columns = array(
63
-            'messenger' => array('MTP_messenger' => true),
64
-        );
65
-
66
-        $this->_hidden_columns = array();
67
-    }
68
-
69
-
70
-    /**
71
-     * Overriding the single_row method from parent to verify whether the $item has an accessible
72
-     * message_type or messenger object before generating the row.
73
-     *
74
-     * @param EE_Message_Template_Group $item
75
-     * @return string
76
-     * @throws EE_Error
77
-     */
78
-    public function single_row($item)
79
-    {
80
-        $message_type = $item->message_type_obj();
81
-        $messenger = $item->messenger_obj();
82
-
83
-        if (! $message_type instanceof EE_message_type || ! $messenger instanceof EE_messenger) {
84
-            echo '';
85
-            return;
86
-        }
87
-
88
-        parent::single_row($item);
89
-    }
90
-
91
-
92
-    /**
93
-     * @return array
94
-     * @throws EE_Error
95
-     */
96
-    protected function _get_table_filters()
97
-    {
98
-        $filters = array();
99
-
100
-        // get select inputs
101
-        $select_inputs = array(
102
-            $this->_get_messengers_dropdown_filter(),
103
-            $this->_get_message_types_dropdown_filter(),
104
-        );
105
-
106
-        // set filters to select inputs if they aren't empty
107
-        foreach ($select_inputs as $select_input) {
108
-            if ($select_input) {
109
-                $filters[] = $select_input;
110
-            }
111
-        }
112
-        return $filters;
113
-    }
114
-
115
-    /**
116
-     * We're just removing the search box for message templates, not needed.
117
-     *
118
-     * @param string $text
119
-     * @param string $input_id
120
-     * @return string ;
121
-     */
122
-    public function search_box($text, $input_id)
123
-    {
124
-        return '';
125
-    }
126
-
127
-
128
-    /**
129
-     * Add counts to the _views property
130
-     */
131
-    protected function _add_view_counts()
132
-    {
133
-        foreach ($this->_views as $view => $args) {
134
-            $this->_views[ $view ]['count'] = $this->get_admin_page()->get_message_templates(
135
-                $this->_per_page,
136
-                $view,
137
-                true,
138
-                true
139
-            );
140
-        }
141
-    }
142
-
143
-
144
-    /**
145
-     * @param EE_Message_Template_Group $item
146
-     * @return string
147
-     */
148
-    public function column_cb($item)
149
-    {
150
-        return '';
151
-    }
152
-
153
-
154
-    /**
155
-     * @param EE_Message_Template_Group $item
156
-     * @return string
157
-     * @throws EE_Error
158
-     */
159
-    public function column_description($item)
160
-    {
161
-        return '<p>' . $item->message_type_obj()->description . '</p>';
162
-    }
163
-
164
-
165
-    /**
166
-     * @param EE_Message_Template_Group $item
167
-     * @return string
168
-     * @throws EE_Error
169
-     */
170
-    public function column_messenger($item)
171
-    {
172
-        // Return the name contents
173
-        return sprintf(
174
-            '%1$s <span style="color:silver">(id:%2$s)</span><br />%3$s%4$s',
175
-            /* $1%s */
176
-            ucwords($item->messenger_obj()->label['singular']),
177
-            /* $2%s */
178
-            $item->GRP_ID(),
179
-            /* %4$s */
180
-            $this->_get_context_links($item),
181
-            $this->row_actions($this->_get_actions_for_messenger_column($item))
182
-        );
183
-    }
184
-
185
-    /**
186
-     * column_message_type
187
-     *
188
-     * @param  EE_Message_Template_Group $item message info for the row
189
-     * @return string message_type name
190
-     * @throws EE_Error
191
-     */
192
-    public function column_message_type($item)
193
-    {
194
-        return ucwords($item->message_type_obj()->label['singular']);
195
-    }
196
-
197
-
198
-    /**
199
-     * Generate dropdown filter select input for messengers
200
-     *
201
-     * @param bool $global
202
-     * @return string
203
-     * @throws EE_Error
204
-     */
205
-    protected function _get_messengers_dropdown_filter($global = true)
206
-    {
207
-        $messenger_options = array();
208
-        $active_message_template_groups_grouped_by_messenger = EEM_Message_Template_Group::instance()->get_all(
209
-            array(
210
-                array(
211
-                    'MTP_is_active' => true,
212
-                    'MTP_is_global' => $global,
213
-                ),
214
-                'group_by' => 'MTP_messenger',
215
-            )
216
-        );
217
-
218
-        foreach ($active_message_template_groups_grouped_by_messenger as $active_message_template_group) {
219
-            if ($active_message_template_group instanceof EE_Message_Template_Group) {
220
-                $messenger = $active_message_template_group->messenger_obj();
221
-                $messenger_label = $messenger instanceof EE_messenger
222
-                    ? $messenger->label['singular']
223
-                    : $active_message_template_group->messenger();
224
-                $messenger_options[ $active_message_template_group->messenger() ] = ucwords($messenger_label);
225
-            }
226
-        }
227
-        return $this->get_admin_page()->get_messengers_select_input($messenger_options);
228
-    }
229
-
230
-
231
-    /**
232
-     * Generate dropdown filter select input for message types
233
-     *
234
-     * @param bool $global
235
-     * @return string
236
-     * @throws EE_Error
237
-     */
238
-    protected function _get_message_types_dropdown_filter($global = true)
239
-    {
240
-        $message_type_options = array();
241
-        $active_message_template_groups_grouped_by_message_type = EEM_Message_Template_Group::instance()->get_all(
242
-            array(
243
-                array(
244
-                    'MTP_is_active' => true,
245
-                    'MTP_is_global' => true,
246
-                ),
247
-                'group_by' => 'MTP_message_type',
248
-            )
249
-        );
250
-
251
-        foreach ($active_message_template_groups_grouped_by_message_type as $active_message_template_group) {
252
-            if ($active_message_template_group instanceof EE_Message_Template_Group) {
253
-                $message_type = $active_message_template_group->message_type_obj();
254
-                $message_type_label = $message_type instanceof EE_message_type
255
-                    ? $message_type->label['singular']
256
-                    : $active_message_template_group->message_type();
257
-                $message_type_options[ $active_message_template_group->message_type() ] = ucwords($message_type_label);
258
-            }
259
-        }
260
-        return $this->get_admin_page()->get_message_types_select_input($message_type_options);
261
-    }
262
-
263
-
264
-    /**
265
-     * Return the edit url for the message template group.
266
-     *
267
-     * @param EE_Message_Template_Group $item
268
-     * @return string
269
-     * @throws EE_Error
270
-     */
271
-    protected function _get_edit_url(EE_Message_Template_Group $item)
272
-    {
273
-        $edit_url = '';
274
-        // edit link but only if item isn't trashed.
275
-        if (! $item->get('MTP_deleted')
276
-            && EE_Registry::instance()->CAP->current_user_can(
277
-                'ee_edit_message',
278
-                'espresso_messages_edit_message_template',
279
-                $item->ID()
280
-            )) {
281
-            $edit_url = EE_Admin_Page::add_query_args_and_nonce(
282
-                array(
283
-                    'action' => 'edit_message_template',
284
-                    'id'     => $item->GRP_ID(),
285
-                ),
286
-                EE_MSG_ADMIN_URL
287
-            );
288
-        }
289
-        return $edit_url;
290
-    }
291
-
292
-
293
-    /**
294
-     * Get the context link string for the messenger column.
295
-     *
296
-     * @param EE_Message_Template_Group $item
297
-     * @return string
298
-     * @throws EE_Error
299
-     */
300
-    protected function _get_context_links(EE_Message_Template_Group $item)
301
-    {
302
-        // first check if we even show the context links or not.
303
-        if (! EE_Registry::instance()->CAP->current_user_can(
304
-            'ee_edit_message',
305
-            'espresso_messages_edit_message_template',
306
-            $item->ID()
307
-        )
308
-            || $item->get('MTP_deleted')
309
-        ) {
310
-            return '';
311
-        }
312
-        // we want to display the contexts in here so we need to set them up
313
-        $c_label = $item->context_label();
314
-        $c_configs = $item->contexts_config();
315
-        $ctxt = array();
316
-        $context_templates = $item->context_templates();
317
-        foreach ($context_templates as $context => $template_fields) {
318
-            $mtp_to = ! empty($context_templates[ $context ]['to'])
319
-                      && $context_templates[ $context ]['to'] instanceof EE_Message_Template
320
-                ? $context_templates[ $context ]['to']->get('MTP_content')
321
-                : null;
322
-            $inactive_class = (
323
-                                  empty($mtp_to)
324
-                                  && ! empty($context_templates[ $context ]['to'])
325
-                              )
326
-                              || ! $item->is_context_active($context)
327
-                ? ' mtp-inactive'
328
-                : '';
329
-            $context_title = sprintf(
330
-                /* translators: Placeholder represents the context label. Example "Edit Event Admin" */
331
-                esc_html__('Edit %1$s', 'event_espresso'),
332
-                ucwords($c_configs[ $context ]['label'])
333
-            );
334
-            $edit_link = EE_Admin_Page::add_query_args_and_nonce(
335
-                array(
336
-                    'action'  => 'edit_message_template',
337
-                    'id'      => $item->GRP_ID(),
338
-                    'context' => $context,
339
-                ),
340
-                EE_MSG_ADMIN_URL
341
-            );
342
-            $ctxt[] = '<a'
343
-                      . ' href="' . $edit_link . '"'
344
-                      . ' class="' . $item->message_type() . '-' . $context . '-edit-link' . $inactive_class . '"'
345
-                      . ' title="' . esc_attr__('Edit Context', 'event_espresso') . '">'
346
-                      . $context_title
347
-                      . '</a>';
348
-        }
349
-
350
-        return sprintf('<strong>%s:</strong> ', ucwords($c_label['plural'])) . implode(' | ', $ctxt);
351
-    }
352
-
353
-
354
-    /**
355
-     * Returns the actions for the messenger column.
356
-     *
357
-     * Note: Children classes may override this so do not remove it.
358
-     *
359
-     * @param EE_Message_Template_Group $item
360
-     * @return array
361
-     */
362
-    protected function _get_actions_for_messenger_column(EE_Message_Template_Group $item)
363
-    {
364
-        return [];
365
-    }
16
+	/**
17
+	 * @return Messages_Admin_Page
18
+	 */
19
+	public function get_admin_page()
20
+	{
21
+		return $this->_admin_page;
22
+	}
23
+
24
+
25
+	/**
26
+	 * Setup data object
27
+	 */
28
+	protected function _setup_data()
29
+	{
30
+		$this->_data = $this->get_admin_page()->get_message_templates(
31
+			$this->_per_page,
32
+			$this->_view,
33
+			false
34
+		);
35
+		$this->_all_data_count = $this->get_admin_page()->get_message_templates(
36
+			$this->_per_page,
37
+			$this->_view,
38
+			true,
39
+			true
40
+		);
41
+	}
42
+
43
+
44
+	/**
45
+	 * Set internal properties
46
+	 */
47
+	protected function _set_properties()
48
+	{
49
+		$this->_wp_list_args = array(
50
+			'singular' => esc_html__('Message Template Group', 'event_espresso'),
51
+			'plural'   => esc_html__('Message Template', 'event_espresso'),
52
+			'ajax'     => true, // for now,
53
+			'screen'   => $this->get_admin_page()->get_current_screen()->id,
54
+		);
55
+		$this->_columns = array(
56
+			// 'cb' => '<input type="checkbox" />', //no deleting default (global) templates!
57
+			'message_type' => esc_html__('Message Type', 'event_espresso'),
58
+			'messenger'    => esc_html__('Messenger', 'event_espresso'),
59
+			'description'  => esc_html__('Description', 'event_espresso'),
60
+		);
61
+
62
+		$this->_sortable_columns = array(
63
+			'messenger' => array('MTP_messenger' => true),
64
+		);
65
+
66
+		$this->_hidden_columns = array();
67
+	}
68
+
69
+
70
+	/**
71
+	 * Overriding the single_row method from parent to verify whether the $item has an accessible
72
+	 * message_type or messenger object before generating the row.
73
+	 *
74
+	 * @param EE_Message_Template_Group $item
75
+	 * @return string
76
+	 * @throws EE_Error
77
+	 */
78
+	public function single_row($item)
79
+	{
80
+		$message_type = $item->message_type_obj();
81
+		$messenger = $item->messenger_obj();
82
+
83
+		if (! $message_type instanceof EE_message_type || ! $messenger instanceof EE_messenger) {
84
+			echo '';
85
+			return;
86
+		}
87
+
88
+		parent::single_row($item);
89
+	}
90
+
91
+
92
+	/**
93
+	 * @return array
94
+	 * @throws EE_Error
95
+	 */
96
+	protected function _get_table_filters()
97
+	{
98
+		$filters = array();
99
+
100
+		// get select inputs
101
+		$select_inputs = array(
102
+			$this->_get_messengers_dropdown_filter(),
103
+			$this->_get_message_types_dropdown_filter(),
104
+		);
105
+
106
+		// set filters to select inputs if they aren't empty
107
+		foreach ($select_inputs as $select_input) {
108
+			if ($select_input) {
109
+				$filters[] = $select_input;
110
+			}
111
+		}
112
+		return $filters;
113
+	}
114
+
115
+	/**
116
+	 * We're just removing the search box for message templates, not needed.
117
+	 *
118
+	 * @param string $text
119
+	 * @param string $input_id
120
+	 * @return string ;
121
+	 */
122
+	public function search_box($text, $input_id)
123
+	{
124
+		return '';
125
+	}
126
+
127
+
128
+	/**
129
+	 * Add counts to the _views property
130
+	 */
131
+	protected function _add_view_counts()
132
+	{
133
+		foreach ($this->_views as $view => $args) {
134
+			$this->_views[ $view ]['count'] = $this->get_admin_page()->get_message_templates(
135
+				$this->_per_page,
136
+				$view,
137
+				true,
138
+				true
139
+			);
140
+		}
141
+	}
142
+
143
+
144
+	/**
145
+	 * @param EE_Message_Template_Group $item
146
+	 * @return string
147
+	 */
148
+	public function column_cb($item)
149
+	{
150
+		return '';
151
+	}
152
+
153
+
154
+	/**
155
+	 * @param EE_Message_Template_Group $item
156
+	 * @return string
157
+	 * @throws EE_Error
158
+	 */
159
+	public function column_description($item)
160
+	{
161
+		return '<p>' . $item->message_type_obj()->description . '</p>';
162
+	}
163
+
164
+
165
+	/**
166
+	 * @param EE_Message_Template_Group $item
167
+	 * @return string
168
+	 * @throws EE_Error
169
+	 */
170
+	public function column_messenger($item)
171
+	{
172
+		// Return the name contents
173
+		return sprintf(
174
+			'%1$s <span style="color:silver">(id:%2$s)</span><br />%3$s%4$s',
175
+			/* $1%s */
176
+			ucwords($item->messenger_obj()->label['singular']),
177
+			/* $2%s */
178
+			$item->GRP_ID(),
179
+			/* %4$s */
180
+			$this->_get_context_links($item),
181
+			$this->row_actions($this->_get_actions_for_messenger_column($item))
182
+		);
183
+	}
184
+
185
+	/**
186
+	 * column_message_type
187
+	 *
188
+	 * @param  EE_Message_Template_Group $item message info for the row
189
+	 * @return string message_type name
190
+	 * @throws EE_Error
191
+	 */
192
+	public function column_message_type($item)
193
+	{
194
+		return ucwords($item->message_type_obj()->label['singular']);
195
+	}
196
+
197
+
198
+	/**
199
+	 * Generate dropdown filter select input for messengers
200
+	 *
201
+	 * @param bool $global
202
+	 * @return string
203
+	 * @throws EE_Error
204
+	 */
205
+	protected function _get_messengers_dropdown_filter($global = true)
206
+	{
207
+		$messenger_options = array();
208
+		$active_message_template_groups_grouped_by_messenger = EEM_Message_Template_Group::instance()->get_all(
209
+			array(
210
+				array(
211
+					'MTP_is_active' => true,
212
+					'MTP_is_global' => $global,
213
+				),
214
+				'group_by' => 'MTP_messenger',
215
+			)
216
+		);
217
+
218
+		foreach ($active_message_template_groups_grouped_by_messenger as $active_message_template_group) {
219
+			if ($active_message_template_group instanceof EE_Message_Template_Group) {
220
+				$messenger = $active_message_template_group->messenger_obj();
221
+				$messenger_label = $messenger instanceof EE_messenger
222
+					? $messenger->label['singular']
223
+					: $active_message_template_group->messenger();
224
+				$messenger_options[ $active_message_template_group->messenger() ] = ucwords($messenger_label);
225
+			}
226
+		}
227
+		return $this->get_admin_page()->get_messengers_select_input($messenger_options);
228
+	}
229
+
230
+
231
+	/**
232
+	 * Generate dropdown filter select input for message types
233
+	 *
234
+	 * @param bool $global
235
+	 * @return string
236
+	 * @throws EE_Error
237
+	 */
238
+	protected function _get_message_types_dropdown_filter($global = true)
239
+	{
240
+		$message_type_options = array();
241
+		$active_message_template_groups_grouped_by_message_type = EEM_Message_Template_Group::instance()->get_all(
242
+			array(
243
+				array(
244
+					'MTP_is_active' => true,
245
+					'MTP_is_global' => true,
246
+				),
247
+				'group_by' => 'MTP_message_type',
248
+			)
249
+		);
250
+
251
+		foreach ($active_message_template_groups_grouped_by_message_type as $active_message_template_group) {
252
+			if ($active_message_template_group instanceof EE_Message_Template_Group) {
253
+				$message_type = $active_message_template_group->message_type_obj();
254
+				$message_type_label = $message_type instanceof EE_message_type
255
+					? $message_type->label['singular']
256
+					: $active_message_template_group->message_type();
257
+				$message_type_options[ $active_message_template_group->message_type() ] = ucwords($message_type_label);
258
+			}
259
+		}
260
+		return $this->get_admin_page()->get_message_types_select_input($message_type_options);
261
+	}
262
+
263
+
264
+	/**
265
+	 * Return the edit url for the message template group.
266
+	 *
267
+	 * @param EE_Message_Template_Group $item
268
+	 * @return string
269
+	 * @throws EE_Error
270
+	 */
271
+	protected function _get_edit_url(EE_Message_Template_Group $item)
272
+	{
273
+		$edit_url = '';
274
+		// edit link but only if item isn't trashed.
275
+		if (! $item->get('MTP_deleted')
276
+			&& EE_Registry::instance()->CAP->current_user_can(
277
+				'ee_edit_message',
278
+				'espresso_messages_edit_message_template',
279
+				$item->ID()
280
+			)) {
281
+			$edit_url = EE_Admin_Page::add_query_args_and_nonce(
282
+				array(
283
+					'action' => 'edit_message_template',
284
+					'id'     => $item->GRP_ID(),
285
+				),
286
+				EE_MSG_ADMIN_URL
287
+			);
288
+		}
289
+		return $edit_url;
290
+	}
291
+
292
+
293
+	/**
294
+	 * Get the context link string for the messenger column.
295
+	 *
296
+	 * @param EE_Message_Template_Group $item
297
+	 * @return string
298
+	 * @throws EE_Error
299
+	 */
300
+	protected function _get_context_links(EE_Message_Template_Group $item)
301
+	{
302
+		// first check if we even show the context links or not.
303
+		if (! EE_Registry::instance()->CAP->current_user_can(
304
+			'ee_edit_message',
305
+			'espresso_messages_edit_message_template',
306
+			$item->ID()
307
+		)
308
+			|| $item->get('MTP_deleted')
309
+		) {
310
+			return '';
311
+		}
312
+		// we want to display the contexts in here so we need to set them up
313
+		$c_label = $item->context_label();
314
+		$c_configs = $item->contexts_config();
315
+		$ctxt = array();
316
+		$context_templates = $item->context_templates();
317
+		foreach ($context_templates as $context => $template_fields) {
318
+			$mtp_to = ! empty($context_templates[ $context ]['to'])
319
+					  && $context_templates[ $context ]['to'] instanceof EE_Message_Template
320
+				? $context_templates[ $context ]['to']->get('MTP_content')
321
+				: null;
322
+			$inactive_class = (
323
+								  empty($mtp_to)
324
+								  && ! empty($context_templates[ $context ]['to'])
325
+							  )
326
+							  || ! $item->is_context_active($context)
327
+				? ' mtp-inactive'
328
+				: '';
329
+			$context_title = sprintf(
330
+				/* translators: Placeholder represents the context label. Example "Edit Event Admin" */
331
+				esc_html__('Edit %1$s', 'event_espresso'),
332
+				ucwords($c_configs[ $context ]['label'])
333
+			);
334
+			$edit_link = EE_Admin_Page::add_query_args_and_nonce(
335
+				array(
336
+					'action'  => 'edit_message_template',
337
+					'id'      => $item->GRP_ID(),
338
+					'context' => $context,
339
+				),
340
+				EE_MSG_ADMIN_URL
341
+			);
342
+			$ctxt[] = '<a'
343
+					  . ' href="' . $edit_link . '"'
344
+					  . ' class="' . $item->message_type() . '-' . $context . '-edit-link' . $inactive_class . '"'
345
+					  . ' title="' . esc_attr__('Edit Context', 'event_espresso') . '">'
346
+					  . $context_title
347
+					  . '</a>';
348
+		}
349
+
350
+		return sprintf('<strong>%s:</strong> ', ucwords($c_label['plural'])) . implode(' | ', $ctxt);
351
+	}
352
+
353
+
354
+	/**
355
+	 * Returns the actions for the messenger column.
356
+	 *
357
+	 * Note: Children classes may override this so do not remove it.
358
+	 *
359
+	 * @param EE_Message_Template_Group $item
360
+	 * @return array
361
+	 */
362
+	protected function _get_actions_for_messenger_column(EE_Message_Template_Group $item)
363
+	{
364
+		return [];
365
+	}
366 366
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -80,7 +80,7 @@  discard block
 block discarded – undo
80 80
         $message_type = $item->message_type_obj();
81 81
         $messenger = $item->messenger_obj();
82 82
 
83
-        if (! $message_type instanceof EE_message_type || ! $messenger instanceof EE_messenger) {
83
+        if ( ! $message_type instanceof EE_message_type || ! $messenger instanceof EE_messenger) {
84 84
             echo '';
85 85
             return;
86 86
         }
@@ -131,7 +131,7 @@  discard block
 block discarded – undo
131 131
     protected function _add_view_counts()
132 132
     {
133 133
         foreach ($this->_views as $view => $args) {
134
-            $this->_views[ $view ]['count'] = $this->get_admin_page()->get_message_templates(
134
+            $this->_views[$view]['count'] = $this->get_admin_page()->get_message_templates(
135 135
                 $this->_per_page,
136 136
                 $view,
137 137
                 true,
@@ -158,7 +158,7 @@  discard block
 block discarded – undo
158 158
      */
159 159
     public function column_description($item)
160 160
     {
161
-        return '<p>' . $item->message_type_obj()->description . '</p>';
161
+        return '<p>'.$item->message_type_obj()->description.'</p>';
162 162
     }
163 163
 
164 164
 
@@ -221,7 +221,7 @@  discard block
 block discarded – undo
221 221
                 $messenger_label = $messenger instanceof EE_messenger
222 222
                     ? $messenger->label['singular']
223 223
                     : $active_message_template_group->messenger();
224
-                $messenger_options[ $active_message_template_group->messenger() ] = ucwords($messenger_label);
224
+                $messenger_options[$active_message_template_group->messenger()] = ucwords($messenger_label);
225 225
             }
226 226
         }
227 227
         return $this->get_admin_page()->get_messengers_select_input($messenger_options);
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
                 $message_type_label = $message_type instanceof EE_message_type
255 255
                     ? $message_type->label['singular']
256 256
                     : $active_message_template_group->message_type();
257
-                $message_type_options[ $active_message_template_group->message_type() ] = ucwords($message_type_label);
257
+                $message_type_options[$active_message_template_group->message_type()] = ucwords($message_type_label);
258 258
             }
259 259
         }
260 260
         return $this->get_admin_page()->get_message_types_select_input($message_type_options);
@@ -272,7 +272,7 @@  discard block
 block discarded – undo
272 272
     {
273 273
         $edit_url = '';
274 274
         // edit link but only if item isn't trashed.
275
-        if (! $item->get('MTP_deleted')
275
+        if ( ! $item->get('MTP_deleted')
276 276
             && EE_Registry::instance()->CAP->current_user_can(
277 277
                 'ee_edit_message',
278 278
                 'espresso_messages_edit_message_template',
@@ -300,7 +300,7 @@  discard block
 block discarded – undo
300 300
     protected function _get_context_links(EE_Message_Template_Group $item)
301 301
     {
302 302
         // first check if we even show the context links or not.
303
-        if (! EE_Registry::instance()->CAP->current_user_can(
303
+        if ( ! EE_Registry::instance()->CAP->current_user_can(
304 304
             'ee_edit_message',
305 305
             'espresso_messages_edit_message_template',
306 306
             $item->ID()
@@ -315,13 +315,13 @@  discard block
 block discarded – undo
315 315
         $ctxt = array();
316 316
         $context_templates = $item->context_templates();
317 317
         foreach ($context_templates as $context => $template_fields) {
318
-            $mtp_to = ! empty($context_templates[ $context ]['to'])
319
-                      && $context_templates[ $context ]['to'] instanceof EE_Message_Template
320
-                ? $context_templates[ $context ]['to']->get('MTP_content')
318
+            $mtp_to = ! empty($context_templates[$context]['to'])
319
+                      && $context_templates[$context]['to'] instanceof EE_Message_Template
320
+                ? $context_templates[$context]['to']->get('MTP_content')
321 321
                 : null;
322 322
             $inactive_class = (
323 323
                                   empty($mtp_to)
324
-                                  && ! empty($context_templates[ $context ]['to'])
324
+                                  && ! empty($context_templates[$context]['to'])
325 325
                               )
326 326
                               || ! $item->is_context_active($context)
327 327
                 ? ' mtp-inactive'
@@ -329,7 +329,7 @@  discard block
 block discarded – undo
329 329
             $context_title = sprintf(
330 330
                 /* translators: Placeholder represents the context label. Example "Edit Event Admin" */
331 331
                 esc_html__('Edit %1$s', 'event_espresso'),
332
-                ucwords($c_configs[ $context ]['label'])
332
+                ucwords($c_configs[$context]['label'])
333 333
             );
334 334
             $edit_link = EE_Admin_Page::add_query_args_and_nonce(
335 335
                 array(
@@ -340,14 +340,14 @@  discard block
 block discarded – undo
340 340
                 EE_MSG_ADMIN_URL
341 341
             );
342 342
             $ctxt[] = '<a'
343
-                      . ' href="' . $edit_link . '"'
344
-                      . ' class="' . $item->message_type() . '-' . $context . '-edit-link' . $inactive_class . '"'
345
-                      . ' title="' . esc_attr__('Edit Context', 'event_espresso') . '">'
343
+                      . ' href="'.$edit_link.'"'
344
+                      . ' class="'.$item->message_type().'-'.$context.'-edit-link'.$inactive_class.'"'
345
+                      . ' title="'.esc_attr__('Edit Context', 'event_espresso').'">'
346 346
                       . $context_title
347 347
                       . '</a>';
348 348
         }
349 349
 
350
-        return sprintf('<strong>%s:</strong> ', ucwords($c_label['plural'])) . implode(' | ', $ctxt);
350
+        return sprintf('<strong>%s:</strong> ', ucwords($c_label['plural'])).implode(' | ', $ctxt);
351 351
     }
352 352
 
353 353
 
Please login to merge, or discard this patch.
espresso.php 1 patch
Indentation   +80 added lines, -80 removed lines patch added patch discarded remove patch
@@ -38,103 +38,103 @@
 block discarded – undo
38 38
  * @since           4.0
39 39
  */
40 40
 if (function_exists('espresso_version')) {
41
-    if (! function_exists('espresso_duplicate_plugin_error')) {
42
-        /**
43
-         *    espresso_duplicate_plugin_error
44
-         *    displays if more than one version of EE is activated at the same time
45
-         */
46
-        function espresso_duplicate_plugin_error()
47
-        {
48
-            ?>
41
+	if (! function_exists('espresso_duplicate_plugin_error')) {
42
+		/**
43
+		 *    espresso_duplicate_plugin_error
44
+		 *    displays if more than one version of EE is activated at the same time
45
+		 */
46
+		function espresso_duplicate_plugin_error()
47
+		{
48
+			?>
49 49
             <div class="error">
50 50
                 <p>
51 51
                     <?php
52
-                    echo esc_html__(
53
-                        'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
-                        'event_espresso'
55
-                    ); ?>
52
+					echo esc_html__(
53
+						'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
+						'event_espresso'
55
+					); ?>
56 56
                 </p>
57 57
             </div>
58 58
             <?php
59
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
60
-        }
61
-    }
62
-    add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
59
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
60
+		}
61
+	}
62
+	add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
63 63
 } else {
64
-    define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
-    if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
-        /**
67
-         * espresso_minimum_php_version_error
68
-         *
69
-         * @return void
70
-         */
71
-        function espresso_minimum_php_version_error()
72
-        {
73
-            ?>
64
+	define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
+	if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
+		/**
67
+		 * espresso_minimum_php_version_error
68
+		 *
69
+		 * @return void
70
+		 */
71
+		function espresso_minimum_php_version_error()
72
+		{
73
+			?>
74 74
             <div class="error">
75 75
                 <p>
76 76
                     <?php
77
-                    printf(
78
-                        esc_html__(
79
-                            'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
-                            'event_espresso'
81
-                        ),
82
-                        EE_MIN_PHP_VER_REQUIRED,
83
-                        PHP_VERSION,
84
-                        '<br/>',
85
-                        '<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
-                    );
87
-                    ?>
77
+					printf(
78
+						esc_html__(
79
+							'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
+							'event_espresso'
81
+						),
82
+						EE_MIN_PHP_VER_REQUIRED,
83
+						PHP_VERSION,
84
+						'<br/>',
85
+						'<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
+					);
87
+					?>
88 88
                 </p>
89 89
             </div>
90 90
             <?php
91
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
92
-        }
91
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
92
+		}
93 93
 
94
-        add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
-    } else {
96
-        define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
-        /**
98
-         * espresso_version
99
-         * Returns the plugin version
100
-         *
101
-         * @return string
102
-         */
103
-        function espresso_version()
104
-        {
105
-            return apply_filters('FHEE__espresso__espresso_version', '4.9.78.rc.011');
106
-        }
94
+		add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
+	} else {
96
+		define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
+		/**
98
+		 * espresso_version
99
+		 * Returns the plugin version
100
+		 *
101
+		 * @return string
102
+		 */
103
+		function espresso_version()
104
+		{
105
+			return apply_filters('FHEE__espresso__espresso_version', '4.9.78.rc.011');
106
+		}
107 107
 
108
-        /**
109
-         * espresso_plugin_activation
110
-         * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
-         */
112
-        function espresso_plugin_activation()
113
-        {
114
-            update_option('ee_espresso_activation', true);
115
-        }
108
+		/**
109
+		 * espresso_plugin_activation
110
+		 * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
+		 */
112
+		function espresso_plugin_activation()
113
+		{
114
+			update_option('ee_espresso_activation', true);
115
+		}
116 116
 
117
-        register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
117
+		register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
118 118
 
119
-        require_once __DIR__ . '/core/bootstrap_espresso.php';
120
-        bootstrap_espresso();
121
-    }
119
+		require_once __DIR__ . '/core/bootstrap_espresso.php';
120
+		bootstrap_espresso();
121
+	}
122 122
 }
123 123
 if (! function_exists('espresso_deactivate_plugin')) {
124
-    /**
125
-     *    deactivate_plugin
126
-     * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
-     *
128
-     * @access public
129
-     * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
-     * @return    void
131
-     */
132
-    function espresso_deactivate_plugin($plugin_basename = '')
133
-    {
134
-        if (! function_exists('deactivate_plugins')) {
135
-            require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
-        }
137
-        unset($_GET['activate'], $_REQUEST['activate']);
138
-        deactivate_plugins($plugin_basename);
139
-    }
124
+	/**
125
+	 *    deactivate_plugin
126
+	 * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
+	 *
128
+	 * @access public
129
+	 * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
+	 * @return    void
131
+	 */
132
+	function espresso_deactivate_plugin($plugin_basename = '')
133
+	{
134
+		if (! function_exists('deactivate_plugins')) {
135
+			require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
+		}
137
+		unset($_GET['activate'], $_REQUEST['activate']);
138
+		deactivate_plugins($plugin_basename);
139
+	}
140 140
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Registration.class.php 1 patch
Indentation   +2068 added lines, -2068 removed lines patch added patch discarded remove patch
@@ -17,2072 +17,2072 @@
 block discarded – undo
17 17
 {
18 18
 
19 19
 
20
-    /**
21
-     * Used to reference when a registration has never been checked in.
22
-     *
23
-     * @deprecated use \EE_Checkin::status_checked_never instead
24
-     * @type int
25
-     */
26
-    const checkin_status_never = 2;
27
-
28
-    /**
29
-     * Used to reference when a registration has been checked in.
30
-     *
31
-     * @deprecated use \EE_Checkin::status_checked_in instead
32
-     * @type int
33
-     */
34
-    const checkin_status_in = 1;
35
-
36
-
37
-    /**
38
-     * Used to reference when a registration has been checked out.
39
-     *
40
-     * @deprecated use \EE_Checkin::status_checked_out instead
41
-     * @type int
42
-     */
43
-    const checkin_status_out = 0;
44
-
45
-
46
-    /**
47
-     * extra meta key for tracking reg status os trashed registrations
48
-     *
49
-     * @type string
50
-     */
51
-    const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
52
-
53
-
54
-    /**
55
-     * extra meta key for tracking if registration has reserved ticket
56
-     *
57
-     * @type string
58
-     */
59
-    const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
60
-
61
-
62
-    /**
63
-     * @param array  $props_n_values          incoming values
64
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
65
-     *                                        used.)
66
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
67
-     *                                        date_format and the second value is the time format
68
-     * @return EE_Registration
69
-     * @throws EE_Error
70
-     */
71
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
-    {
73
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
-    }
76
-
77
-
78
-    /**
79
-     * @param array  $props_n_values  incoming values from the database
80
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
-     *                                the website will be used.
82
-     * @return EE_Registration
83
-     */
84
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
85
-    {
86
-        return new self($props_n_values, true, $timezone);
87
-    }
88
-
89
-
90
-    /**
91
-     *        Set Event ID
92
-     *
93
-     * @param        int $EVT_ID Event ID
94
-     * @throws EE_Error
95
-     * @throws RuntimeException
96
-     */
97
-    public function set_event($EVT_ID = 0)
98
-    {
99
-        $this->set('EVT_ID', $EVT_ID);
100
-    }
101
-
102
-
103
-    /**
104
-     * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
105
-     * be routed to internal methods
106
-     *
107
-     * @param string $field_name
108
-     * @param mixed  $field_value
109
-     * @param bool   $use_default
110
-     * @throws EE_Error
111
-     * @throws EntityNotFoundException
112
-     * @throws InvalidArgumentException
113
-     * @throws InvalidDataTypeException
114
-     * @throws InvalidInterfaceException
115
-     * @throws ReflectionException
116
-     * @throws RuntimeException
117
-     */
118
-    public function set($field_name, $field_value, $use_default = false)
119
-    {
120
-        switch ($field_name) {
121
-            case 'REG_code':
122
-                if (! empty($field_value) && $this->reg_code() === null) {
123
-                    $this->set_reg_code($field_value, $use_default);
124
-                }
125
-                break;
126
-            case 'STS_ID':
127
-                $this->set_status($field_value, $use_default);
128
-                break;
129
-            default:
130
-                parent::set($field_name, $field_value, $use_default);
131
-        }
132
-    }
133
-
134
-
135
-    /**
136
-     * Set Status ID
137
-     * updates the registration status and ALSO...
138
-     * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
139
-     * calls release_registration_space() if the reg status changes FROM approved to any other reg status
140
-     *
141
-     * @param string                $new_STS_ID
142
-     * @param boolean               $use_default
143
-     * @param ContextInterface|null $context
144
-     * @return bool
145
-     * @throws EE_Error
146
-     * @throws EntityNotFoundException
147
-     * @throws InvalidArgumentException
148
-     * @throws ReflectionException
149
-     * @throws RuntimeException
150
-     * @throws InvalidDataTypeException
151
-     * @throws InvalidInterfaceException
152
-     */
153
-    public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
154
-    {
155
-        // get current REG_Status
156
-        $old_STS_ID = $this->status_ID();
157
-        // if status has changed
158
-        if ($old_STS_ID !== $new_STS_ID // and that status has actually changed
159
-            && ! empty($old_STS_ID) // and that old status is actually set
160
-            && ! empty($new_STS_ID) // as well as the new status
161
-            && $this->ID() // ensure registration is in the db
162
-        ) {
163
-            // update internal status first
164
-            parent::set('STS_ID', $new_STS_ID, $use_default);
165
-            // THEN handle other changes that occur when reg status changes
166
-            // TO approved
167
-            if ($new_STS_ID === EEM_Registration::status_id_approved) {
168
-                // reserve a space by incrementing ticket and datetime sold values
169
-                $this->_reserve_registration_space();
170
-                do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
171
-                // OR FROM  approved
172
-            } elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
173
-                // release a space by decrementing ticket and datetime sold values
174
-                $this->_release_registration_space();
175
-                do_action(
176
-                    'AHEE__EE_Registration__set_status__from_approved',
177
-                    $this,
178
-                    $old_STS_ID,
179
-                    $new_STS_ID,
180
-                    $context
181
-                );
182
-            }
183
-            $this->_update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, $context);
184
-            if ($this->statusChangeUpdatesTransaction($context)) {
185
-                $this->updateTransactionAfterStatusChange();
186
-            }
187
-            do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
188
-            return true;
189
-        }
190
-        // even though the old value matches the new value, it's still good to
191
-        // allow the parent set method to have a say
192
-        parent::set('STS_ID', $new_STS_ID, $use_default);
193
-        return true;
194
-    }
195
-
196
-
197
-    /**
198
-     * update REGs and TXN when cancelled or declined registrations involved
199
-     *
200
-     * @param string                $new_STS_ID
201
-     * @param string                $old_STS_ID
202
-     * @param ContextInterface|null $context
203
-     * @throws EE_Error
204
-     * @throws InvalidArgumentException
205
-     * @throws InvalidDataTypeException
206
-     * @throws InvalidInterfaceException
207
-     * @throws ReflectionException
208
-     */
209
-    private function _update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
210
-    {
211
-        // these reg statuses should not be considered in any calculations involving monies owing
212
-        $closed_reg_statuses = EEM_Registration::closed_reg_statuses();
213
-        // true if registration has been cancelled or declined
214
-        $this->updateIfCanceled(
215
-            $closed_reg_statuses,
216
-            $new_STS_ID,
217
-            $old_STS_ID,
218
-            $context
219
-        );
220
-        $this->updateIfDeclined(
221
-            $closed_reg_statuses,
222
-            $new_STS_ID,
223
-            $old_STS_ID,
224
-            $context
225
-        );
226
-    }
227
-
228
-
229
-    /**
230
-     * update REGs and TXN when cancelled or declined registrations involved
231
-     *
232
-     * @param array                 $closed_reg_statuses
233
-     * @param string                $new_STS_ID
234
-     * @param string                $old_STS_ID
235
-     * @param ContextInterface|null $context
236
-     * @throws EE_Error
237
-     * @throws InvalidArgumentException
238
-     * @throws InvalidDataTypeException
239
-     * @throws InvalidInterfaceException
240
-     * @throws ReflectionException
241
-     */
242
-    private function updateIfCanceled(
243
-        array $closed_reg_statuses,
244
-        $new_STS_ID,
245
-        $old_STS_ID,
246
-        ContextInterface $context = null
247
-    ) {
248
-        // true if registration has been cancelled or declined
249
-        if (in_array($new_STS_ID, $closed_reg_statuses, true)
250
-            && ! in_array($old_STS_ID, $closed_reg_statuses, true)
251
-        ) {
252
-            /** @type EE_Registration_Processor $registration_processor */
253
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
254
-            /** @type EE_Transaction_Processor $transaction_processor */
255
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
256
-            // cancelled or declined registration
257
-            $registration_processor->update_registration_after_being_canceled_or_declined(
258
-                $this,
259
-                $closed_reg_statuses
260
-            );
261
-            $transaction_processor->update_transaction_after_canceled_or_declined_registration(
262
-                $this,
263
-                $closed_reg_statuses,
264
-                false
265
-            );
266
-            do_action(
267
-                'AHEE__EE_Registration__set_status__canceled_or_declined',
268
-                $this,
269
-                $old_STS_ID,
270
-                $new_STS_ID,
271
-                $context
272
-            );
273
-            return;
274
-        }
275
-    }
276
-
277
-
278
-    /**
279
-     * update REGs and TXN when cancelled or declined registrations involved
280
-     *
281
-     * @param array                 $closed_reg_statuses
282
-     * @param string                $new_STS_ID
283
-     * @param string                $old_STS_ID
284
-     * @param ContextInterface|null $context
285
-     * @throws EE_Error
286
-     * @throws InvalidArgumentException
287
-     * @throws InvalidDataTypeException
288
-     * @throws InvalidInterfaceException
289
-     * @throws ReflectionException
290
-     */
291
-    private function updateIfDeclined(
292
-        array $closed_reg_statuses,
293
-        $new_STS_ID,
294
-        $old_STS_ID,
295
-        ContextInterface $context = null
296
-    ) {
297
-        // true if reinstating cancelled or declined registration
298
-        if (in_array($old_STS_ID, $closed_reg_statuses, true)
299
-            && ! in_array($new_STS_ID, $closed_reg_statuses, true)
300
-        ) {
301
-            /** @type EE_Registration_Processor $registration_processor */
302
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
303
-            /** @type EE_Transaction_Processor $transaction_processor */
304
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
305
-            // reinstating cancelled or declined registration
306
-            $registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
307
-                $this,
308
-                $closed_reg_statuses
309
-            );
310
-            $transaction_processor->update_transaction_after_reinstating_canceled_registration(
311
-                $this,
312
-                $closed_reg_statuses,
313
-                false
314
-            );
315
-            do_action(
316
-                'AHEE__EE_Registration__set_status__after_reinstated',
317
-                $this,
318
-                $old_STS_ID,
319
-                $new_STS_ID,
320
-                $context
321
-            );
322
-        }
323
-    }
324
-
325
-
326
-    /**
327
-     * @param ContextInterface|null $context
328
-     * @return bool
329
-     */
330
-    private function statusChangeUpdatesTransaction(ContextInterface $context = null)
331
-    {
332
-        $contexts_that_do_not_update_transaction = (array) apply_filters(
333
-            'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
334
-            array('spco_reg_step_attendee_information_process_registrations'),
335
-            $context,
336
-            $this
337
-        );
338
-        return ! (
339
-            $context instanceof ContextInterface
340
-            && in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
341
-        );
342
-    }
343
-
344
-
345
-    /**
346
-     * @throws EE_Error
347
-     * @throws EntityNotFoundException
348
-     * @throws InvalidArgumentException
349
-     * @throws InvalidDataTypeException
350
-     * @throws InvalidInterfaceException
351
-     * @throws ReflectionException
352
-     * @throws RuntimeException
353
-     */
354
-    private function updateTransactionAfterStatusChange()
355
-    {
356
-        /** @type EE_Transaction_Payments $transaction_payments */
357
-        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
358
-        $transaction_payments->recalculate_transaction_total($this->transaction(), false);
359
-        $this->transaction()->update_status_based_on_total_paid(true);
360
-    }
361
-
362
-
363
-    /**
364
-     *        get Status ID
365
-     */
366
-    public function status_ID()
367
-    {
368
-        return $this->get('STS_ID');
369
-    }
370
-
371
-
372
-    /**
373
-     * Gets the ticket this registration is for
374
-     *
375
-     * @param boolean $include_archived whether to include archived tickets or not.
376
-     *
377
-     * @return EE_Ticket|EE_Base_Class
378
-     * @throws EE_Error
379
-     */
380
-    public function ticket($include_archived = true)
381
-    {
382
-        $query_params = array();
383
-        if ($include_archived) {
384
-            $query_params['default_where_conditions'] = 'none';
385
-        }
386
-        return $this->get_first_related('Ticket', $query_params);
387
-    }
388
-
389
-
390
-    /**
391
-     * Gets the event this registration is for
392
-     *
393
-     * @return EE_Event
394
-     * @throws EE_Error
395
-     * @throws EntityNotFoundException
396
-     */
397
-    public function event()
398
-    {
399
-        $event = $this->get_first_related('Event');
400
-        if (! $event instanceof \EE_Event) {
401
-            throw new EntityNotFoundException('Event ID', $this->event_ID());
402
-        }
403
-        return $event;
404
-    }
405
-
406
-
407
-    /**
408
-     * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
409
-     * with the author of the event this registration is for.
410
-     *
411
-     * @since 4.5.0
412
-     * @return int
413
-     * @throws EE_Error
414
-     * @throws EntityNotFoundException
415
-     */
416
-    public function wp_user()
417
-    {
418
-        $event = $this->event();
419
-        if ($event instanceof EE_Event) {
420
-            return $event->wp_user();
421
-        }
422
-        return 0;
423
-    }
424
-
425
-
426
-    /**
427
-     * increments this registration's related ticket sold and corresponding datetime sold values
428
-     *
429
-     * @return void
430
-     * @throws DomainException
431
-     * @throws EE_Error
432
-     * @throws EntityNotFoundException
433
-     * @throws InvalidArgumentException
434
-     * @throws InvalidDataTypeException
435
-     * @throws InvalidInterfaceException
436
-     * @throws ReflectionException
437
-     * @throws UnexpectedEntityException
438
-     */
439
-    private function _reserve_registration_space()
440
-    {
441
-        // reserved ticket and datetime counts will be decremented as sold counts are incremented
442
-        // so stop tracking that this reg has a ticket reserved
443
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
444
-        $ticket = $this->ticket();
445
-        $ticket->increase_sold();
446
-        $ticket->save();
447
-        // possibly set event status to sold out
448
-        $this->event()->perform_sold_out_status_check();
449
-    }
450
-
451
-
452
-    /**
453
-     * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
454
-     *
455
-     * @return void
456
-     * @throws DomainException
457
-     * @throws EE_Error
458
-     * @throws EntityNotFoundException
459
-     * @throws InvalidArgumentException
460
-     * @throws InvalidDataTypeException
461
-     * @throws InvalidInterfaceException
462
-     * @throws ReflectionException
463
-     * @throws UnexpectedEntityException
464
-     */
465
-    private function _release_registration_space()
466
-    {
467
-        $ticket = $this->ticket();
468
-        $ticket->decrease_sold();
469
-        $ticket->save();
470
-        // possibly change event status from sold out back to previous status
471
-        $this->event()->perform_sold_out_status_check();
472
-    }
473
-
474
-
475
-    /**
476
-     * tracks this registration's ticket reservation in extra meta
477
-     * and can increment related ticket reserved and corresponding datetime reserved values
478
-     *
479
-     * @param bool $update_ticket if true, will increment ticket and datetime reserved count
480
-     * @return void
481
-     * @throws EE_Error
482
-     * @throws InvalidArgumentException
483
-     * @throws InvalidDataTypeException
484
-     * @throws InvalidInterfaceException
485
-     * @throws ReflectionException
486
-     */
487
-    public function reserve_ticket($update_ticket = false, $source = 'unknown')
488
-    {
489
-        // only reserve ticket if space is not currently reserved
490
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
491
-            $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
492
-            // IMPORTANT !!!
493
-            // although checking $update_ticket first would be more efficient,
494
-            // we NEED to ALWAYS call update_extra_meta(), which is why that is done first
495
-            if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true)
496
-                && $update_ticket
497
-            ) {
498
-                $ticket = $this->ticket();
499
-                $ticket->increase_reserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
500
-                $ticket->save();
501
-            }
502
-        }
503
-    }
504
-
505
-
506
-    /**
507
-     * stops tracking this registration's ticket reservation in extra meta
508
-     * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
509
-     *
510
-     * @param bool $update_ticket if true, will decrement ticket and datetime reserved count
511
-     * @return void
512
-     * @throws EE_Error
513
-     * @throws InvalidArgumentException
514
-     * @throws InvalidDataTypeException
515
-     * @throws InvalidInterfaceException
516
-     * @throws ReflectionException
517
-     */
518
-    public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
519
-    {
520
-        // only release ticket if space is currently reserved
521
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
522
-            $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
523
-            // IMPORTANT !!!
524
-            // although checking $update_ticket first would be more efficient,
525
-            // we NEED to ALWAYS call update_extra_meta(), which is why that is done first
526
-            if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false)
527
-                && $update_ticket
528
-            ) {
529
-                $ticket = $this->ticket();
530
-                $ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
531
-                $ticket->save();
532
-            }
533
-        }
534
-    }
535
-
536
-
537
-    /**
538
-     * Set Attendee ID
539
-     *
540
-     * @param        int $ATT_ID Attendee ID
541
-     * @throws EE_Error
542
-     * @throws RuntimeException
543
-     */
544
-    public function set_attendee_id($ATT_ID = 0)
545
-    {
546
-        $this->set('ATT_ID', $ATT_ID);
547
-    }
548
-
549
-
550
-    /**
551
-     *        Set Transaction ID
552
-     *
553
-     * @param        int $TXN_ID Transaction ID
554
-     * @throws EE_Error
555
-     * @throws RuntimeException
556
-     */
557
-    public function set_transaction_id($TXN_ID = 0)
558
-    {
559
-        $this->set('TXN_ID', $TXN_ID);
560
-    }
561
-
562
-
563
-    /**
564
-     *        Set Session
565
-     *
566
-     * @param    string $REG_session PHP Session ID
567
-     * @throws EE_Error
568
-     * @throws RuntimeException
569
-     */
570
-    public function set_session($REG_session = '')
571
-    {
572
-        $this->set('REG_session', $REG_session);
573
-    }
574
-
575
-
576
-    /**
577
-     *        Set Registration URL Link
578
-     *
579
-     * @param    string $REG_url_link Registration URL Link
580
-     * @throws EE_Error
581
-     * @throws RuntimeException
582
-     */
583
-    public function set_reg_url_link($REG_url_link = '')
584
-    {
585
-        $this->set('REG_url_link', $REG_url_link);
586
-    }
587
-
588
-
589
-    /**
590
-     *        Set Attendee Counter
591
-     *
592
-     * @param        int $REG_count Primary Attendee
593
-     * @throws EE_Error
594
-     * @throws RuntimeException
595
-     */
596
-    public function set_count($REG_count = 1)
597
-    {
598
-        $this->set('REG_count', $REG_count);
599
-    }
600
-
601
-
602
-    /**
603
-     *        Set Group Size
604
-     *
605
-     * @param        boolean $REG_group_size Group Registration
606
-     * @throws EE_Error
607
-     * @throws RuntimeException
608
-     */
609
-    public function set_group_size($REG_group_size = false)
610
-    {
611
-        $this->set('REG_group_size', $REG_group_size);
612
-    }
613
-
614
-
615
-    /**
616
-     *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
617
-     *    EEM_Registration::status_id_not_approved
618
-     *
619
-     * @return        boolean
620
-     */
621
-    public function is_not_approved()
622
-    {
623
-        return $this->status_ID() == EEM_Registration::status_id_not_approved ? true : false;
624
-    }
625
-
626
-
627
-    /**
628
-     *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
629
-     *    EEM_Registration::status_id_pending_payment
630
-     *
631
-     * @return        boolean
632
-     */
633
-    public function is_pending_payment()
634
-    {
635
-        return $this->status_ID() == EEM_Registration::status_id_pending_payment ? true : false;
636
-    }
637
-
638
-
639
-    /**
640
-     *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
641
-     *
642
-     * @return        boolean
643
-     */
644
-    public function is_approved()
645
-    {
646
-        return $this->status_ID() == EEM_Registration::status_id_approved ? true : false;
647
-    }
648
-
649
-
650
-    /**
651
-     *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
652
-     *
653
-     * @return        boolean
654
-     */
655
-    public function is_cancelled()
656
-    {
657
-        return $this->status_ID() == EEM_Registration::status_id_cancelled ? true : false;
658
-    }
659
-
660
-
661
-    /**
662
-     *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
663
-     *
664
-     * @return        boolean
665
-     */
666
-    public function is_declined()
667
-    {
668
-        return $this->status_ID() == EEM_Registration::status_id_declined ? true : false;
669
-    }
670
-
671
-
672
-    /**
673
-     *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
674
-     *    EEM_Registration::status_id_incomplete
675
-     *
676
-     * @return        boolean
677
-     */
678
-    public function is_incomplete()
679
-    {
680
-        return $this->status_ID() == EEM_Registration::status_id_incomplete ? true : false;
681
-    }
682
-
683
-
684
-    /**
685
-     *        Set Registration Date
686
-     *
687
-     * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
688
-     *                                                 Date
689
-     * @throws EE_Error
690
-     * @throws RuntimeException
691
-     */
692
-    public function set_reg_date($REG_date = false)
693
-    {
694
-        $this->set('REG_date', $REG_date);
695
-    }
696
-
697
-
698
-    /**
699
-     *    Set final price owing for this registration after all ticket/price modifications
700
-     *
701
-     * @access    public
702
-     * @param    float $REG_final_price
703
-     * @throws EE_Error
704
-     * @throws RuntimeException
705
-     */
706
-    public function set_final_price($REG_final_price = 0.00)
707
-    {
708
-        $this->set('REG_final_price', $REG_final_price);
709
-    }
710
-
711
-
712
-    /**
713
-     *    Set amount paid towards this registration's final price
714
-     *
715
-     * @access    public
716
-     * @param    float $REG_paid
717
-     * @throws EE_Error
718
-     * @throws RuntimeException
719
-     */
720
-    public function set_paid($REG_paid = 0.00)
721
-    {
722
-        $this->set('REG_paid', $REG_paid);
723
-    }
724
-
725
-
726
-    /**
727
-     *        Attendee Is Going
728
-     *
729
-     * @param        boolean $REG_att_is_going Attendee Is Going
730
-     * @throws EE_Error
731
-     * @throws RuntimeException
732
-     */
733
-    public function set_att_is_going($REG_att_is_going = false)
734
-    {
735
-        $this->set('REG_att_is_going', $REG_att_is_going);
736
-    }
737
-
738
-
739
-    /**
740
-     * Gets the related attendee
741
-     *
742
-     * @return EE_Attendee
743
-     * @throws EE_Error
744
-     */
745
-    public function attendee()
746
-    {
747
-        return $this->get_first_related('Attendee');
748
-    }
749
-
750
-
751
-    /**
752
-     *        get Event ID
753
-     */
754
-    public function event_ID()
755
-    {
756
-        return $this->get('EVT_ID');
757
-    }
758
-
759
-
760
-    /**
761
-     *        get Event ID
762
-     */
763
-    public function event_name()
764
-    {
765
-        $event = $this->event_obj();
766
-        if ($event) {
767
-            return $event->name();
768
-        } else {
769
-            return null;
770
-        }
771
-    }
772
-
773
-
774
-    /**
775
-     * Fetches the event this registration is for
776
-     *
777
-     * @return EE_Event
778
-     * @throws EE_Error
779
-     */
780
-    public function event_obj()
781
-    {
782
-        return $this->get_first_related('Event');
783
-    }
784
-
785
-
786
-    /**
787
-     *        get Attendee ID
788
-     */
789
-    public function attendee_ID()
790
-    {
791
-        return $this->get('ATT_ID');
792
-    }
793
-
794
-
795
-    /**
796
-     *        get PHP Session ID
797
-     */
798
-    public function session_ID()
799
-    {
800
-        return $this->get('REG_session');
801
-    }
802
-
803
-
804
-    /**
805
-     * Gets the string which represents the URL trigger for the receipt template in the message template system.
806
-     *
807
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
808
-     * @return string
809
-     */
810
-    public function receipt_url($messenger = 'html')
811
-    {
812
-
813
-        /**
814
-         * The below will be deprecated one version after this.  We check first if there is a custom receipt template
815
-         * already in use on old system.  If there is then we just return the standard url for it.
816
-         *
817
-         * @since 4.5.0
818
-         */
819
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
820
-        $has_custom = EEH_Template::locate_template(
821
-            $template_relative_path,
822
-            array(),
823
-            true,
824
-            true,
825
-            true
826
-        );
827
-
828
-        if ($has_custom) {
829
-            return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
830
-        }
831
-        return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
832
-    }
833
-
834
-
835
-    /**
836
-     * Gets the string which represents the URL trigger for the invoice template in the message template system.
837
-     *
838
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
839
-     * @return string
840
-     * @throws EE_Error
841
-     */
842
-    public function invoice_url($messenger = 'html')
843
-    {
844
-        /**
845
-         * The below will be deprecated one version after this.  We check first if there is a custom invoice template
846
-         * already in use on old system.  If there is then we just return the standard url for it.
847
-         *
848
-         * @since 4.5.0
849
-         */
850
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
851
-        $has_custom = EEH_Template::locate_template(
852
-            $template_relative_path,
853
-            array(),
854
-            true,
855
-            true,
856
-            true
857
-        );
858
-
859
-        if ($has_custom) {
860
-            if ($messenger == 'html') {
861
-                return $this->invoice_url('launch');
862
-            }
863
-            $route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
864
-
865
-            $query_args = array('ee' => $route, 'id' => $this->reg_url_link());
866
-            if ($messenger == 'html') {
867
-                $query_args['html'] = true;
868
-            }
869
-            return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
870
-        }
871
-        return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
872
-    }
873
-
874
-
875
-    /**
876
-     * get Registration URL Link
877
-     *
878
-     * @access public
879
-     * @return string
880
-     * @throws EE_Error
881
-     */
882
-    public function reg_url_link()
883
-    {
884
-        return (string) $this->get('REG_url_link');
885
-    }
886
-
887
-
888
-    /**
889
-     * Echoes out invoice_url()
890
-     *
891
-     * @param string $type 'download','launch', or 'html' (default is 'launch')
892
-     * @return void
893
-     * @throws EE_Error
894
-     */
895
-    public function e_invoice_url($type = 'launch')
896
-    {
897
-        echo $this->invoice_url($type);
898
-    }
899
-
900
-
901
-    /**
902
-     * Echoes out payment_overview_url
903
-     */
904
-    public function e_payment_overview_url()
905
-    {
906
-        echo $this->payment_overview_url();
907
-    }
908
-
909
-
910
-    /**
911
-     * Gets the URL for the checkout payment options reg step
912
-     * with this registration's REG_url_link added as a query parameter
913
-     *
914
-     * @param bool $clear_session Set to true when you want to clear the session on revisiting the
915
-     *                            payment overview url.
916
-     * @return string
917
-     * @throws InvalidInterfaceException
918
-     * @throws InvalidDataTypeException
919
-     * @throws EE_Error
920
-     * @throws InvalidArgumentException
921
-     */
922
-    public function payment_overview_url($clear_session = false)
923
-    {
924
-        return add_query_arg(
925
-            (array) apply_filters(
926
-                'FHEE__EE_Registration__payment_overview_url__query_args',
927
-                array(
928
-                    'e_reg_url_link' => $this->reg_url_link(),
929
-                    'step'           => 'payment_options',
930
-                    'revisit'        => true,
931
-                    'clear_session'  => (bool) $clear_session,
932
-                ),
933
-                $this
934
-            ),
935
-            EE_Registry::instance()->CFG->core->reg_page_url()
936
-        );
937
-    }
938
-
939
-
940
-    /**
941
-     * Gets the URL for the checkout attendee information reg step
942
-     * with this registration's REG_url_link added as a query parameter
943
-     *
944
-     * @return string
945
-     * @throws InvalidInterfaceException
946
-     * @throws InvalidDataTypeException
947
-     * @throws EE_Error
948
-     * @throws InvalidArgumentException
949
-     */
950
-    public function edit_attendee_information_url()
951
-    {
952
-        return add_query_arg(
953
-            (array) apply_filters(
954
-                'FHEE__EE_Registration__edit_attendee_information_url__query_args',
955
-                array(
956
-                    'e_reg_url_link' => $this->reg_url_link(),
957
-                    'step'           => 'attendee_information',
958
-                    'revisit'        => true,
959
-                ),
960
-                $this
961
-            ),
962
-            EE_Registry::instance()->CFG->core->reg_page_url()
963
-        );
964
-    }
965
-
966
-
967
-    /**
968
-     * Simply generates and returns the appropriate admin_url link to edit this registration
969
-     *
970
-     * @return string
971
-     * @throws EE_Error
972
-     */
973
-    public function get_admin_edit_url()
974
-    {
975
-        return EEH_URL::add_query_args_and_nonce(
976
-            array(
977
-                'page'    => 'espresso_registrations',
978
-                'action'  => 'view_registration',
979
-                '_REG_ID' => $this->ID(),
980
-            ),
981
-            admin_url('admin.php')
982
-        );
983
-    }
984
-
985
-
986
-    /**
987
-     *    is_primary_registrant?
988
-     */
989
-    public function is_primary_registrant()
990
-    {
991
-        return $this->get('REG_count') == 1 ? true : false;
992
-    }
993
-
994
-
995
-    /**
996
-     * This returns the primary registration object for this registration group (which may be this object).
997
-     *
998
-     * @return EE_Registration
999
-     * @throws EE_Error
1000
-     */
1001
-    public function get_primary_registration()
1002
-    {
1003
-        if ($this->is_primary_registrant()) {
1004
-            return $this;
1005
-        }
1006
-
1007
-        // k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1008
-        /** @var EE_Registration $primary_registrant */
1009
-        $primary_registrant = EEM_Registration::instance()->get_one(
1010
-            array(
1011
-                array(
1012
-                    'TXN_ID'    => $this->transaction_ID(),
1013
-                    'REG_count' => 1,
1014
-                ),
1015
-            )
1016
-        );
1017
-        return $primary_registrant;
1018
-    }
1019
-
1020
-
1021
-    /**
1022
-     *        get  Attendee Number
1023
-     *
1024
-     * @access        public
1025
-     */
1026
-    public function count()
1027
-    {
1028
-        return $this->get('REG_count');
1029
-    }
1030
-
1031
-
1032
-    /**
1033
-     *        get Group Size
1034
-     */
1035
-    public function group_size()
1036
-    {
1037
-        return $this->get('REG_group_size');
1038
-    }
1039
-
1040
-
1041
-    /**
1042
-     *        get Registration Date
1043
-     */
1044
-    public function date()
1045
-    {
1046
-        return $this->get('REG_date');
1047
-    }
1048
-
1049
-
1050
-    /**
1051
-     * gets a pretty date
1052
-     *
1053
-     * @param string $date_format
1054
-     * @param string $time_format
1055
-     * @return string
1056
-     * @throws EE_Error
1057
-     */
1058
-    public function pretty_date($date_format = null, $time_format = null)
1059
-    {
1060
-        return $this->get_datetime('REG_date', $date_format, $time_format);
1061
-    }
1062
-
1063
-
1064
-    /**
1065
-     * final_price
1066
-     * the registration's share of the transaction total, so that the
1067
-     * sum of all the transaction's REG_final_prices equal the transaction's total
1068
-     *
1069
-     * @return float
1070
-     * @throws EE_Error
1071
-     */
1072
-    public function final_price()
1073
-    {
1074
-        return $this->get('REG_final_price');
1075
-    }
1076
-
1077
-
1078
-    /**
1079
-     * pretty_final_price
1080
-     *  final price as formatted string, with correct decimal places and currency symbol
1081
-     *
1082
-     * @return string
1083
-     * @throws EE_Error
1084
-     */
1085
-    public function pretty_final_price()
1086
-    {
1087
-        return $this->get_pretty('REG_final_price');
1088
-    }
1089
-
1090
-
1091
-    /**
1092
-     * get paid (yeah)
1093
-     *
1094
-     * @return float
1095
-     * @throws EE_Error
1096
-     */
1097
-    public function paid()
1098
-    {
1099
-        return $this->get('REG_paid');
1100
-    }
1101
-
1102
-
1103
-    /**
1104
-     * pretty_paid
1105
-     *
1106
-     * @return float
1107
-     * @throws EE_Error
1108
-     */
1109
-    public function pretty_paid()
1110
-    {
1111
-        return $this->get_pretty('REG_paid');
1112
-    }
1113
-
1114
-
1115
-    /**
1116
-     * owes_monies_and_can_pay
1117
-     * whether or not this registration has monies owing and it's' status allows payment
1118
-     *
1119
-     * @param array $requires_payment
1120
-     * @return bool
1121
-     * @throws EE_Error
1122
-     */
1123
-    public function owes_monies_and_can_pay($requires_payment = array())
1124
-    {
1125
-        // these reg statuses require payment (if event is not free)
1126
-        $requires_payment = ! empty($requires_payment)
1127
-            ? $requires_payment
1128
-            : EEM_Registration::reg_statuses_that_allow_payment();
1129
-        if (in_array($this->status_ID(), $requires_payment) &&
1130
-            $this->final_price() != 0 &&
1131
-            $this->final_price() != $this->paid()
1132
-        ) {
1133
-            return true;
1134
-        } else {
1135
-            return false;
1136
-        }
1137
-    }
1138
-
1139
-
1140
-    /**
1141
-     * Prints out the return value of $this->pretty_status()
1142
-     *
1143
-     * @param bool $show_icons
1144
-     * @return void
1145
-     * @throws EE_Error
1146
-     */
1147
-    public function e_pretty_status($show_icons = false)
1148
-    {
1149
-        echo $this->pretty_status($show_icons);
1150
-    }
1151
-
1152
-
1153
-    /**
1154
-     * Returns a nice version of the status for displaying to customers
1155
-     *
1156
-     * @param bool $show_icons
1157
-     * @return string
1158
-     * @throws EE_Error
1159
-     */
1160
-    public function pretty_status($show_icons = false)
1161
-    {
1162
-        $status = EEM_Status::instance()->localized_status(
1163
-            array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1164
-            false,
1165
-            'sentence'
1166
-        );
1167
-        $icon = '';
1168
-        switch ($this->status_ID()) {
1169
-            case EEM_Registration::status_id_approved:
1170
-                $icon = $show_icons
1171
-                    ? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1172
-                    : '';
1173
-                break;
1174
-            case EEM_Registration::status_id_pending_payment:
1175
-                $icon = $show_icons
1176
-                    ? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1177
-                    : '';
1178
-                break;
1179
-            case EEM_Registration::status_id_not_approved:
1180
-                $icon = $show_icons
1181
-                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1182
-                    : '';
1183
-                break;
1184
-            case EEM_Registration::status_id_cancelled:
1185
-                $icon = $show_icons
1186
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1187
-                    : '';
1188
-                break;
1189
-            case EEM_Registration::status_id_incomplete:
1190
-                $icon = $show_icons
1191
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1192
-                    : '';
1193
-                break;
1194
-            case EEM_Registration::status_id_declined:
1195
-                $icon = $show_icons
1196
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1197
-                    : '';
1198
-                break;
1199
-            case EEM_Registration::status_id_wait_list:
1200
-                $icon = $show_icons
1201
-                    ? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1202
-                    : '';
1203
-                break;
1204
-        }
1205
-        return $icon . $status[ $this->status_ID() ];
1206
-    }
1207
-
1208
-
1209
-    /**
1210
-     *        get Attendee Is Going
1211
-     */
1212
-    public function att_is_going()
1213
-    {
1214
-        return $this->get('REG_att_is_going');
1215
-    }
1216
-
1217
-
1218
-    /**
1219
-     * Gets related answers
1220
-     *
1221
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1222
-     * @return EE_Answer[]
1223
-     * @throws EE_Error
1224
-     */
1225
-    public function answers($query_params = null)
1226
-    {
1227
-        return $this->get_many_related('Answer', $query_params);
1228
-    }
1229
-
1230
-
1231
-    /**
1232
-     * Gets the registration's answer value to the specified question
1233
-     * (either the question's ID or a question object)
1234
-     *
1235
-     * @param EE_Question|int $question
1236
-     * @param bool            $pretty_value
1237
-     * @return array|string if pretty_value= true, the result will always be a string
1238
-     * (because the answer might be an array of answer values, so passing pretty_value=true
1239
-     * will convert it into some kind of string)
1240
-     * @throws EE_Error
1241
-     */
1242
-    public function answer_value_to_question($question, $pretty_value = true)
1243
-    {
1244
-        $question_id = EEM_Question::instance()->ensure_is_ID($question);
1245
-        return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1246
-    }
1247
-
1248
-
1249
-    /**
1250
-     * question_groups
1251
-     * returns an array of EE_Question_Group objects for this registration
1252
-     *
1253
-     * @return EE_Question_Group[]
1254
-     * @throws EE_Error
1255
-     * @throws EntityNotFoundException
1256
-     */
1257
-    public function question_groups()
1258
-    {
1259
-        $question_groups = array();
1260
-        if ($this->event() instanceof EE_Event) {
1261
-            $question_groups = $this->event()->question_groups(
1262
-                array(
1263
-                    array(
1264
-                        'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1265
-                    ),
1266
-                    'order_by' => array('QSG_order' => 'ASC'),
1267
-                )
1268
-            );
1269
-        }
1270
-        return $question_groups;
1271
-    }
1272
-
1273
-
1274
-    /**
1275
-     * count_question_groups
1276
-     * returns a count of the number of EE_Question_Group objects for this registration
1277
-     *
1278
-     * @return int
1279
-     * @throws EE_Error
1280
-     * @throws EntityNotFoundException
1281
-     */
1282
-    public function count_question_groups()
1283
-    {
1284
-        $qg_count = 0;
1285
-        if ($this->event() instanceof EE_Event) {
1286
-            $qg_count = $this->event()->count_related(
1287
-                'Question_Group',
1288
-                array(
1289
-                    array(
1290
-                        'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1291
-                    ),
1292
-                )
1293
-            );
1294
-        }
1295
-        return $qg_count;
1296
-    }
1297
-
1298
-
1299
-    /**
1300
-     * Returns the registration date in the 'standard' string format
1301
-     * (function may be improved in the future to allow for different formats and timezones)
1302
-     *
1303
-     * @return string
1304
-     * @throws EE_Error
1305
-     */
1306
-    public function reg_date()
1307
-    {
1308
-        return $this->get_datetime('REG_date');
1309
-    }
1310
-
1311
-
1312
-    /**
1313
-     * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1314
-     * the ticket this registration purchased, or the datetime they have registered
1315
-     * to attend)
1316
-     *
1317
-     * @return EE_Datetime_Ticket
1318
-     * @throws EE_Error
1319
-     */
1320
-    public function datetime_ticket()
1321
-    {
1322
-        return $this->get_first_related('Datetime_Ticket');
1323
-    }
1324
-
1325
-
1326
-    /**
1327
-     * Sets the registration's datetime_ticket.
1328
-     *
1329
-     * @param EE_Datetime_Ticket $datetime_ticket
1330
-     * @return EE_Datetime_Ticket
1331
-     * @throws EE_Error
1332
-     */
1333
-    public function set_datetime_ticket($datetime_ticket)
1334
-    {
1335
-        return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1336
-    }
1337
-
1338
-    /**
1339
-     * Gets deleted
1340
-     *
1341
-     * @return bool
1342
-     * @throws EE_Error
1343
-     */
1344
-    public function deleted()
1345
-    {
1346
-        return $this->get('REG_deleted');
1347
-    }
1348
-
1349
-    /**
1350
-     * Sets deleted
1351
-     *
1352
-     * @param boolean $deleted
1353
-     * @return bool
1354
-     * @throws EE_Error
1355
-     * @throws RuntimeException
1356
-     */
1357
-    public function set_deleted($deleted)
1358
-    {
1359
-        if ($deleted) {
1360
-            $this->delete();
1361
-        } else {
1362
-            $this->restore();
1363
-        }
1364
-    }
1365
-
1366
-
1367
-    /**
1368
-     * Get the status object of this object
1369
-     *
1370
-     * @return EE_Status
1371
-     * @throws EE_Error
1372
-     */
1373
-    public function status_obj()
1374
-    {
1375
-        return $this->get_first_related('Status');
1376
-    }
1377
-
1378
-
1379
-    /**
1380
-     * Returns the number of times this registration has checked into any of the datetimes
1381
-     * its available for
1382
-     *
1383
-     * @return int
1384
-     * @throws EE_Error
1385
-     */
1386
-    public function count_checkins()
1387
-    {
1388
-        return $this->get_model()->count_related($this, 'Checkin');
1389
-    }
1390
-
1391
-
1392
-    /**
1393
-     * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1394
-     * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1395
-     *
1396
-     * @return int
1397
-     * @throws EE_Error
1398
-     */
1399
-    public function count_checkins_not_checkedout()
1400
-    {
1401
-        return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1402
-    }
1403
-
1404
-
1405
-    /**
1406
-     * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1407
-     *
1408
-     * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1409
-     * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1410
-     *                                          consider registration status as well as datetime access.
1411
-     * @return bool
1412
-     * @throws EE_Error
1413
-     */
1414
-    public function can_checkin($DTT_OR_ID, $check_approved = true)
1415
-    {
1416
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1417
-
1418
-        // first check registration status
1419
-        if (($check_approved && ! $this->is_approved()) || ! $DTT_ID) {
1420
-            return false;
1421
-        }
1422
-        // is there a datetime ticket that matches this dtt_ID?
1423
-        if (! (EEM_Datetime_Ticket::instance()->exists(
1424
-            array(
1425
-                array(
1426
-                    'TKT_ID' => $this->get('TKT_ID'),
1427
-                    'DTT_ID' => $DTT_ID,
1428
-                ),
1429
-            )
1430
-        ))
1431
-        ) {
1432
-            return false;
1433
-        }
1434
-
1435
-        // final check is against TKT_uses
1436
-        return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1437
-    }
1438
-
1439
-
1440
-    /**
1441
-     * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1442
-     * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1443
-     * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1444
-     * then return false.  Otherwise return true.
1445
-     *
1446
-     * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1447
-     * @return bool true means can checkin.  false means cannot checkin.
1448
-     * @throws EE_Error
1449
-     */
1450
-    public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1451
-    {
1452
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1453
-
1454
-        if (! $DTT_ID) {
1455
-            return false;
1456
-        }
1457
-
1458
-        $max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1459
-
1460
-        // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1461
-        // check-in or not.
1462
-        if (! $max_uses || $max_uses === EE_INF) {
1463
-            return true;
1464
-        }
1465
-
1466
-        // does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1467
-        // go ahead and toggle.
1468
-        if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1469
-            return true;
1470
-        }
1471
-
1472
-        // made it here so the last check is whether the number of checkins per unique datetime on this registration
1473
-        // disallows further check-ins.
1474
-        $count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1475
-            array(
1476
-                array(
1477
-                    'REG_ID' => $this->ID(),
1478
-                    'CHK_in' => true,
1479
-                ),
1480
-            ),
1481
-            'DTT_ID',
1482
-            true
1483
-        );
1484
-        // checkins have already reached their max number of uses
1485
-        // so registrant can NOT checkin
1486
-        if ($count_unique_dtt_checkins >= $max_uses) {
1487
-            EE_Error::add_error(
1488
-                esc_html__(
1489
-                    'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1490
-                    'event_espresso'
1491
-                ),
1492
-                __FILE__,
1493
-                __FUNCTION__,
1494
-                __LINE__
1495
-            );
1496
-            return false;
1497
-        }
1498
-        return true;
1499
-    }
1500
-
1501
-
1502
-    /**
1503
-     * toggle Check-in status for this registration
1504
-     * Check-ins are toggled in the following order:
1505
-     * never checked in -> checked in
1506
-     * checked in -> checked out
1507
-     * checked out -> checked in
1508
-     *
1509
-     * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1510
-     *                      If not included or null, then it is assumed latest datetime is being toggled.
1511
-     * @param bool $verify  If true then can_checkin() is used to verify whether the person
1512
-     *                      can be checked in or not.  Otherwise this forces change in checkin status.
1513
-     * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1514
-     * @throws EE_Error
1515
-     */
1516
-    public function toggle_checkin_status($DTT_ID = null, $verify = false)
1517
-    {
1518
-        if (empty($DTT_ID)) {
1519
-            $datetime = $this->get_latest_related_datetime();
1520
-            $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1521
-            // verify the registration can checkin for the given DTT_ID
1522
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1523
-            EE_Error::add_error(
1524
-                sprintf(
1525
-                    esc_html__(
1526
-                        'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1527
-                        'event_espresso'
1528
-                    ),
1529
-                    $this->ID(),
1530
-                    $DTT_ID
1531
-                ),
1532
-                __FILE__,
1533
-                __FUNCTION__,
1534
-                __LINE__
1535
-            );
1536
-            return false;
1537
-        }
1538
-        $status_paths = array(
1539
-            EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1540
-            EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1541
-            EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1542
-        );
1543
-        // start by getting the current status so we know what status we'll be changing to.
1544
-        $cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1545
-        $status_to = $status_paths[ $cur_status ];
1546
-        // database only records true for checked IN or false for checked OUT
1547
-        // no record ( null ) means checked in NEVER, but we obviously don't save that
1548
-        $new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
1549
-        // add relation - note Check-ins are always creating new rows
1550
-        // because we are keeping track of Check-ins over time.
1551
-        // Eventually we'll probably want to show a list table
1552
-        // for the individual Check-ins so that they can be managed.
1553
-        $checkin = EE_Checkin::new_instance(
1554
-            array(
1555
-                'REG_ID' => $this->ID(),
1556
-                'DTT_ID' => $DTT_ID,
1557
-                'CHK_in' => $new_status,
1558
-            )
1559
-        );
1560
-        // if the record could not be saved then return false
1561
-        if ($checkin->save() === 0) {
1562
-            if (WP_DEBUG) {
1563
-                global $wpdb;
1564
-                $error = sprintf(
1565
-                    esc_html__(
1566
-                        'Registration check in update failed because of the following database error: %1$s%2$s',
1567
-                        'event_espresso'
1568
-                    ),
1569
-                    '<br />',
1570
-                    $wpdb->last_error
1571
-                );
1572
-            } else {
1573
-                $error = esc_html__(
1574
-                    'Registration check in update failed because of an unknown database error',
1575
-                    'event_espresso'
1576
-                );
1577
-            }
1578
-            EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1579
-            return false;
1580
-        }
1581
-        return $status_to;
1582
-    }
1583
-
1584
-
1585
-    /**
1586
-     * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1587
-     * "Latest" is defined by the `DTT_EVT_start` column.
1588
-     *
1589
-     * @return EE_Datetime|null
1590
-     * @throws EE_Error
1591
-     */
1592
-    public function get_latest_related_datetime()
1593
-    {
1594
-        return EEM_Datetime::instance()->get_one(
1595
-            array(
1596
-                array(
1597
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1598
-                ),
1599
-                'order_by' => array('DTT_EVT_start' => 'DESC'),
1600
-            )
1601
-        );
1602
-    }
1603
-
1604
-
1605
-    /**
1606
-     * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1607
-     * "Earliest" is defined by the `DTT_EVT_start` column.
1608
-     *
1609
-     * @throws EE_Error
1610
-     */
1611
-    public function get_earliest_related_datetime()
1612
-    {
1613
-        return EEM_Datetime::instance()->get_one(
1614
-            array(
1615
-                array(
1616
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1617
-                ),
1618
-                'order_by' => array('DTT_EVT_start' => 'ASC'),
1619
-            )
1620
-        );
1621
-    }
1622
-
1623
-
1624
-    /**
1625
-     * This method simply returns the check-in status for this registration and the given datetime.
1626
-     * If neither the datetime nor the checkin values are provided as arguments,
1627
-     * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1628
-     *
1629
-     * @param  int       $DTT_ID  The ID of the datetime we're checking against
1630
-     *                            (if empty we'll get the primary datetime for
1631
-     *                            this registration (via event) and use it's ID);
1632
-     * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1633
-     *
1634
-     * @return int                Integer representing Check-in status.
1635
-     * @throws EE_Error
1636
-     */
1637
-    public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1638
-    {
1639
-        $checkin_query_params = array(
1640
-            'order_by' => array('CHK_timestamp' => 'DESC'),
1641
-        );
1642
-
1643
-        if ($DTT_ID > 0) {
1644
-            $checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1645
-        }
1646
-
1647
-        // get checkin object (if exists)
1648
-        $checkin = $checkin instanceof EE_Checkin
1649
-            ? $checkin
1650
-            : $this->get_first_related('Checkin', $checkin_query_params);
1651
-        if ($checkin instanceof EE_Checkin) {
1652
-            if ($checkin->get('CHK_in')) {
1653
-                return EE_Checkin::status_checked_in; // checked in
1654
-            }
1655
-            return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1656
-        }
1657
-        return EE_Checkin::status_checked_never; // never been checked in
1658
-    }
1659
-
1660
-
1661
-    /**
1662
-     * This method returns a localized message for the toggled Check-in message.
1663
-     *
1664
-     * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1665
-     *                     then it is assumed Check-in for primary datetime was toggled.
1666
-     * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1667
-     *                     message can be customized with the attendee name.
1668
-     * @return string internationalized message
1669
-     * @throws EE_Error
1670
-     */
1671
-    public function get_checkin_msg($DTT_ID, $error = false)
1672
-    {
1673
-        // let's get the attendee first so we can include the name of the attendee
1674
-        $attendee = $this->get_first_related('Attendee');
1675
-        if ($attendee instanceof EE_Attendee) {
1676
-            if ($error) {
1677
-                return sprintf(__("%s's check-in status was not changed.", "event_espresso"), $attendee->full_name());
1678
-            }
1679
-            $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1680
-            // what is the status message going to be?
1681
-            switch ($cur_status) {
1682
-                case EE_Checkin::status_checked_never:
1683
-                    return sprintf(
1684
-                        __("%s has been removed from Check-in records", "event_espresso"),
1685
-                        $attendee->full_name()
1686
-                    );
1687
-                    break;
1688
-                case EE_Checkin::status_checked_in:
1689
-                    return sprintf(__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1690
-                    break;
1691
-                case EE_Checkin::status_checked_out:
1692
-                    return sprintf(__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1693
-                    break;
1694
-            }
1695
-        }
1696
-        return esc_html__("The check-in status could not be determined.", "event_espresso");
1697
-    }
1698
-
1699
-
1700
-    /**
1701
-     * Returns the related EE_Transaction to this registration
1702
-     *
1703
-     * @return EE_Transaction
1704
-     * @throws EE_Error
1705
-     * @throws EntityNotFoundException
1706
-     */
1707
-    public function transaction()
1708
-    {
1709
-        $transaction = $this->get_first_related('Transaction');
1710
-        if (! $transaction instanceof \EE_Transaction) {
1711
-            throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1712
-        }
1713
-        return $transaction;
1714
-    }
1715
-
1716
-
1717
-    /**
1718
-     *        get Registration Code
1719
-     */
1720
-    public function reg_code()
1721
-    {
1722
-        return $this->get('REG_code');
1723
-    }
1724
-
1725
-
1726
-    /**
1727
-     *        get Transaction ID
1728
-     */
1729
-    public function transaction_ID()
1730
-    {
1731
-        return $this->get('TXN_ID');
1732
-    }
1733
-
1734
-
1735
-    /**
1736
-     * @return int
1737
-     * @throws EE_Error
1738
-     */
1739
-    public function ticket_ID()
1740
-    {
1741
-        return $this->get('TKT_ID');
1742
-    }
1743
-
1744
-
1745
-    /**
1746
-     *        Set Registration Code
1747
-     *
1748
-     * @access    public
1749
-     * @param    string  $REG_code Registration Code
1750
-     * @param    boolean $use_default
1751
-     * @throws EE_Error
1752
-     */
1753
-    public function set_reg_code($REG_code, $use_default = false)
1754
-    {
1755
-        if (empty($REG_code)) {
1756
-            EE_Error::add_error(
1757
-                esc_html__('REG_code can not be empty.', 'event_espresso'),
1758
-                __FILE__,
1759
-                __FUNCTION__,
1760
-                __LINE__
1761
-            );
1762
-            return;
1763
-        }
1764
-        if (! $this->reg_code()) {
1765
-            parent::set('REG_code', $REG_code, $use_default);
1766
-        } else {
1767
-            EE_Error::doing_it_wrong(
1768
-                __CLASS__ . '::' . __FUNCTION__,
1769
-                esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1770
-                '4.6.0'
1771
-            );
1772
-        }
1773
-    }
1774
-
1775
-
1776
-    /**
1777
-     * Returns all other registrations in the same group as this registrant who have the same ticket option.
1778
-     * Note, if you want to just get all registrations in the same transaction (group), use:
1779
-     *    $registration->transaction()->registrations();
1780
-     *
1781
-     * @since 4.5.0
1782
-     * @return EE_Registration[] or empty array if this isn't a group registration.
1783
-     * @throws EE_Error
1784
-     */
1785
-    public function get_all_other_registrations_in_group()
1786
-    {
1787
-        if ($this->group_size() < 2) {
1788
-            return array();
1789
-        }
1790
-
1791
-        $query[0] = array(
1792
-            'TXN_ID' => $this->transaction_ID(),
1793
-            'REG_ID' => array('!=', $this->ID()),
1794
-            'TKT_ID' => $this->ticket_ID(),
1795
-        );
1796
-        /** @var EE_Registration[] $registrations */
1797
-        $registrations = $this->get_model()->get_all($query);
1798
-        return $registrations;
1799
-    }
1800
-
1801
-    /**
1802
-     * Return the link to the admin details for the object.
1803
-     *
1804
-     * @return string
1805
-     * @throws EE_Error
1806
-     */
1807
-    public function get_admin_details_link()
1808
-    {
1809
-        EE_Registry::instance()->load_helper('URL');
1810
-        return EEH_URL::add_query_args_and_nonce(
1811
-            array(
1812
-                'page'    => 'espresso_registrations',
1813
-                'action'  => 'view_registration',
1814
-                '_REG_ID' => $this->ID(),
1815
-            ),
1816
-            admin_url('admin.php')
1817
-        );
1818
-    }
1819
-
1820
-    /**
1821
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
1822
-     *
1823
-     * @return string
1824
-     * @throws EE_Error
1825
-     */
1826
-    public function get_admin_edit_link()
1827
-    {
1828
-        return $this->get_admin_details_link();
1829
-    }
1830
-
1831
-    /**
1832
-     * Returns the link to a settings page for the object.
1833
-     *
1834
-     * @return string
1835
-     * @throws EE_Error
1836
-     */
1837
-    public function get_admin_settings_link()
1838
-    {
1839
-        return $this->get_admin_details_link();
1840
-    }
1841
-
1842
-    /**
1843
-     * Returns the link to the "overview" for the object (typically the "list table" view).
1844
-     *
1845
-     * @return string
1846
-     */
1847
-    public function get_admin_overview_link()
1848
-    {
1849
-        EE_Registry::instance()->load_helper('URL');
1850
-        return EEH_URL::add_query_args_and_nonce(
1851
-            array(
1852
-                'page' => 'espresso_registrations',
1853
-            ),
1854
-            admin_url('admin.php')
1855
-        );
1856
-    }
1857
-
1858
-
1859
-    /**
1860
-     * @param array $query_params
1861
-     *
1862
-     * @return \EE_Registration[]
1863
-     * @throws EE_Error
1864
-     */
1865
-    public function payments($query_params = array())
1866
-    {
1867
-        return $this->get_many_related('Payment', $query_params);
1868
-    }
1869
-
1870
-
1871
-    /**
1872
-     * @param array $query_params
1873
-     *
1874
-     * @return \EE_Registration_Payment[]
1875
-     * @throws EE_Error
1876
-     */
1877
-    public function registration_payments($query_params = array())
1878
-    {
1879
-        return $this->get_many_related('Registration_Payment', $query_params);
1880
-    }
1881
-
1882
-
1883
-    /**
1884
-     * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
1885
-     * Note: if there are no payments on the registration there will be no payment method returned.
1886
-     *
1887
-     * @return EE_Payment_Method|null
1888
-     */
1889
-    public function payment_method()
1890
-    {
1891
-        return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
1892
-    }
1893
-
1894
-
1895
-    /**
1896
-     * @return \EE_Line_Item
1897
-     * @throws EntityNotFoundException
1898
-     * @throws EE_Error
1899
-     */
1900
-    public function ticket_line_item()
1901
-    {
1902
-        $ticket = $this->ticket();
1903
-        $transaction = $this->transaction();
1904
-        $line_item = null;
1905
-        $ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
1906
-            $transaction->total_line_item(),
1907
-            'Ticket',
1908
-            array($ticket->ID())
1909
-        );
1910
-        foreach ($ticket_line_items as $ticket_line_item) {
1911
-            if ($ticket_line_item instanceof \EE_Line_Item
1912
-                && $ticket_line_item->OBJ_type() === 'Ticket'
1913
-                && $ticket_line_item->OBJ_ID() === $ticket->ID()
1914
-            ) {
1915
-                $line_item = $ticket_line_item;
1916
-                break;
1917
-            }
1918
-        }
1919
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1920
-            throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1921
-        }
1922
-        return $line_item;
1923
-    }
1924
-
1925
-
1926
-    /**
1927
-     * Soft Deletes this model object.
1928
-     *
1929
-     * @return boolean | int
1930
-     * @throws RuntimeException
1931
-     * @throws EE_Error
1932
-     */
1933
-    public function delete()
1934
-    {
1935
-        if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
1936
-            $this->set_status(EEM_Registration::status_id_cancelled);
1937
-        }
1938
-        return parent::delete();
1939
-    }
1940
-
1941
-
1942
-    /**
1943
-     * Restores whatever the previous status was on a registration before it was trashed (if possible)
1944
-     *
1945
-     * @throws EE_Error
1946
-     * @throws RuntimeException
1947
-     */
1948
-    public function restore()
1949
-    {
1950
-        $previous_status = $this->get_extra_meta(
1951
-            EE_Registration::PRE_TRASH_REG_STATUS_KEY,
1952
-            true,
1953
-            EEM_Registration::status_id_cancelled
1954
-        );
1955
-        if ($previous_status) {
1956
-            $this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
1957
-            $this->set_status($previous_status);
1958
-        }
1959
-        return parent::restore();
1960
-    }
1961
-
1962
-
1963
-    /**
1964
-     * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
1965
-     *
1966
-     * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
1967
-     *                                           depending on whether the reg status changes to or from "Approved"
1968
-     * @return boolean whether the Registration status was updated
1969
-     * @throws EE_Error
1970
-     * @throws RuntimeException
1971
-     */
1972
-    public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
1973
-    {
1974
-        $paid = $this->paid();
1975
-        $price = $this->final_price();
1976
-        switch (true) {
1977
-            // overpaid or paid
1978
-            case EEH_Money::compare_floats($paid, $price, '>'):
1979
-            case EEH_Money::compare_floats($paid, $price):
1980
-                $new_status = EEM_Registration::status_id_approved;
1981
-                break;
1982
-            //  underpaid
1983
-            case EEH_Money::compare_floats($paid, $price, '<'):
1984
-                $new_status = EEM_Registration::status_id_pending_payment;
1985
-                break;
1986
-            // uhhh Houston...
1987
-            default:
1988
-                throw new RuntimeException(
1989
-                    esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
1990
-                );
1991
-        }
1992
-        if ($new_status !== $this->status_ID()) {
1993
-            if ($trigger_set_status_logic) {
1994
-                return $this->set_status($new_status);
1995
-            }
1996
-            parent::set('STS_ID', $new_status);
1997
-            return true;
1998
-        }
1999
-        return false;
2000
-    }
2001
-
2002
-
2003
-    /*************************** DEPRECATED ***************************/
2004
-
2005
-
2006
-    /**
2007
-     * @deprecated
2008
-     * @since     4.7.0
2009
-     * @access    public
2010
-     */
2011
-    public function price_paid()
2012
-    {
2013
-        EE_Error::doing_it_wrong(
2014
-            'EE_Registration::price_paid()',
2015
-            esc_html__(
2016
-                'This method is deprecated, please use EE_Registration::final_price() instead.',
2017
-                'event_espresso'
2018
-            ),
2019
-            '4.7.0'
2020
-        );
2021
-        return $this->final_price();
2022
-    }
2023
-
2024
-
2025
-    /**
2026
-     * @deprecated
2027
-     * @since     4.7.0
2028
-     * @access    public
2029
-     * @param    float $REG_final_price
2030
-     * @throws EE_Error
2031
-     * @throws RuntimeException
2032
-     */
2033
-    public function set_price_paid($REG_final_price = 0.00)
2034
-    {
2035
-        EE_Error::doing_it_wrong(
2036
-            'EE_Registration::set_price_paid()',
2037
-            esc_html__(
2038
-                'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2039
-                'event_espresso'
2040
-            ),
2041
-            '4.7.0'
2042
-        );
2043
-        $this->set_final_price($REG_final_price);
2044
-    }
2045
-
2046
-
2047
-    /**
2048
-     * @deprecated
2049
-     * @since 4.7.0
2050
-     * @return string
2051
-     * @throws EE_Error
2052
-     */
2053
-    public function pretty_price_paid()
2054
-    {
2055
-        EE_Error::doing_it_wrong(
2056
-            'EE_Registration::pretty_price_paid()',
2057
-            esc_html__(
2058
-                'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2059
-                'event_espresso'
2060
-            ),
2061
-            '4.7.0'
2062
-        );
2063
-        return $this->pretty_final_price();
2064
-    }
2065
-
2066
-
2067
-    /**
2068
-     * Gets the primary datetime related to this registration via the related Event to this registration
2069
-     *
2070
-     * @deprecated 4.9.17
2071
-     * @return EE_Datetime
2072
-     * @throws EE_Error
2073
-     * @throws EntityNotFoundException
2074
-     */
2075
-    public function get_related_primary_datetime()
2076
-    {
2077
-        EE_Error::doing_it_wrong(
2078
-            __METHOD__,
2079
-            esc_html__(
2080
-                'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2081
-                'event_espresso'
2082
-            ),
2083
-            '4.9.17',
2084
-            '5.0.0'
2085
-        );
2086
-        return $this->event()->primary_datetime();
2087
-    }
20
+	/**
21
+	 * Used to reference when a registration has never been checked in.
22
+	 *
23
+	 * @deprecated use \EE_Checkin::status_checked_never instead
24
+	 * @type int
25
+	 */
26
+	const checkin_status_never = 2;
27
+
28
+	/**
29
+	 * Used to reference when a registration has been checked in.
30
+	 *
31
+	 * @deprecated use \EE_Checkin::status_checked_in instead
32
+	 * @type int
33
+	 */
34
+	const checkin_status_in = 1;
35
+
36
+
37
+	/**
38
+	 * Used to reference when a registration has been checked out.
39
+	 *
40
+	 * @deprecated use \EE_Checkin::status_checked_out instead
41
+	 * @type int
42
+	 */
43
+	const checkin_status_out = 0;
44
+
45
+
46
+	/**
47
+	 * extra meta key for tracking reg status os trashed registrations
48
+	 *
49
+	 * @type string
50
+	 */
51
+	const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
52
+
53
+
54
+	/**
55
+	 * extra meta key for tracking if registration has reserved ticket
56
+	 *
57
+	 * @type string
58
+	 */
59
+	const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
60
+
61
+
62
+	/**
63
+	 * @param array  $props_n_values          incoming values
64
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
65
+	 *                                        used.)
66
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
67
+	 *                                        date_format and the second value is the time format
68
+	 * @return EE_Registration
69
+	 * @throws EE_Error
70
+	 */
71
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
+	{
73
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
+	}
76
+
77
+
78
+	/**
79
+	 * @param array  $props_n_values  incoming values from the database
80
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
+	 *                                the website will be used.
82
+	 * @return EE_Registration
83
+	 */
84
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
85
+	{
86
+		return new self($props_n_values, true, $timezone);
87
+	}
88
+
89
+
90
+	/**
91
+	 *        Set Event ID
92
+	 *
93
+	 * @param        int $EVT_ID Event ID
94
+	 * @throws EE_Error
95
+	 * @throws RuntimeException
96
+	 */
97
+	public function set_event($EVT_ID = 0)
98
+	{
99
+		$this->set('EVT_ID', $EVT_ID);
100
+	}
101
+
102
+
103
+	/**
104
+	 * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
105
+	 * be routed to internal methods
106
+	 *
107
+	 * @param string $field_name
108
+	 * @param mixed  $field_value
109
+	 * @param bool   $use_default
110
+	 * @throws EE_Error
111
+	 * @throws EntityNotFoundException
112
+	 * @throws InvalidArgumentException
113
+	 * @throws InvalidDataTypeException
114
+	 * @throws InvalidInterfaceException
115
+	 * @throws ReflectionException
116
+	 * @throws RuntimeException
117
+	 */
118
+	public function set($field_name, $field_value, $use_default = false)
119
+	{
120
+		switch ($field_name) {
121
+			case 'REG_code':
122
+				if (! empty($field_value) && $this->reg_code() === null) {
123
+					$this->set_reg_code($field_value, $use_default);
124
+				}
125
+				break;
126
+			case 'STS_ID':
127
+				$this->set_status($field_value, $use_default);
128
+				break;
129
+			default:
130
+				parent::set($field_name, $field_value, $use_default);
131
+		}
132
+	}
133
+
134
+
135
+	/**
136
+	 * Set Status ID
137
+	 * updates the registration status and ALSO...
138
+	 * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
139
+	 * calls release_registration_space() if the reg status changes FROM approved to any other reg status
140
+	 *
141
+	 * @param string                $new_STS_ID
142
+	 * @param boolean               $use_default
143
+	 * @param ContextInterface|null $context
144
+	 * @return bool
145
+	 * @throws EE_Error
146
+	 * @throws EntityNotFoundException
147
+	 * @throws InvalidArgumentException
148
+	 * @throws ReflectionException
149
+	 * @throws RuntimeException
150
+	 * @throws InvalidDataTypeException
151
+	 * @throws InvalidInterfaceException
152
+	 */
153
+	public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
154
+	{
155
+		// get current REG_Status
156
+		$old_STS_ID = $this->status_ID();
157
+		// if status has changed
158
+		if ($old_STS_ID !== $new_STS_ID // and that status has actually changed
159
+			&& ! empty($old_STS_ID) // and that old status is actually set
160
+			&& ! empty($new_STS_ID) // as well as the new status
161
+			&& $this->ID() // ensure registration is in the db
162
+		) {
163
+			// update internal status first
164
+			parent::set('STS_ID', $new_STS_ID, $use_default);
165
+			// THEN handle other changes that occur when reg status changes
166
+			// TO approved
167
+			if ($new_STS_ID === EEM_Registration::status_id_approved) {
168
+				// reserve a space by incrementing ticket and datetime sold values
169
+				$this->_reserve_registration_space();
170
+				do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
171
+				// OR FROM  approved
172
+			} elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
173
+				// release a space by decrementing ticket and datetime sold values
174
+				$this->_release_registration_space();
175
+				do_action(
176
+					'AHEE__EE_Registration__set_status__from_approved',
177
+					$this,
178
+					$old_STS_ID,
179
+					$new_STS_ID,
180
+					$context
181
+				);
182
+			}
183
+			$this->_update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, $context);
184
+			if ($this->statusChangeUpdatesTransaction($context)) {
185
+				$this->updateTransactionAfterStatusChange();
186
+			}
187
+			do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
188
+			return true;
189
+		}
190
+		// even though the old value matches the new value, it's still good to
191
+		// allow the parent set method to have a say
192
+		parent::set('STS_ID', $new_STS_ID, $use_default);
193
+		return true;
194
+	}
195
+
196
+
197
+	/**
198
+	 * update REGs and TXN when cancelled or declined registrations involved
199
+	 *
200
+	 * @param string                $new_STS_ID
201
+	 * @param string                $old_STS_ID
202
+	 * @param ContextInterface|null $context
203
+	 * @throws EE_Error
204
+	 * @throws InvalidArgumentException
205
+	 * @throws InvalidDataTypeException
206
+	 * @throws InvalidInterfaceException
207
+	 * @throws ReflectionException
208
+	 */
209
+	private function _update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
210
+	{
211
+		// these reg statuses should not be considered in any calculations involving monies owing
212
+		$closed_reg_statuses = EEM_Registration::closed_reg_statuses();
213
+		// true if registration has been cancelled or declined
214
+		$this->updateIfCanceled(
215
+			$closed_reg_statuses,
216
+			$new_STS_ID,
217
+			$old_STS_ID,
218
+			$context
219
+		);
220
+		$this->updateIfDeclined(
221
+			$closed_reg_statuses,
222
+			$new_STS_ID,
223
+			$old_STS_ID,
224
+			$context
225
+		);
226
+	}
227
+
228
+
229
+	/**
230
+	 * update REGs and TXN when cancelled or declined registrations involved
231
+	 *
232
+	 * @param array                 $closed_reg_statuses
233
+	 * @param string                $new_STS_ID
234
+	 * @param string                $old_STS_ID
235
+	 * @param ContextInterface|null $context
236
+	 * @throws EE_Error
237
+	 * @throws InvalidArgumentException
238
+	 * @throws InvalidDataTypeException
239
+	 * @throws InvalidInterfaceException
240
+	 * @throws ReflectionException
241
+	 */
242
+	private function updateIfCanceled(
243
+		array $closed_reg_statuses,
244
+		$new_STS_ID,
245
+		$old_STS_ID,
246
+		ContextInterface $context = null
247
+	) {
248
+		// true if registration has been cancelled or declined
249
+		if (in_array($new_STS_ID, $closed_reg_statuses, true)
250
+			&& ! in_array($old_STS_ID, $closed_reg_statuses, true)
251
+		) {
252
+			/** @type EE_Registration_Processor $registration_processor */
253
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
254
+			/** @type EE_Transaction_Processor $transaction_processor */
255
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
256
+			// cancelled or declined registration
257
+			$registration_processor->update_registration_after_being_canceled_or_declined(
258
+				$this,
259
+				$closed_reg_statuses
260
+			);
261
+			$transaction_processor->update_transaction_after_canceled_or_declined_registration(
262
+				$this,
263
+				$closed_reg_statuses,
264
+				false
265
+			);
266
+			do_action(
267
+				'AHEE__EE_Registration__set_status__canceled_or_declined',
268
+				$this,
269
+				$old_STS_ID,
270
+				$new_STS_ID,
271
+				$context
272
+			);
273
+			return;
274
+		}
275
+	}
276
+
277
+
278
+	/**
279
+	 * update REGs and TXN when cancelled or declined registrations involved
280
+	 *
281
+	 * @param array                 $closed_reg_statuses
282
+	 * @param string                $new_STS_ID
283
+	 * @param string                $old_STS_ID
284
+	 * @param ContextInterface|null $context
285
+	 * @throws EE_Error
286
+	 * @throws InvalidArgumentException
287
+	 * @throws InvalidDataTypeException
288
+	 * @throws InvalidInterfaceException
289
+	 * @throws ReflectionException
290
+	 */
291
+	private function updateIfDeclined(
292
+		array $closed_reg_statuses,
293
+		$new_STS_ID,
294
+		$old_STS_ID,
295
+		ContextInterface $context = null
296
+	) {
297
+		// true if reinstating cancelled or declined registration
298
+		if (in_array($old_STS_ID, $closed_reg_statuses, true)
299
+			&& ! in_array($new_STS_ID, $closed_reg_statuses, true)
300
+		) {
301
+			/** @type EE_Registration_Processor $registration_processor */
302
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
303
+			/** @type EE_Transaction_Processor $transaction_processor */
304
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
305
+			// reinstating cancelled or declined registration
306
+			$registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
307
+				$this,
308
+				$closed_reg_statuses
309
+			);
310
+			$transaction_processor->update_transaction_after_reinstating_canceled_registration(
311
+				$this,
312
+				$closed_reg_statuses,
313
+				false
314
+			);
315
+			do_action(
316
+				'AHEE__EE_Registration__set_status__after_reinstated',
317
+				$this,
318
+				$old_STS_ID,
319
+				$new_STS_ID,
320
+				$context
321
+			);
322
+		}
323
+	}
324
+
325
+
326
+	/**
327
+	 * @param ContextInterface|null $context
328
+	 * @return bool
329
+	 */
330
+	private function statusChangeUpdatesTransaction(ContextInterface $context = null)
331
+	{
332
+		$contexts_that_do_not_update_transaction = (array) apply_filters(
333
+			'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
334
+			array('spco_reg_step_attendee_information_process_registrations'),
335
+			$context,
336
+			$this
337
+		);
338
+		return ! (
339
+			$context instanceof ContextInterface
340
+			&& in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
341
+		);
342
+	}
343
+
344
+
345
+	/**
346
+	 * @throws EE_Error
347
+	 * @throws EntityNotFoundException
348
+	 * @throws InvalidArgumentException
349
+	 * @throws InvalidDataTypeException
350
+	 * @throws InvalidInterfaceException
351
+	 * @throws ReflectionException
352
+	 * @throws RuntimeException
353
+	 */
354
+	private function updateTransactionAfterStatusChange()
355
+	{
356
+		/** @type EE_Transaction_Payments $transaction_payments */
357
+		$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
358
+		$transaction_payments->recalculate_transaction_total($this->transaction(), false);
359
+		$this->transaction()->update_status_based_on_total_paid(true);
360
+	}
361
+
362
+
363
+	/**
364
+	 *        get Status ID
365
+	 */
366
+	public function status_ID()
367
+	{
368
+		return $this->get('STS_ID');
369
+	}
370
+
371
+
372
+	/**
373
+	 * Gets the ticket this registration is for
374
+	 *
375
+	 * @param boolean $include_archived whether to include archived tickets or not.
376
+	 *
377
+	 * @return EE_Ticket|EE_Base_Class
378
+	 * @throws EE_Error
379
+	 */
380
+	public function ticket($include_archived = true)
381
+	{
382
+		$query_params = array();
383
+		if ($include_archived) {
384
+			$query_params['default_where_conditions'] = 'none';
385
+		}
386
+		return $this->get_first_related('Ticket', $query_params);
387
+	}
388
+
389
+
390
+	/**
391
+	 * Gets the event this registration is for
392
+	 *
393
+	 * @return EE_Event
394
+	 * @throws EE_Error
395
+	 * @throws EntityNotFoundException
396
+	 */
397
+	public function event()
398
+	{
399
+		$event = $this->get_first_related('Event');
400
+		if (! $event instanceof \EE_Event) {
401
+			throw new EntityNotFoundException('Event ID', $this->event_ID());
402
+		}
403
+		return $event;
404
+	}
405
+
406
+
407
+	/**
408
+	 * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
409
+	 * with the author of the event this registration is for.
410
+	 *
411
+	 * @since 4.5.0
412
+	 * @return int
413
+	 * @throws EE_Error
414
+	 * @throws EntityNotFoundException
415
+	 */
416
+	public function wp_user()
417
+	{
418
+		$event = $this->event();
419
+		if ($event instanceof EE_Event) {
420
+			return $event->wp_user();
421
+		}
422
+		return 0;
423
+	}
424
+
425
+
426
+	/**
427
+	 * increments this registration's related ticket sold and corresponding datetime sold values
428
+	 *
429
+	 * @return void
430
+	 * @throws DomainException
431
+	 * @throws EE_Error
432
+	 * @throws EntityNotFoundException
433
+	 * @throws InvalidArgumentException
434
+	 * @throws InvalidDataTypeException
435
+	 * @throws InvalidInterfaceException
436
+	 * @throws ReflectionException
437
+	 * @throws UnexpectedEntityException
438
+	 */
439
+	private function _reserve_registration_space()
440
+	{
441
+		// reserved ticket and datetime counts will be decremented as sold counts are incremented
442
+		// so stop tracking that this reg has a ticket reserved
443
+		$this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
444
+		$ticket = $this->ticket();
445
+		$ticket->increase_sold();
446
+		$ticket->save();
447
+		// possibly set event status to sold out
448
+		$this->event()->perform_sold_out_status_check();
449
+	}
450
+
451
+
452
+	/**
453
+	 * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
454
+	 *
455
+	 * @return void
456
+	 * @throws DomainException
457
+	 * @throws EE_Error
458
+	 * @throws EntityNotFoundException
459
+	 * @throws InvalidArgumentException
460
+	 * @throws InvalidDataTypeException
461
+	 * @throws InvalidInterfaceException
462
+	 * @throws ReflectionException
463
+	 * @throws UnexpectedEntityException
464
+	 */
465
+	private function _release_registration_space()
466
+	{
467
+		$ticket = $this->ticket();
468
+		$ticket->decrease_sold();
469
+		$ticket->save();
470
+		// possibly change event status from sold out back to previous status
471
+		$this->event()->perform_sold_out_status_check();
472
+	}
473
+
474
+
475
+	/**
476
+	 * tracks this registration's ticket reservation in extra meta
477
+	 * and can increment related ticket reserved and corresponding datetime reserved values
478
+	 *
479
+	 * @param bool $update_ticket if true, will increment ticket and datetime reserved count
480
+	 * @return void
481
+	 * @throws EE_Error
482
+	 * @throws InvalidArgumentException
483
+	 * @throws InvalidDataTypeException
484
+	 * @throws InvalidInterfaceException
485
+	 * @throws ReflectionException
486
+	 */
487
+	public function reserve_ticket($update_ticket = false, $source = 'unknown')
488
+	{
489
+		// only reserve ticket if space is not currently reserved
490
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
491
+			$this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
492
+			// IMPORTANT !!!
493
+			// although checking $update_ticket first would be more efficient,
494
+			// we NEED to ALWAYS call update_extra_meta(), which is why that is done first
495
+			if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true)
496
+				&& $update_ticket
497
+			) {
498
+				$ticket = $this->ticket();
499
+				$ticket->increase_reserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
500
+				$ticket->save();
501
+			}
502
+		}
503
+	}
504
+
505
+
506
+	/**
507
+	 * stops tracking this registration's ticket reservation in extra meta
508
+	 * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
509
+	 *
510
+	 * @param bool $update_ticket if true, will decrement ticket and datetime reserved count
511
+	 * @return void
512
+	 * @throws EE_Error
513
+	 * @throws InvalidArgumentException
514
+	 * @throws InvalidDataTypeException
515
+	 * @throws InvalidInterfaceException
516
+	 * @throws ReflectionException
517
+	 */
518
+	public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
519
+	{
520
+		// only release ticket if space is currently reserved
521
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
522
+			$this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
523
+			// IMPORTANT !!!
524
+			// although checking $update_ticket first would be more efficient,
525
+			// we NEED to ALWAYS call update_extra_meta(), which is why that is done first
526
+			if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false)
527
+				&& $update_ticket
528
+			) {
529
+				$ticket = $this->ticket();
530
+				$ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
531
+				$ticket->save();
532
+			}
533
+		}
534
+	}
535
+
536
+
537
+	/**
538
+	 * Set Attendee ID
539
+	 *
540
+	 * @param        int $ATT_ID Attendee ID
541
+	 * @throws EE_Error
542
+	 * @throws RuntimeException
543
+	 */
544
+	public function set_attendee_id($ATT_ID = 0)
545
+	{
546
+		$this->set('ATT_ID', $ATT_ID);
547
+	}
548
+
549
+
550
+	/**
551
+	 *        Set Transaction ID
552
+	 *
553
+	 * @param        int $TXN_ID Transaction ID
554
+	 * @throws EE_Error
555
+	 * @throws RuntimeException
556
+	 */
557
+	public function set_transaction_id($TXN_ID = 0)
558
+	{
559
+		$this->set('TXN_ID', $TXN_ID);
560
+	}
561
+
562
+
563
+	/**
564
+	 *        Set Session
565
+	 *
566
+	 * @param    string $REG_session PHP Session ID
567
+	 * @throws EE_Error
568
+	 * @throws RuntimeException
569
+	 */
570
+	public function set_session($REG_session = '')
571
+	{
572
+		$this->set('REG_session', $REG_session);
573
+	}
574
+
575
+
576
+	/**
577
+	 *        Set Registration URL Link
578
+	 *
579
+	 * @param    string $REG_url_link Registration URL Link
580
+	 * @throws EE_Error
581
+	 * @throws RuntimeException
582
+	 */
583
+	public function set_reg_url_link($REG_url_link = '')
584
+	{
585
+		$this->set('REG_url_link', $REG_url_link);
586
+	}
587
+
588
+
589
+	/**
590
+	 *        Set Attendee Counter
591
+	 *
592
+	 * @param        int $REG_count Primary Attendee
593
+	 * @throws EE_Error
594
+	 * @throws RuntimeException
595
+	 */
596
+	public function set_count($REG_count = 1)
597
+	{
598
+		$this->set('REG_count', $REG_count);
599
+	}
600
+
601
+
602
+	/**
603
+	 *        Set Group Size
604
+	 *
605
+	 * @param        boolean $REG_group_size Group Registration
606
+	 * @throws EE_Error
607
+	 * @throws RuntimeException
608
+	 */
609
+	public function set_group_size($REG_group_size = false)
610
+	{
611
+		$this->set('REG_group_size', $REG_group_size);
612
+	}
613
+
614
+
615
+	/**
616
+	 *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
617
+	 *    EEM_Registration::status_id_not_approved
618
+	 *
619
+	 * @return        boolean
620
+	 */
621
+	public function is_not_approved()
622
+	{
623
+		return $this->status_ID() == EEM_Registration::status_id_not_approved ? true : false;
624
+	}
625
+
626
+
627
+	/**
628
+	 *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
629
+	 *    EEM_Registration::status_id_pending_payment
630
+	 *
631
+	 * @return        boolean
632
+	 */
633
+	public function is_pending_payment()
634
+	{
635
+		return $this->status_ID() == EEM_Registration::status_id_pending_payment ? true : false;
636
+	}
637
+
638
+
639
+	/**
640
+	 *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
641
+	 *
642
+	 * @return        boolean
643
+	 */
644
+	public function is_approved()
645
+	{
646
+		return $this->status_ID() == EEM_Registration::status_id_approved ? true : false;
647
+	}
648
+
649
+
650
+	/**
651
+	 *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
652
+	 *
653
+	 * @return        boolean
654
+	 */
655
+	public function is_cancelled()
656
+	{
657
+		return $this->status_ID() == EEM_Registration::status_id_cancelled ? true : false;
658
+	}
659
+
660
+
661
+	/**
662
+	 *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
663
+	 *
664
+	 * @return        boolean
665
+	 */
666
+	public function is_declined()
667
+	{
668
+		return $this->status_ID() == EEM_Registration::status_id_declined ? true : false;
669
+	}
670
+
671
+
672
+	/**
673
+	 *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
674
+	 *    EEM_Registration::status_id_incomplete
675
+	 *
676
+	 * @return        boolean
677
+	 */
678
+	public function is_incomplete()
679
+	{
680
+		return $this->status_ID() == EEM_Registration::status_id_incomplete ? true : false;
681
+	}
682
+
683
+
684
+	/**
685
+	 *        Set Registration Date
686
+	 *
687
+	 * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
688
+	 *                                                 Date
689
+	 * @throws EE_Error
690
+	 * @throws RuntimeException
691
+	 */
692
+	public function set_reg_date($REG_date = false)
693
+	{
694
+		$this->set('REG_date', $REG_date);
695
+	}
696
+
697
+
698
+	/**
699
+	 *    Set final price owing for this registration after all ticket/price modifications
700
+	 *
701
+	 * @access    public
702
+	 * @param    float $REG_final_price
703
+	 * @throws EE_Error
704
+	 * @throws RuntimeException
705
+	 */
706
+	public function set_final_price($REG_final_price = 0.00)
707
+	{
708
+		$this->set('REG_final_price', $REG_final_price);
709
+	}
710
+
711
+
712
+	/**
713
+	 *    Set amount paid towards this registration's final price
714
+	 *
715
+	 * @access    public
716
+	 * @param    float $REG_paid
717
+	 * @throws EE_Error
718
+	 * @throws RuntimeException
719
+	 */
720
+	public function set_paid($REG_paid = 0.00)
721
+	{
722
+		$this->set('REG_paid', $REG_paid);
723
+	}
724
+
725
+
726
+	/**
727
+	 *        Attendee Is Going
728
+	 *
729
+	 * @param        boolean $REG_att_is_going Attendee Is Going
730
+	 * @throws EE_Error
731
+	 * @throws RuntimeException
732
+	 */
733
+	public function set_att_is_going($REG_att_is_going = false)
734
+	{
735
+		$this->set('REG_att_is_going', $REG_att_is_going);
736
+	}
737
+
738
+
739
+	/**
740
+	 * Gets the related attendee
741
+	 *
742
+	 * @return EE_Attendee
743
+	 * @throws EE_Error
744
+	 */
745
+	public function attendee()
746
+	{
747
+		return $this->get_first_related('Attendee');
748
+	}
749
+
750
+
751
+	/**
752
+	 *        get Event ID
753
+	 */
754
+	public function event_ID()
755
+	{
756
+		return $this->get('EVT_ID');
757
+	}
758
+
759
+
760
+	/**
761
+	 *        get Event ID
762
+	 */
763
+	public function event_name()
764
+	{
765
+		$event = $this->event_obj();
766
+		if ($event) {
767
+			return $event->name();
768
+		} else {
769
+			return null;
770
+		}
771
+	}
772
+
773
+
774
+	/**
775
+	 * Fetches the event this registration is for
776
+	 *
777
+	 * @return EE_Event
778
+	 * @throws EE_Error
779
+	 */
780
+	public function event_obj()
781
+	{
782
+		return $this->get_first_related('Event');
783
+	}
784
+
785
+
786
+	/**
787
+	 *        get Attendee ID
788
+	 */
789
+	public function attendee_ID()
790
+	{
791
+		return $this->get('ATT_ID');
792
+	}
793
+
794
+
795
+	/**
796
+	 *        get PHP Session ID
797
+	 */
798
+	public function session_ID()
799
+	{
800
+		return $this->get('REG_session');
801
+	}
802
+
803
+
804
+	/**
805
+	 * Gets the string which represents the URL trigger for the receipt template in the message template system.
806
+	 *
807
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
808
+	 * @return string
809
+	 */
810
+	public function receipt_url($messenger = 'html')
811
+	{
812
+
813
+		/**
814
+		 * The below will be deprecated one version after this.  We check first if there is a custom receipt template
815
+		 * already in use on old system.  If there is then we just return the standard url for it.
816
+		 *
817
+		 * @since 4.5.0
818
+		 */
819
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
820
+		$has_custom = EEH_Template::locate_template(
821
+			$template_relative_path,
822
+			array(),
823
+			true,
824
+			true,
825
+			true
826
+		);
827
+
828
+		if ($has_custom) {
829
+			return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
830
+		}
831
+		return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
832
+	}
833
+
834
+
835
+	/**
836
+	 * Gets the string which represents the URL trigger for the invoice template in the message template system.
837
+	 *
838
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
839
+	 * @return string
840
+	 * @throws EE_Error
841
+	 */
842
+	public function invoice_url($messenger = 'html')
843
+	{
844
+		/**
845
+		 * The below will be deprecated one version after this.  We check first if there is a custom invoice template
846
+		 * already in use on old system.  If there is then we just return the standard url for it.
847
+		 *
848
+		 * @since 4.5.0
849
+		 */
850
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
851
+		$has_custom = EEH_Template::locate_template(
852
+			$template_relative_path,
853
+			array(),
854
+			true,
855
+			true,
856
+			true
857
+		);
858
+
859
+		if ($has_custom) {
860
+			if ($messenger == 'html') {
861
+				return $this->invoice_url('launch');
862
+			}
863
+			$route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
864
+
865
+			$query_args = array('ee' => $route, 'id' => $this->reg_url_link());
866
+			if ($messenger == 'html') {
867
+				$query_args['html'] = true;
868
+			}
869
+			return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
870
+		}
871
+		return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
872
+	}
873
+
874
+
875
+	/**
876
+	 * get Registration URL Link
877
+	 *
878
+	 * @access public
879
+	 * @return string
880
+	 * @throws EE_Error
881
+	 */
882
+	public function reg_url_link()
883
+	{
884
+		return (string) $this->get('REG_url_link');
885
+	}
886
+
887
+
888
+	/**
889
+	 * Echoes out invoice_url()
890
+	 *
891
+	 * @param string $type 'download','launch', or 'html' (default is 'launch')
892
+	 * @return void
893
+	 * @throws EE_Error
894
+	 */
895
+	public function e_invoice_url($type = 'launch')
896
+	{
897
+		echo $this->invoice_url($type);
898
+	}
899
+
900
+
901
+	/**
902
+	 * Echoes out payment_overview_url
903
+	 */
904
+	public function e_payment_overview_url()
905
+	{
906
+		echo $this->payment_overview_url();
907
+	}
908
+
909
+
910
+	/**
911
+	 * Gets the URL for the checkout payment options reg step
912
+	 * with this registration's REG_url_link added as a query parameter
913
+	 *
914
+	 * @param bool $clear_session Set to true when you want to clear the session on revisiting the
915
+	 *                            payment overview url.
916
+	 * @return string
917
+	 * @throws InvalidInterfaceException
918
+	 * @throws InvalidDataTypeException
919
+	 * @throws EE_Error
920
+	 * @throws InvalidArgumentException
921
+	 */
922
+	public function payment_overview_url($clear_session = false)
923
+	{
924
+		return add_query_arg(
925
+			(array) apply_filters(
926
+				'FHEE__EE_Registration__payment_overview_url__query_args',
927
+				array(
928
+					'e_reg_url_link' => $this->reg_url_link(),
929
+					'step'           => 'payment_options',
930
+					'revisit'        => true,
931
+					'clear_session'  => (bool) $clear_session,
932
+				),
933
+				$this
934
+			),
935
+			EE_Registry::instance()->CFG->core->reg_page_url()
936
+		);
937
+	}
938
+
939
+
940
+	/**
941
+	 * Gets the URL for the checkout attendee information reg step
942
+	 * with this registration's REG_url_link added as a query parameter
943
+	 *
944
+	 * @return string
945
+	 * @throws InvalidInterfaceException
946
+	 * @throws InvalidDataTypeException
947
+	 * @throws EE_Error
948
+	 * @throws InvalidArgumentException
949
+	 */
950
+	public function edit_attendee_information_url()
951
+	{
952
+		return add_query_arg(
953
+			(array) apply_filters(
954
+				'FHEE__EE_Registration__edit_attendee_information_url__query_args',
955
+				array(
956
+					'e_reg_url_link' => $this->reg_url_link(),
957
+					'step'           => 'attendee_information',
958
+					'revisit'        => true,
959
+				),
960
+				$this
961
+			),
962
+			EE_Registry::instance()->CFG->core->reg_page_url()
963
+		);
964
+	}
965
+
966
+
967
+	/**
968
+	 * Simply generates and returns the appropriate admin_url link to edit this registration
969
+	 *
970
+	 * @return string
971
+	 * @throws EE_Error
972
+	 */
973
+	public function get_admin_edit_url()
974
+	{
975
+		return EEH_URL::add_query_args_and_nonce(
976
+			array(
977
+				'page'    => 'espresso_registrations',
978
+				'action'  => 'view_registration',
979
+				'_REG_ID' => $this->ID(),
980
+			),
981
+			admin_url('admin.php')
982
+		);
983
+	}
984
+
985
+
986
+	/**
987
+	 *    is_primary_registrant?
988
+	 */
989
+	public function is_primary_registrant()
990
+	{
991
+		return $this->get('REG_count') == 1 ? true : false;
992
+	}
993
+
994
+
995
+	/**
996
+	 * This returns the primary registration object for this registration group (which may be this object).
997
+	 *
998
+	 * @return EE_Registration
999
+	 * @throws EE_Error
1000
+	 */
1001
+	public function get_primary_registration()
1002
+	{
1003
+		if ($this->is_primary_registrant()) {
1004
+			return $this;
1005
+		}
1006
+
1007
+		// k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1008
+		/** @var EE_Registration $primary_registrant */
1009
+		$primary_registrant = EEM_Registration::instance()->get_one(
1010
+			array(
1011
+				array(
1012
+					'TXN_ID'    => $this->transaction_ID(),
1013
+					'REG_count' => 1,
1014
+				),
1015
+			)
1016
+		);
1017
+		return $primary_registrant;
1018
+	}
1019
+
1020
+
1021
+	/**
1022
+	 *        get  Attendee Number
1023
+	 *
1024
+	 * @access        public
1025
+	 */
1026
+	public function count()
1027
+	{
1028
+		return $this->get('REG_count');
1029
+	}
1030
+
1031
+
1032
+	/**
1033
+	 *        get Group Size
1034
+	 */
1035
+	public function group_size()
1036
+	{
1037
+		return $this->get('REG_group_size');
1038
+	}
1039
+
1040
+
1041
+	/**
1042
+	 *        get Registration Date
1043
+	 */
1044
+	public function date()
1045
+	{
1046
+		return $this->get('REG_date');
1047
+	}
1048
+
1049
+
1050
+	/**
1051
+	 * gets a pretty date
1052
+	 *
1053
+	 * @param string $date_format
1054
+	 * @param string $time_format
1055
+	 * @return string
1056
+	 * @throws EE_Error
1057
+	 */
1058
+	public function pretty_date($date_format = null, $time_format = null)
1059
+	{
1060
+		return $this->get_datetime('REG_date', $date_format, $time_format);
1061
+	}
1062
+
1063
+
1064
+	/**
1065
+	 * final_price
1066
+	 * the registration's share of the transaction total, so that the
1067
+	 * sum of all the transaction's REG_final_prices equal the transaction's total
1068
+	 *
1069
+	 * @return float
1070
+	 * @throws EE_Error
1071
+	 */
1072
+	public function final_price()
1073
+	{
1074
+		return $this->get('REG_final_price');
1075
+	}
1076
+
1077
+
1078
+	/**
1079
+	 * pretty_final_price
1080
+	 *  final price as formatted string, with correct decimal places and currency symbol
1081
+	 *
1082
+	 * @return string
1083
+	 * @throws EE_Error
1084
+	 */
1085
+	public function pretty_final_price()
1086
+	{
1087
+		return $this->get_pretty('REG_final_price');
1088
+	}
1089
+
1090
+
1091
+	/**
1092
+	 * get paid (yeah)
1093
+	 *
1094
+	 * @return float
1095
+	 * @throws EE_Error
1096
+	 */
1097
+	public function paid()
1098
+	{
1099
+		return $this->get('REG_paid');
1100
+	}
1101
+
1102
+
1103
+	/**
1104
+	 * pretty_paid
1105
+	 *
1106
+	 * @return float
1107
+	 * @throws EE_Error
1108
+	 */
1109
+	public function pretty_paid()
1110
+	{
1111
+		return $this->get_pretty('REG_paid');
1112
+	}
1113
+
1114
+
1115
+	/**
1116
+	 * owes_monies_and_can_pay
1117
+	 * whether or not this registration has monies owing and it's' status allows payment
1118
+	 *
1119
+	 * @param array $requires_payment
1120
+	 * @return bool
1121
+	 * @throws EE_Error
1122
+	 */
1123
+	public function owes_monies_and_can_pay($requires_payment = array())
1124
+	{
1125
+		// these reg statuses require payment (if event is not free)
1126
+		$requires_payment = ! empty($requires_payment)
1127
+			? $requires_payment
1128
+			: EEM_Registration::reg_statuses_that_allow_payment();
1129
+		if (in_array($this->status_ID(), $requires_payment) &&
1130
+			$this->final_price() != 0 &&
1131
+			$this->final_price() != $this->paid()
1132
+		) {
1133
+			return true;
1134
+		} else {
1135
+			return false;
1136
+		}
1137
+	}
1138
+
1139
+
1140
+	/**
1141
+	 * Prints out the return value of $this->pretty_status()
1142
+	 *
1143
+	 * @param bool $show_icons
1144
+	 * @return void
1145
+	 * @throws EE_Error
1146
+	 */
1147
+	public function e_pretty_status($show_icons = false)
1148
+	{
1149
+		echo $this->pretty_status($show_icons);
1150
+	}
1151
+
1152
+
1153
+	/**
1154
+	 * Returns a nice version of the status for displaying to customers
1155
+	 *
1156
+	 * @param bool $show_icons
1157
+	 * @return string
1158
+	 * @throws EE_Error
1159
+	 */
1160
+	public function pretty_status($show_icons = false)
1161
+	{
1162
+		$status = EEM_Status::instance()->localized_status(
1163
+			array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1164
+			false,
1165
+			'sentence'
1166
+		);
1167
+		$icon = '';
1168
+		switch ($this->status_ID()) {
1169
+			case EEM_Registration::status_id_approved:
1170
+				$icon = $show_icons
1171
+					? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1172
+					: '';
1173
+				break;
1174
+			case EEM_Registration::status_id_pending_payment:
1175
+				$icon = $show_icons
1176
+					? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1177
+					: '';
1178
+				break;
1179
+			case EEM_Registration::status_id_not_approved:
1180
+				$icon = $show_icons
1181
+					? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1182
+					: '';
1183
+				break;
1184
+			case EEM_Registration::status_id_cancelled:
1185
+				$icon = $show_icons
1186
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1187
+					: '';
1188
+				break;
1189
+			case EEM_Registration::status_id_incomplete:
1190
+				$icon = $show_icons
1191
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1192
+					: '';
1193
+				break;
1194
+			case EEM_Registration::status_id_declined:
1195
+				$icon = $show_icons
1196
+					? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1197
+					: '';
1198
+				break;
1199
+			case EEM_Registration::status_id_wait_list:
1200
+				$icon = $show_icons
1201
+					? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1202
+					: '';
1203
+				break;
1204
+		}
1205
+		return $icon . $status[ $this->status_ID() ];
1206
+	}
1207
+
1208
+
1209
+	/**
1210
+	 *        get Attendee Is Going
1211
+	 */
1212
+	public function att_is_going()
1213
+	{
1214
+		return $this->get('REG_att_is_going');
1215
+	}
1216
+
1217
+
1218
+	/**
1219
+	 * Gets related answers
1220
+	 *
1221
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1222
+	 * @return EE_Answer[]
1223
+	 * @throws EE_Error
1224
+	 */
1225
+	public function answers($query_params = null)
1226
+	{
1227
+		return $this->get_many_related('Answer', $query_params);
1228
+	}
1229
+
1230
+
1231
+	/**
1232
+	 * Gets the registration's answer value to the specified question
1233
+	 * (either the question's ID or a question object)
1234
+	 *
1235
+	 * @param EE_Question|int $question
1236
+	 * @param bool            $pretty_value
1237
+	 * @return array|string if pretty_value= true, the result will always be a string
1238
+	 * (because the answer might be an array of answer values, so passing pretty_value=true
1239
+	 * will convert it into some kind of string)
1240
+	 * @throws EE_Error
1241
+	 */
1242
+	public function answer_value_to_question($question, $pretty_value = true)
1243
+	{
1244
+		$question_id = EEM_Question::instance()->ensure_is_ID($question);
1245
+		return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1246
+	}
1247
+
1248
+
1249
+	/**
1250
+	 * question_groups
1251
+	 * returns an array of EE_Question_Group objects for this registration
1252
+	 *
1253
+	 * @return EE_Question_Group[]
1254
+	 * @throws EE_Error
1255
+	 * @throws EntityNotFoundException
1256
+	 */
1257
+	public function question_groups()
1258
+	{
1259
+		$question_groups = array();
1260
+		if ($this->event() instanceof EE_Event) {
1261
+			$question_groups = $this->event()->question_groups(
1262
+				array(
1263
+					array(
1264
+						'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1265
+					),
1266
+					'order_by' => array('QSG_order' => 'ASC'),
1267
+				)
1268
+			);
1269
+		}
1270
+		return $question_groups;
1271
+	}
1272
+
1273
+
1274
+	/**
1275
+	 * count_question_groups
1276
+	 * returns a count of the number of EE_Question_Group objects for this registration
1277
+	 *
1278
+	 * @return int
1279
+	 * @throws EE_Error
1280
+	 * @throws EntityNotFoundException
1281
+	 */
1282
+	public function count_question_groups()
1283
+	{
1284
+		$qg_count = 0;
1285
+		if ($this->event() instanceof EE_Event) {
1286
+			$qg_count = $this->event()->count_related(
1287
+				'Question_Group',
1288
+				array(
1289
+					array(
1290
+						'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1291
+					),
1292
+				)
1293
+			);
1294
+		}
1295
+		return $qg_count;
1296
+	}
1297
+
1298
+
1299
+	/**
1300
+	 * Returns the registration date in the 'standard' string format
1301
+	 * (function may be improved in the future to allow for different formats and timezones)
1302
+	 *
1303
+	 * @return string
1304
+	 * @throws EE_Error
1305
+	 */
1306
+	public function reg_date()
1307
+	{
1308
+		return $this->get_datetime('REG_date');
1309
+	}
1310
+
1311
+
1312
+	/**
1313
+	 * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1314
+	 * the ticket this registration purchased, or the datetime they have registered
1315
+	 * to attend)
1316
+	 *
1317
+	 * @return EE_Datetime_Ticket
1318
+	 * @throws EE_Error
1319
+	 */
1320
+	public function datetime_ticket()
1321
+	{
1322
+		return $this->get_first_related('Datetime_Ticket');
1323
+	}
1324
+
1325
+
1326
+	/**
1327
+	 * Sets the registration's datetime_ticket.
1328
+	 *
1329
+	 * @param EE_Datetime_Ticket $datetime_ticket
1330
+	 * @return EE_Datetime_Ticket
1331
+	 * @throws EE_Error
1332
+	 */
1333
+	public function set_datetime_ticket($datetime_ticket)
1334
+	{
1335
+		return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1336
+	}
1337
+
1338
+	/**
1339
+	 * Gets deleted
1340
+	 *
1341
+	 * @return bool
1342
+	 * @throws EE_Error
1343
+	 */
1344
+	public function deleted()
1345
+	{
1346
+		return $this->get('REG_deleted');
1347
+	}
1348
+
1349
+	/**
1350
+	 * Sets deleted
1351
+	 *
1352
+	 * @param boolean $deleted
1353
+	 * @return bool
1354
+	 * @throws EE_Error
1355
+	 * @throws RuntimeException
1356
+	 */
1357
+	public function set_deleted($deleted)
1358
+	{
1359
+		if ($deleted) {
1360
+			$this->delete();
1361
+		} else {
1362
+			$this->restore();
1363
+		}
1364
+	}
1365
+
1366
+
1367
+	/**
1368
+	 * Get the status object of this object
1369
+	 *
1370
+	 * @return EE_Status
1371
+	 * @throws EE_Error
1372
+	 */
1373
+	public function status_obj()
1374
+	{
1375
+		return $this->get_first_related('Status');
1376
+	}
1377
+
1378
+
1379
+	/**
1380
+	 * Returns the number of times this registration has checked into any of the datetimes
1381
+	 * its available for
1382
+	 *
1383
+	 * @return int
1384
+	 * @throws EE_Error
1385
+	 */
1386
+	public function count_checkins()
1387
+	{
1388
+		return $this->get_model()->count_related($this, 'Checkin');
1389
+	}
1390
+
1391
+
1392
+	/**
1393
+	 * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1394
+	 * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1395
+	 *
1396
+	 * @return int
1397
+	 * @throws EE_Error
1398
+	 */
1399
+	public function count_checkins_not_checkedout()
1400
+	{
1401
+		return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1402
+	}
1403
+
1404
+
1405
+	/**
1406
+	 * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1407
+	 *
1408
+	 * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1409
+	 * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1410
+	 *                                          consider registration status as well as datetime access.
1411
+	 * @return bool
1412
+	 * @throws EE_Error
1413
+	 */
1414
+	public function can_checkin($DTT_OR_ID, $check_approved = true)
1415
+	{
1416
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1417
+
1418
+		// first check registration status
1419
+		if (($check_approved && ! $this->is_approved()) || ! $DTT_ID) {
1420
+			return false;
1421
+		}
1422
+		// is there a datetime ticket that matches this dtt_ID?
1423
+		if (! (EEM_Datetime_Ticket::instance()->exists(
1424
+			array(
1425
+				array(
1426
+					'TKT_ID' => $this->get('TKT_ID'),
1427
+					'DTT_ID' => $DTT_ID,
1428
+				),
1429
+			)
1430
+		))
1431
+		) {
1432
+			return false;
1433
+		}
1434
+
1435
+		// final check is against TKT_uses
1436
+		return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1437
+	}
1438
+
1439
+
1440
+	/**
1441
+	 * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1442
+	 * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1443
+	 * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1444
+	 * then return false.  Otherwise return true.
1445
+	 *
1446
+	 * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1447
+	 * @return bool true means can checkin.  false means cannot checkin.
1448
+	 * @throws EE_Error
1449
+	 */
1450
+	public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1451
+	{
1452
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1453
+
1454
+		if (! $DTT_ID) {
1455
+			return false;
1456
+		}
1457
+
1458
+		$max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1459
+
1460
+		// if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1461
+		// check-in or not.
1462
+		if (! $max_uses || $max_uses === EE_INF) {
1463
+			return true;
1464
+		}
1465
+
1466
+		// does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1467
+		// go ahead and toggle.
1468
+		if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1469
+			return true;
1470
+		}
1471
+
1472
+		// made it here so the last check is whether the number of checkins per unique datetime on this registration
1473
+		// disallows further check-ins.
1474
+		$count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1475
+			array(
1476
+				array(
1477
+					'REG_ID' => $this->ID(),
1478
+					'CHK_in' => true,
1479
+				),
1480
+			),
1481
+			'DTT_ID',
1482
+			true
1483
+		);
1484
+		// checkins have already reached their max number of uses
1485
+		// so registrant can NOT checkin
1486
+		if ($count_unique_dtt_checkins >= $max_uses) {
1487
+			EE_Error::add_error(
1488
+				esc_html__(
1489
+					'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1490
+					'event_espresso'
1491
+				),
1492
+				__FILE__,
1493
+				__FUNCTION__,
1494
+				__LINE__
1495
+			);
1496
+			return false;
1497
+		}
1498
+		return true;
1499
+	}
1500
+
1501
+
1502
+	/**
1503
+	 * toggle Check-in status for this registration
1504
+	 * Check-ins are toggled in the following order:
1505
+	 * never checked in -> checked in
1506
+	 * checked in -> checked out
1507
+	 * checked out -> checked in
1508
+	 *
1509
+	 * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1510
+	 *                      If not included or null, then it is assumed latest datetime is being toggled.
1511
+	 * @param bool $verify  If true then can_checkin() is used to verify whether the person
1512
+	 *                      can be checked in or not.  Otherwise this forces change in checkin status.
1513
+	 * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1514
+	 * @throws EE_Error
1515
+	 */
1516
+	public function toggle_checkin_status($DTT_ID = null, $verify = false)
1517
+	{
1518
+		if (empty($DTT_ID)) {
1519
+			$datetime = $this->get_latest_related_datetime();
1520
+			$DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1521
+			// verify the registration can checkin for the given DTT_ID
1522
+		} elseif (! $this->can_checkin($DTT_ID, $verify)) {
1523
+			EE_Error::add_error(
1524
+				sprintf(
1525
+					esc_html__(
1526
+						'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1527
+						'event_espresso'
1528
+					),
1529
+					$this->ID(),
1530
+					$DTT_ID
1531
+				),
1532
+				__FILE__,
1533
+				__FUNCTION__,
1534
+				__LINE__
1535
+			);
1536
+			return false;
1537
+		}
1538
+		$status_paths = array(
1539
+			EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1540
+			EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1541
+			EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1542
+		);
1543
+		// start by getting the current status so we know what status we'll be changing to.
1544
+		$cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1545
+		$status_to = $status_paths[ $cur_status ];
1546
+		// database only records true for checked IN or false for checked OUT
1547
+		// no record ( null ) means checked in NEVER, but we obviously don't save that
1548
+		$new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
1549
+		// add relation - note Check-ins are always creating new rows
1550
+		// because we are keeping track of Check-ins over time.
1551
+		// Eventually we'll probably want to show a list table
1552
+		// for the individual Check-ins so that they can be managed.
1553
+		$checkin = EE_Checkin::new_instance(
1554
+			array(
1555
+				'REG_ID' => $this->ID(),
1556
+				'DTT_ID' => $DTT_ID,
1557
+				'CHK_in' => $new_status,
1558
+			)
1559
+		);
1560
+		// if the record could not be saved then return false
1561
+		if ($checkin->save() === 0) {
1562
+			if (WP_DEBUG) {
1563
+				global $wpdb;
1564
+				$error = sprintf(
1565
+					esc_html__(
1566
+						'Registration check in update failed because of the following database error: %1$s%2$s',
1567
+						'event_espresso'
1568
+					),
1569
+					'<br />',
1570
+					$wpdb->last_error
1571
+				);
1572
+			} else {
1573
+				$error = esc_html__(
1574
+					'Registration check in update failed because of an unknown database error',
1575
+					'event_espresso'
1576
+				);
1577
+			}
1578
+			EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1579
+			return false;
1580
+		}
1581
+		return $status_to;
1582
+	}
1583
+
1584
+
1585
+	/**
1586
+	 * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1587
+	 * "Latest" is defined by the `DTT_EVT_start` column.
1588
+	 *
1589
+	 * @return EE_Datetime|null
1590
+	 * @throws EE_Error
1591
+	 */
1592
+	public function get_latest_related_datetime()
1593
+	{
1594
+		return EEM_Datetime::instance()->get_one(
1595
+			array(
1596
+				array(
1597
+					'Ticket.Registration.REG_ID' => $this->ID(),
1598
+				),
1599
+				'order_by' => array('DTT_EVT_start' => 'DESC'),
1600
+			)
1601
+		);
1602
+	}
1603
+
1604
+
1605
+	/**
1606
+	 * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1607
+	 * "Earliest" is defined by the `DTT_EVT_start` column.
1608
+	 *
1609
+	 * @throws EE_Error
1610
+	 */
1611
+	public function get_earliest_related_datetime()
1612
+	{
1613
+		return EEM_Datetime::instance()->get_one(
1614
+			array(
1615
+				array(
1616
+					'Ticket.Registration.REG_ID' => $this->ID(),
1617
+				),
1618
+				'order_by' => array('DTT_EVT_start' => 'ASC'),
1619
+			)
1620
+		);
1621
+	}
1622
+
1623
+
1624
+	/**
1625
+	 * This method simply returns the check-in status for this registration and the given datetime.
1626
+	 * If neither the datetime nor the checkin values are provided as arguments,
1627
+	 * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1628
+	 *
1629
+	 * @param  int       $DTT_ID  The ID of the datetime we're checking against
1630
+	 *                            (if empty we'll get the primary datetime for
1631
+	 *                            this registration (via event) and use it's ID);
1632
+	 * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1633
+	 *
1634
+	 * @return int                Integer representing Check-in status.
1635
+	 * @throws EE_Error
1636
+	 */
1637
+	public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1638
+	{
1639
+		$checkin_query_params = array(
1640
+			'order_by' => array('CHK_timestamp' => 'DESC'),
1641
+		);
1642
+
1643
+		if ($DTT_ID > 0) {
1644
+			$checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1645
+		}
1646
+
1647
+		// get checkin object (if exists)
1648
+		$checkin = $checkin instanceof EE_Checkin
1649
+			? $checkin
1650
+			: $this->get_first_related('Checkin', $checkin_query_params);
1651
+		if ($checkin instanceof EE_Checkin) {
1652
+			if ($checkin->get('CHK_in')) {
1653
+				return EE_Checkin::status_checked_in; // checked in
1654
+			}
1655
+			return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1656
+		}
1657
+		return EE_Checkin::status_checked_never; // never been checked in
1658
+	}
1659
+
1660
+
1661
+	/**
1662
+	 * This method returns a localized message for the toggled Check-in message.
1663
+	 *
1664
+	 * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1665
+	 *                     then it is assumed Check-in for primary datetime was toggled.
1666
+	 * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1667
+	 *                     message can be customized with the attendee name.
1668
+	 * @return string internationalized message
1669
+	 * @throws EE_Error
1670
+	 */
1671
+	public function get_checkin_msg($DTT_ID, $error = false)
1672
+	{
1673
+		// let's get the attendee first so we can include the name of the attendee
1674
+		$attendee = $this->get_first_related('Attendee');
1675
+		if ($attendee instanceof EE_Attendee) {
1676
+			if ($error) {
1677
+				return sprintf(__("%s's check-in status was not changed.", "event_espresso"), $attendee->full_name());
1678
+			}
1679
+			$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1680
+			// what is the status message going to be?
1681
+			switch ($cur_status) {
1682
+				case EE_Checkin::status_checked_never:
1683
+					return sprintf(
1684
+						__("%s has been removed from Check-in records", "event_espresso"),
1685
+						$attendee->full_name()
1686
+					);
1687
+					break;
1688
+				case EE_Checkin::status_checked_in:
1689
+					return sprintf(__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1690
+					break;
1691
+				case EE_Checkin::status_checked_out:
1692
+					return sprintf(__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1693
+					break;
1694
+			}
1695
+		}
1696
+		return esc_html__("The check-in status could not be determined.", "event_espresso");
1697
+	}
1698
+
1699
+
1700
+	/**
1701
+	 * Returns the related EE_Transaction to this registration
1702
+	 *
1703
+	 * @return EE_Transaction
1704
+	 * @throws EE_Error
1705
+	 * @throws EntityNotFoundException
1706
+	 */
1707
+	public function transaction()
1708
+	{
1709
+		$transaction = $this->get_first_related('Transaction');
1710
+		if (! $transaction instanceof \EE_Transaction) {
1711
+			throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1712
+		}
1713
+		return $transaction;
1714
+	}
1715
+
1716
+
1717
+	/**
1718
+	 *        get Registration Code
1719
+	 */
1720
+	public function reg_code()
1721
+	{
1722
+		return $this->get('REG_code');
1723
+	}
1724
+
1725
+
1726
+	/**
1727
+	 *        get Transaction ID
1728
+	 */
1729
+	public function transaction_ID()
1730
+	{
1731
+		return $this->get('TXN_ID');
1732
+	}
1733
+
1734
+
1735
+	/**
1736
+	 * @return int
1737
+	 * @throws EE_Error
1738
+	 */
1739
+	public function ticket_ID()
1740
+	{
1741
+		return $this->get('TKT_ID');
1742
+	}
1743
+
1744
+
1745
+	/**
1746
+	 *        Set Registration Code
1747
+	 *
1748
+	 * @access    public
1749
+	 * @param    string  $REG_code Registration Code
1750
+	 * @param    boolean $use_default
1751
+	 * @throws EE_Error
1752
+	 */
1753
+	public function set_reg_code($REG_code, $use_default = false)
1754
+	{
1755
+		if (empty($REG_code)) {
1756
+			EE_Error::add_error(
1757
+				esc_html__('REG_code can not be empty.', 'event_espresso'),
1758
+				__FILE__,
1759
+				__FUNCTION__,
1760
+				__LINE__
1761
+			);
1762
+			return;
1763
+		}
1764
+		if (! $this->reg_code()) {
1765
+			parent::set('REG_code', $REG_code, $use_default);
1766
+		} else {
1767
+			EE_Error::doing_it_wrong(
1768
+				__CLASS__ . '::' . __FUNCTION__,
1769
+				esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1770
+				'4.6.0'
1771
+			);
1772
+		}
1773
+	}
1774
+
1775
+
1776
+	/**
1777
+	 * Returns all other registrations in the same group as this registrant who have the same ticket option.
1778
+	 * Note, if you want to just get all registrations in the same transaction (group), use:
1779
+	 *    $registration->transaction()->registrations();
1780
+	 *
1781
+	 * @since 4.5.0
1782
+	 * @return EE_Registration[] or empty array if this isn't a group registration.
1783
+	 * @throws EE_Error
1784
+	 */
1785
+	public function get_all_other_registrations_in_group()
1786
+	{
1787
+		if ($this->group_size() < 2) {
1788
+			return array();
1789
+		}
1790
+
1791
+		$query[0] = array(
1792
+			'TXN_ID' => $this->transaction_ID(),
1793
+			'REG_ID' => array('!=', $this->ID()),
1794
+			'TKT_ID' => $this->ticket_ID(),
1795
+		);
1796
+		/** @var EE_Registration[] $registrations */
1797
+		$registrations = $this->get_model()->get_all($query);
1798
+		return $registrations;
1799
+	}
1800
+
1801
+	/**
1802
+	 * Return the link to the admin details for the object.
1803
+	 *
1804
+	 * @return string
1805
+	 * @throws EE_Error
1806
+	 */
1807
+	public function get_admin_details_link()
1808
+	{
1809
+		EE_Registry::instance()->load_helper('URL');
1810
+		return EEH_URL::add_query_args_and_nonce(
1811
+			array(
1812
+				'page'    => 'espresso_registrations',
1813
+				'action'  => 'view_registration',
1814
+				'_REG_ID' => $this->ID(),
1815
+			),
1816
+			admin_url('admin.php')
1817
+		);
1818
+	}
1819
+
1820
+	/**
1821
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
1822
+	 *
1823
+	 * @return string
1824
+	 * @throws EE_Error
1825
+	 */
1826
+	public function get_admin_edit_link()
1827
+	{
1828
+		return $this->get_admin_details_link();
1829
+	}
1830
+
1831
+	/**
1832
+	 * Returns the link to a settings page for the object.
1833
+	 *
1834
+	 * @return string
1835
+	 * @throws EE_Error
1836
+	 */
1837
+	public function get_admin_settings_link()
1838
+	{
1839
+		return $this->get_admin_details_link();
1840
+	}
1841
+
1842
+	/**
1843
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
1844
+	 *
1845
+	 * @return string
1846
+	 */
1847
+	public function get_admin_overview_link()
1848
+	{
1849
+		EE_Registry::instance()->load_helper('URL');
1850
+		return EEH_URL::add_query_args_and_nonce(
1851
+			array(
1852
+				'page' => 'espresso_registrations',
1853
+			),
1854
+			admin_url('admin.php')
1855
+		);
1856
+	}
1857
+
1858
+
1859
+	/**
1860
+	 * @param array $query_params
1861
+	 *
1862
+	 * @return \EE_Registration[]
1863
+	 * @throws EE_Error
1864
+	 */
1865
+	public function payments($query_params = array())
1866
+	{
1867
+		return $this->get_many_related('Payment', $query_params);
1868
+	}
1869
+
1870
+
1871
+	/**
1872
+	 * @param array $query_params
1873
+	 *
1874
+	 * @return \EE_Registration_Payment[]
1875
+	 * @throws EE_Error
1876
+	 */
1877
+	public function registration_payments($query_params = array())
1878
+	{
1879
+		return $this->get_many_related('Registration_Payment', $query_params);
1880
+	}
1881
+
1882
+
1883
+	/**
1884
+	 * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
1885
+	 * Note: if there are no payments on the registration there will be no payment method returned.
1886
+	 *
1887
+	 * @return EE_Payment_Method|null
1888
+	 */
1889
+	public function payment_method()
1890
+	{
1891
+		return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
1892
+	}
1893
+
1894
+
1895
+	/**
1896
+	 * @return \EE_Line_Item
1897
+	 * @throws EntityNotFoundException
1898
+	 * @throws EE_Error
1899
+	 */
1900
+	public function ticket_line_item()
1901
+	{
1902
+		$ticket = $this->ticket();
1903
+		$transaction = $this->transaction();
1904
+		$line_item = null;
1905
+		$ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
1906
+			$transaction->total_line_item(),
1907
+			'Ticket',
1908
+			array($ticket->ID())
1909
+		);
1910
+		foreach ($ticket_line_items as $ticket_line_item) {
1911
+			if ($ticket_line_item instanceof \EE_Line_Item
1912
+				&& $ticket_line_item->OBJ_type() === 'Ticket'
1913
+				&& $ticket_line_item->OBJ_ID() === $ticket->ID()
1914
+			) {
1915
+				$line_item = $ticket_line_item;
1916
+				break;
1917
+			}
1918
+		}
1919
+		if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1920
+			throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1921
+		}
1922
+		return $line_item;
1923
+	}
1924
+
1925
+
1926
+	/**
1927
+	 * Soft Deletes this model object.
1928
+	 *
1929
+	 * @return boolean | int
1930
+	 * @throws RuntimeException
1931
+	 * @throws EE_Error
1932
+	 */
1933
+	public function delete()
1934
+	{
1935
+		if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
1936
+			$this->set_status(EEM_Registration::status_id_cancelled);
1937
+		}
1938
+		return parent::delete();
1939
+	}
1940
+
1941
+
1942
+	/**
1943
+	 * Restores whatever the previous status was on a registration before it was trashed (if possible)
1944
+	 *
1945
+	 * @throws EE_Error
1946
+	 * @throws RuntimeException
1947
+	 */
1948
+	public function restore()
1949
+	{
1950
+		$previous_status = $this->get_extra_meta(
1951
+			EE_Registration::PRE_TRASH_REG_STATUS_KEY,
1952
+			true,
1953
+			EEM_Registration::status_id_cancelled
1954
+		);
1955
+		if ($previous_status) {
1956
+			$this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
1957
+			$this->set_status($previous_status);
1958
+		}
1959
+		return parent::restore();
1960
+	}
1961
+
1962
+
1963
+	/**
1964
+	 * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
1965
+	 *
1966
+	 * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
1967
+	 *                                           depending on whether the reg status changes to or from "Approved"
1968
+	 * @return boolean whether the Registration status was updated
1969
+	 * @throws EE_Error
1970
+	 * @throws RuntimeException
1971
+	 */
1972
+	public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
1973
+	{
1974
+		$paid = $this->paid();
1975
+		$price = $this->final_price();
1976
+		switch (true) {
1977
+			// overpaid or paid
1978
+			case EEH_Money::compare_floats($paid, $price, '>'):
1979
+			case EEH_Money::compare_floats($paid, $price):
1980
+				$new_status = EEM_Registration::status_id_approved;
1981
+				break;
1982
+			//  underpaid
1983
+			case EEH_Money::compare_floats($paid, $price, '<'):
1984
+				$new_status = EEM_Registration::status_id_pending_payment;
1985
+				break;
1986
+			// uhhh Houston...
1987
+			default:
1988
+				throw new RuntimeException(
1989
+					esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
1990
+				);
1991
+		}
1992
+		if ($new_status !== $this->status_ID()) {
1993
+			if ($trigger_set_status_logic) {
1994
+				return $this->set_status($new_status);
1995
+			}
1996
+			parent::set('STS_ID', $new_status);
1997
+			return true;
1998
+		}
1999
+		return false;
2000
+	}
2001
+
2002
+
2003
+	/*************************** DEPRECATED ***************************/
2004
+
2005
+
2006
+	/**
2007
+	 * @deprecated
2008
+	 * @since     4.7.0
2009
+	 * @access    public
2010
+	 */
2011
+	public function price_paid()
2012
+	{
2013
+		EE_Error::doing_it_wrong(
2014
+			'EE_Registration::price_paid()',
2015
+			esc_html__(
2016
+				'This method is deprecated, please use EE_Registration::final_price() instead.',
2017
+				'event_espresso'
2018
+			),
2019
+			'4.7.0'
2020
+		);
2021
+		return $this->final_price();
2022
+	}
2023
+
2024
+
2025
+	/**
2026
+	 * @deprecated
2027
+	 * @since     4.7.0
2028
+	 * @access    public
2029
+	 * @param    float $REG_final_price
2030
+	 * @throws EE_Error
2031
+	 * @throws RuntimeException
2032
+	 */
2033
+	public function set_price_paid($REG_final_price = 0.00)
2034
+	{
2035
+		EE_Error::doing_it_wrong(
2036
+			'EE_Registration::set_price_paid()',
2037
+			esc_html__(
2038
+				'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2039
+				'event_espresso'
2040
+			),
2041
+			'4.7.0'
2042
+		);
2043
+		$this->set_final_price($REG_final_price);
2044
+	}
2045
+
2046
+
2047
+	/**
2048
+	 * @deprecated
2049
+	 * @since 4.7.0
2050
+	 * @return string
2051
+	 * @throws EE_Error
2052
+	 */
2053
+	public function pretty_price_paid()
2054
+	{
2055
+		EE_Error::doing_it_wrong(
2056
+			'EE_Registration::pretty_price_paid()',
2057
+			esc_html__(
2058
+				'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2059
+				'event_espresso'
2060
+			),
2061
+			'4.7.0'
2062
+		);
2063
+		return $this->pretty_final_price();
2064
+	}
2065
+
2066
+
2067
+	/**
2068
+	 * Gets the primary datetime related to this registration via the related Event to this registration
2069
+	 *
2070
+	 * @deprecated 4.9.17
2071
+	 * @return EE_Datetime
2072
+	 * @throws EE_Error
2073
+	 * @throws EntityNotFoundException
2074
+	 */
2075
+	public function get_related_primary_datetime()
2076
+	{
2077
+		EE_Error::doing_it_wrong(
2078
+			__METHOD__,
2079
+			esc_html__(
2080
+				'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2081
+				'event_espresso'
2082
+			),
2083
+			'4.9.17',
2084
+			'5.0.0'
2085
+		);
2086
+		return $this->event()->primary_datetime();
2087
+	}
2088 2088
 }
Please login to merge, or discard this patch.