Completed
Branch nk-deployment (d9c3dc)
by
unknown
03:04
created
core/db_classes/EE_Answer.class.php 1 patch
Indentation   +138 added lines, -138 removed lines patch added patch discarded remove patch
@@ -10,144 +10,144 @@
 block discarded – undo
10 10
 class EE_Answer extends EE_Base_Class
11 11
 {
12 12
 
13
-    /**
14
-     *
15
-     * @param array $props_n_values
16
-     * @return EE_Answer
17
-     */
18
-    public static function new_instance($props_n_values = array())
19
-    {
20
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__);
21
-        return $has_object ? $has_object : new self($props_n_values);
22
-    }
23
-
24
-
25
-    /**
26
-     *
27
-     * @param array $props_n_values
28
-     * @return EE_Answer
29
-     */
30
-    public static function new_instance_from_db($props_n_values = array())
31
-    {
32
-        return new self($props_n_values, true);
33
-    }
34
-
35
-
36
-    /**
37
-     *    Set Question ID
38
-     *
39
-     * @access        public
40
-     * @param int $QST_ID
41
-     */
42
-    public function set_question($QST_ID = 0)
43
-    {
44
-        $this->set('QST_ID', $QST_ID);
45
-    }
46
-
47
-
48
-    /**
49
-     *    Set Registration ID
50
-     *
51
-     * @access        public
52
-     * @param int $REG_ID
53
-     */
54
-    public function set_registration($REG_ID = 0)
55
-    {
56
-        $this->set('REG_ID', $REG_ID);
57
-    }
58
-
59
-
60
-    /**
61
-     *    Set Answer value
62
-     *
63
-     * @access        public
64
-     * @param mixed $ANS_value
65
-     */
66
-    public function set_value($ANS_value = '')
67
-    {
68
-        $this->set('ANS_value', $ANS_value);
69
-    }
70
-
71
-
72
-    /**
73
-     *    get Attendee First Name
74
-     *
75
-     * @access        public
76
-     * @return        int
77
-     */
78
-    public function registration_ID()
79
-    {
80
-        return $this->get('REG_ID');
81
-    }
82
-
83
-
84
-    /**
85
-     *    get Attendee Last Name
86
-     *
87
-     * @access        public
88
-     * @return        int
89
-     */
90
-    public function question_ID()
91
-    {
92
-        return $this->get('QST_ID');
93
-    }
94
-
95
-
96
-    /**
97
-     *    get Attendee Address
98
-     *
99
-     * @access        public
100
-     * @return        string
101
-     */
102
-    public function value()
103
-    {
104
-        return $this->get('ANS_value');
105
-    }
106
-
107
-
108
-    /**
109
-     * Gets a pretty form of the value (mostly applies to answers that have multiple answers)
110
-     *
111
-     * @param null $schema
112
-     * @return string
113
-     */
114
-    public function pretty_value($schema = null)
115
-    {
116
-        return $this->get_pretty('ANS_value', $schema);
117
-    }
118
-
119
-
120
-    /**
121
-     * Echoes out a pretty value (even for multi-choice options)
122
-     *
123
-     * @param string $schema
124
-     */
125
-    public function e_value($schema = null)
126
-    {
127
-        $this->e('ANS_value', $schema);
128
-    }
129
-
130
-
131
-    /**
132
-     * Gets the related EE_Question to this EE_Answer
133
-     *
134
-     * @return EE_Question
135
-     */
136
-    public function question()
137
-    {
138
-        return $this->get_first_related('Question');
139
-    }
140
-
141
-
142
-    /**
143
-     * Gets the related EE_Registration to this EE_Answer
144
-     *
145
-     * @return EE_Registration
146
-     */
147
-    public function registration()
148
-    {
149
-        return $this->get_first_related('Registration');
150
-    }
13
+	/**
14
+	 *
15
+	 * @param array $props_n_values
16
+	 * @return EE_Answer
17
+	 */
18
+	public static function new_instance($props_n_values = array())
19
+	{
20
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__);
21
+		return $has_object ? $has_object : new self($props_n_values);
22
+	}
23
+
24
+
25
+	/**
26
+	 *
27
+	 * @param array $props_n_values
28
+	 * @return EE_Answer
29
+	 */
30
+	public static function new_instance_from_db($props_n_values = array())
31
+	{
32
+		return new self($props_n_values, true);
33
+	}
34
+
35
+
36
+	/**
37
+	 *    Set Question ID
38
+	 *
39
+	 * @access        public
40
+	 * @param int $QST_ID
41
+	 */
42
+	public function set_question($QST_ID = 0)
43
+	{
44
+		$this->set('QST_ID', $QST_ID);
45
+	}
46
+
47
+
48
+	/**
49
+	 *    Set Registration ID
50
+	 *
51
+	 * @access        public
52
+	 * @param int $REG_ID
53
+	 */
54
+	public function set_registration($REG_ID = 0)
55
+	{
56
+		$this->set('REG_ID', $REG_ID);
57
+	}
58
+
59
+
60
+	/**
61
+	 *    Set Answer value
62
+	 *
63
+	 * @access        public
64
+	 * @param mixed $ANS_value
65
+	 */
66
+	public function set_value($ANS_value = '')
67
+	{
68
+		$this->set('ANS_value', $ANS_value);
69
+	}
70
+
71
+
72
+	/**
73
+	 *    get Attendee First Name
74
+	 *
75
+	 * @access        public
76
+	 * @return        int
77
+	 */
78
+	public function registration_ID()
79
+	{
80
+		return $this->get('REG_ID');
81
+	}
82
+
83
+
84
+	/**
85
+	 *    get Attendee Last Name
86
+	 *
87
+	 * @access        public
88
+	 * @return        int
89
+	 */
90
+	public function question_ID()
91
+	{
92
+		return $this->get('QST_ID');
93
+	}
94
+
95
+
96
+	/**
97
+	 *    get Attendee Address
98
+	 *
99
+	 * @access        public
100
+	 * @return        string
101
+	 */
102
+	public function value()
103
+	{
104
+		return $this->get('ANS_value');
105
+	}
106
+
107
+
108
+	/**
109
+	 * Gets a pretty form of the value (mostly applies to answers that have multiple answers)
110
+	 *
111
+	 * @param null $schema
112
+	 * @return string
113
+	 */
114
+	public function pretty_value($schema = null)
115
+	{
116
+		return $this->get_pretty('ANS_value', $schema);
117
+	}
118
+
119
+
120
+	/**
121
+	 * Echoes out a pretty value (even for multi-choice options)
122
+	 *
123
+	 * @param string $schema
124
+	 */
125
+	public function e_value($schema = null)
126
+	{
127
+		$this->e('ANS_value', $schema);
128
+	}
129
+
130
+
131
+	/**
132
+	 * Gets the related EE_Question to this EE_Answer
133
+	 *
134
+	 * @return EE_Question
135
+	 */
136
+	public function question()
137
+	{
138
+		return $this->get_first_related('Question');
139
+	}
140
+
141
+
142
+	/**
143
+	 * Gets the related EE_Registration to this EE_Answer
144
+	 *
145
+	 * @return EE_Registration
146
+	 */
147
+	public function registration()
148
+	{
149
+		return $this->get_first_related('Registration');
150
+	}
151 151
 }
152 152
 
153 153
 /* End of file EE_Answer.class.php */
Please login to merge, or discard this patch.
core/db_classes/EE_Country.class.php 1 patch
Indentation   +119 added lines, -119 removed lines patch added patch discarded remove patch
@@ -10,123 +10,123 @@
 block discarded – undo
10 10
 class EE_Country extends EE_Base_Class
11 11
 {
12 12
 
13
-    /**
14
-     * @param array $props_n_values
15
-     * @return EE_Country|mixed
16
-     */
17
-    public static function new_instance($props_n_values = array())
18
-    {
19
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__);
20
-        return $has_object ? $has_object : new self($props_n_values);
21
-    }
22
-
23
-
24
-    /**
25
-     * @param array $props_n_values
26
-     * @return EE_Country
27
-     */
28
-    public static function new_instance_from_db($props_n_values = array())
29
-    {
30
-        return new self($props_n_values, true);
31
-    }
32
-
33
-
34
-    /**
35
-     * Gets the country name
36
-     *
37
-     * @return string
38
-     */
39
-    public function name()
40
-    {
41
-        return $this->get('CNT_name');
42
-    }
43
-
44
-
45
-    /**
46
-     * gets the country's currency code
47
-     *
48
-     * @return string
49
-     */
50
-    public function currency_code()
51
-    {
52
-        return $this->get('CNT_cur_code');
53
-    }
54
-
55
-
56
-    /**
57
-     * gets the country's currency sign/symbol
58
-     *
59
-     * @return string
60
-     */
61
-    public function currency_sign()
62
-    {
63
-        $CNT_cur_sign = $this->get('CNT_cur_sign');
64
-        return $CNT_cur_sign ? $CNT_cur_sign : '';
65
-    }
66
-
67
-
68
-    /**
69
-     * Currency name singular
70
-     *
71
-     * @return string
72
-     */
73
-    public function currency_name_single()
74
-    {
75
-        return $this->get('CNT_cur_single');
76
-    }
77
-
78
-
79
-    /**
80
-     * Currency name plural
81
-     *
82
-     * @return string
83
-     */
84
-    public function currency_name_plural()
85
-    {
86
-        return $this->get('CNT_cur_plural');
87
-    }
88
-
89
-
90
-    /**
91
-     * currency_sign_before - ie: $TRUE  or  FALSE$
92
-     *
93
-     * @return boolean
94
-     */
95
-    public function currency_sign_before()
96
-    {
97
-        return $this->get('CNT_cur_sign_b4');
98
-    }
99
-
100
-
101
-    /**
102
-     * currency_decimal_places : 2 = 0.00   3 = 0.000
103
-     *
104
-     * @return integer
105
-     */
106
-    public function currency_decimal_places()
107
-    {
108
-        return $this->get('CNT_cur_dec_plc');
109
-    }
110
-
111
-
112
-    /**
113
-     * currency_decimal_mark :   (comma) ',' = 0,01   or   (decimal) '.' = 0.01
114
-     *
115
-     * @return string
116
-     */
117
-    public function currency_decimal_mark()
118
-    {
119
-        return $this->get('CNT_cur_dec_mrk');
120
-    }
121
-
122
-
123
-    /**
124
-     * currency thousands separator:   (comma) ',' = 1,000   or   (decimal) '.' = 1.000
125
-     *
126
-     * @return string
127
-     */
128
-    public function currency_thousands_separator()
129
-    {
130
-        return $this->get('CNT_cur_thsnds');
131
-    }
13
+	/**
14
+	 * @param array $props_n_values
15
+	 * @return EE_Country|mixed
16
+	 */
17
+	public static function new_instance($props_n_values = array())
18
+	{
19
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__);
20
+		return $has_object ? $has_object : new self($props_n_values);
21
+	}
22
+
23
+
24
+	/**
25
+	 * @param array $props_n_values
26
+	 * @return EE_Country
27
+	 */
28
+	public static function new_instance_from_db($props_n_values = array())
29
+	{
30
+		return new self($props_n_values, true);
31
+	}
32
+
33
+
34
+	/**
35
+	 * Gets the country name
36
+	 *
37
+	 * @return string
38
+	 */
39
+	public function name()
40
+	{
41
+		return $this->get('CNT_name');
42
+	}
43
+
44
+
45
+	/**
46
+	 * gets the country's currency code
47
+	 *
48
+	 * @return string
49
+	 */
50
+	public function currency_code()
51
+	{
52
+		return $this->get('CNT_cur_code');
53
+	}
54
+
55
+
56
+	/**
57
+	 * gets the country's currency sign/symbol
58
+	 *
59
+	 * @return string
60
+	 */
61
+	public function currency_sign()
62
+	{
63
+		$CNT_cur_sign = $this->get('CNT_cur_sign');
64
+		return $CNT_cur_sign ? $CNT_cur_sign : '';
65
+	}
66
+
67
+
68
+	/**
69
+	 * Currency name singular
70
+	 *
71
+	 * @return string
72
+	 */
73
+	public function currency_name_single()
74
+	{
75
+		return $this->get('CNT_cur_single');
76
+	}
77
+
78
+
79
+	/**
80
+	 * Currency name plural
81
+	 *
82
+	 * @return string
83
+	 */
84
+	public function currency_name_plural()
85
+	{
86
+		return $this->get('CNT_cur_plural');
87
+	}
88
+
89
+
90
+	/**
91
+	 * currency_sign_before - ie: $TRUE  or  FALSE$
92
+	 *
93
+	 * @return boolean
94
+	 */
95
+	public function currency_sign_before()
96
+	{
97
+		return $this->get('CNT_cur_sign_b4');
98
+	}
99
+
100
+
101
+	/**
102
+	 * currency_decimal_places : 2 = 0.00   3 = 0.000
103
+	 *
104
+	 * @return integer
105
+	 */
106
+	public function currency_decimal_places()
107
+	{
108
+		return $this->get('CNT_cur_dec_plc');
109
+	}
110
+
111
+
112
+	/**
113
+	 * currency_decimal_mark :   (comma) ',' = 0,01   or   (decimal) '.' = 0.01
114
+	 *
115
+	 * @return string
116
+	 */
117
+	public function currency_decimal_mark()
118
+	{
119
+		return $this->get('CNT_cur_dec_mrk');
120
+	}
121
+
122
+
123
+	/**
124
+	 * currency thousands separator:   (comma) ',' = 1,000   or   (decimal) '.' = 1.000
125
+	 *
126
+	 * @return string
127
+	 */
128
+	public function currency_thousands_separator()
129
+	{
130
+		return $this->get('CNT_cur_thsnds');
131
+	}
132 132
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Message.class.php 2 patches
Indentation   +867 added lines, -867 removed lines patch added patch discarded remove patch
@@ -10,875 +10,875 @@
 block discarded – undo
10 10
 class EE_Message extends EE_Base_Class implements EEI_Admin_Links
11 11
 {
12 12
 
13
-    /**
14
-     * @deprecated 4.9.0  Added for backward compat with add-on's
15
-     * @type null
16
-     */
17
-    public $template_pack;
18
-
19
-    /**
20
-     * @deprecated 4.9.0 Added for backward compat with add-on's
21
-     * @type null
22
-     */
23
-    public $template_variation;
24
-
25
-    /**
26
-     * @deprecated 4.9.0 Added for backward compat with add-on's
27
-     * @type string
28
-     */
29
-    public $content = '';
30
-
31
-
32
-    /**
33
-     * @type EE_messenger $_messenger
34
-     */
35
-    protected $_messenger = null;
36
-
37
-    /**
38
-     * @type EE_message_type $_message_type
39
-     */
40
-    protected $_message_type = null;
41
-
42
-
43
-    /**
44
-     * @param array  $props_n_values
45
-     * @param string $timezone
46
-     * @param array  $date_formats incoming date formats in an array.  First value is the date_format, second is time
47
-     *                             format.
48
-     * @return EE_Message
49
-     */
50
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
51
-    {
52
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__);
53
-        // if object doesn't exist, let's generate a unique token on instantiation so that its available even before saving to db.
54
-        if (! $has_object) {
55
-            EE_Registry::instance()->load_helper('URL');
56
-            $props_n_values['MSG_token'] = EEH_URL::generate_unique_token();
57
-        }
58
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
59
-    }
60
-
61
-
62
-    /**
63
-     * @param array  $props_n_values
64
-     * @param string $timezone
65
-     * @return EE_Message
66
-     */
67
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
68
-    {
69
-        return new self($props_n_values, true, $timezone);
70
-    }
71
-
72
-
73
-    /**
74
-     * Gets MSG_token
75
-     *
76
-     * @return int
77
-     */
78
-    public function MSG_token()
79
-    {
80
-        return $this->get('MSG_token');
81
-    }
82
-
83
-
84
-    /**
85
-     * Sets MSG_token
86
-     *
87
-     * @param int $MSG_token
88
-     */
89
-    public function set_MSG_token($MSG_token)
90
-    {
91
-        $this->set('MSG_token', $MSG_token);
92
-    }
93
-
94
-
95
-    /**
96
-     * Gets GRP_ID
97
-     *
98
-     * @return int
99
-     */
100
-    public function GRP_ID()
101
-    {
102
-        return $this->get('GRP_ID');
103
-    }
104
-
105
-
106
-    /**
107
-     * Sets GRP_ID
108
-     *
109
-     * @param int $GRP_ID
110
-     */
111
-    public function set_GRP_ID($GRP_ID)
112
-    {
113
-        $this->set('GRP_ID', $GRP_ID);
114
-    }
115
-
116
-
117
-    /**
118
-     * Gets TXN_ID
119
-     *
120
-     * @return int
121
-     */
122
-    public function TXN_ID()
123
-    {
124
-        return $this->get('TXN_ID');
125
-    }
126
-
127
-
128
-    /**
129
-     * Sets TXN_ID
130
-     *
131
-     * @param int $TXN_ID
132
-     */
133
-    public function set_TXN_ID($TXN_ID)
134
-    {
135
-        $this->set('TXN_ID', $TXN_ID);
136
-    }
137
-
138
-
139
-    /**
140
-     * Gets messenger
141
-     *
142
-     * @return string
143
-     */
144
-    public function messenger()
145
-    {
146
-        return $this->get('MSG_messenger');
147
-    }
148
-
149
-
150
-    /**
151
-     * Sets messenger
152
-     *
153
-     * @param string $messenger
154
-     */
155
-    public function set_messenger($messenger)
156
-    {
157
-        $this->set('MSG_messenger', $messenger);
158
-    }
159
-
160
-
161
-    /**
162
-     * Returns corresponding messenger object for the set messenger on this message
163
-     *
164
-     * @return EE_messenger | null
165
-     */
166
-    public function messenger_object()
167
-    {
168
-        return $this->_messenger;
169
-    }
170
-
171
-
172
-    /**
173
-     * Sets messenger
174
-     *
175
-     * @param EE_messenger $messenger
176
-     */
177
-    public function set_messenger_object(EE_messenger $messenger)
178
-    {
179
-        $this->_messenger = $messenger;
180
-    }
181
-
182
-
183
-    /**
184
-     * validates messenger
185
-     *
186
-     * @param bool $throw_exceptions
187
-     * @return bool
188
-     * @throws \EE_Error
189
-     */
190
-    public function valid_messenger($throw_exceptions = false)
191
-    {
192
-        if ($this->_messenger instanceof EE_messenger) {
193
-            return true;
194
-        }
195
-        if ($throw_exceptions) {
196
-            throw new EE_Error(
197
-                sprintf(
198
-                    __(
199
-                        'The "%1$s" messenger set for this message is missing or invalid. Please double-check the spelling and verify that the correct files exist.',
200
-                        'event_espresso'
201
-                    ),
202
-                    $this->messenger()
203
-                )
204
-            );
205
-        }
206
-        return false;
207
-    }
208
-
209
-
210
-    /**
211
-     * This returns the set localized label for the messenger on this message.
212
-     * Note, if unable to retrieve the EE_messenger object then will just return the messenger slug saved
213
-     * with this message.
214
-     *
215
-     * @param   bool $plural whether to return the plural label or not.
216
-     * @return string
217
-     */
218
-    public function messenger_label($plural = false)
219
-    {
220
-        $label_type = $plural ? 'plural' : 'singular';
221
-        $messenger = $this->messenger_object();
222
-        return $messenger instanceof EE_messenger ? $messenger->label[ $label_type ] : $this->messenger();
223
-    }
224
-
225
-
226
-    /**
227
-     * Gets message_type
228
-     *
229
-     * @return string
230
-     */
231
-    public function message_type()
232
-    {
233
-        return $this->get('MSG_message_type');
234
-    }
235
-
236
-
237
-    /**
238
-     * Sets message_type
239
-     *
240
-     * @param string $message_type
241
-     */
242
-    public function set_message_type($message_type)
243
-    {
244
-        $this->set('MSG_message_type', $message_type);
245
-    }
246
-
247
-
248
-    /**
249
-     * Returns the message type object for the set message type on this message
250
-     *
251
-     * @return EE_message_type | null
252
-     */
253
-    public function message_type_object()
254
-    {
255
-        return $this->_message_type;
256
-    }
257
-
258
-
259
-    /**
260
-     * Sets message_type
261
-     *
262
-     * @param EE_message_type $message_type
263
-     * @param bool            $set_priority   This indicates whether to set the priority to whatever the priority is on
264
-     *                                        the message type or not.
265
-     */
266
-    public function set_message_type_object(EE_message_type $message_type, $set_priority = false)
267
-    {
268
-        $this->_message_type = $message_type;
269
-        if ($set_priority) {
270
-            $this->set_priority($this->_message_type->get_priority());
271
-        }
272
-    }
273
-
274
-
275
-    /**
276
-     * validates message_type
277
-     *
278
-     * @param bool $throw_exceptions
279
-     * @return bool
280
-     * @throws \EE_Error
281
-     */
282
-    public function valid_message_type($throw_exceptions = false)
283
-    {
284
-        if ($this->_message_type instanceof EE_message_type) {
285
-            return true;
286
-        }
287
-        if ($throw_exceptions) {
288
-            throw new EE_Error(
289
-                sprintf(
290
-                    __(
291
-                        'The %1$s message type set for this message is missing or invalid. Please double-check the spelling and verify that the correct files exist.',
292
-                        'event_espresso'
293
-                    ),
294
-                    $this->message_type()
295
-                )
296
-            );
297
-        }
298
-        return false;
299
-    }
300
-
301
-
302
-    /**
303
-     * validates messenger and message_type (that they are valid EE_messenger and EE_message_type objects).
304
-     *
305
-     * @param bool $throw_exceptions
306
-     * @return bool
307
-     * @throws \EE_Error
308
-     */
309
-    public function is_valid($throw_exceptions = false)
310
-    {
311
-        if ($this->valid_messenger($throw_exceptions) && $this->valid_message_type($throw_exceptions)) {
312
-            return true;
313
-        }
314
-        return false;
315
-    }
316
-
317
-
318
-    /**
319
-     * This validates whether the internal messenger and message type objects are valid for sending.
320
-     * Three checks are done:
321
-     * 1. There is a valid messenger object.
322
-     * 2. There is a valid message type object.
323
-     * 3. The message type object is active for the messenger.
324
-     *
325
-     * @throws EE_Error  But only if $throw_exceptions is set to true.
326
-     * @param bool $throw_exceptions
327
-     * @return bool
328
-     */
329
-    public function is_valid_for_sending_or_generation($throw_exceptions = false)
330
-    {
331
-        $valid = false;
332
-        if ($this->is_valid($throw_exceptions)) {
333
-            /** @var EE_Message_Resource_Manager $message_resource_manager */
334
-            $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
335
-            $valid = $message_resource_manager->is_message_type_active_for_messenger(
336
-                $this->messenger(),
337
-                $this->message_type()
338
-            );
339
-            if (! $valid && $throw_exceptions) {
340
-                throw new EE_Error(
341
-                    sprintf(
342
-                        __(
343
-                            'The %1$s message type is not a valid message type for the %2$s messenger so it will not be sent.',
344
-                            'event_espresso'
345
-                        ),
346
-                        $this->message_type(),
347
-                        $this->messenger()
348
-                    )
349
-                );
350
-            }
351
-        }
352
-        return $valid;
353
-    }
354
-
355
-
356
-    /**
357
-     * This returns the set localized label for the message type on this message.
358
-     * Note, if unable to retrieve the EE_message_type object then will just return the message type slug saved
359
-     * with this message.
360
-     *
361
-     * @param   bool $plural whether to return the plural label or not.
362
-     * @return string
363
-     */
364
-    public function message_type_label($plural = false)
365
-    {
366
-        $label_type = $plural ? 'plural' : 'singular';
367
-        $message_type = $this->message_type_object();
368
-        return $message_type instanceof EE_message_type
369
-            ? $message_type->label[ $label_type ]
370
-            : str_replace(
371
-                '_',
372
-                ' ',
373
-                $this->message_type()
374
-            );
375
-    }
376
-
377
-
378
-    /**
379
-     * Gets context
380
-     *
381
-     * @return string
382
-     */
383
-    public function context()
384
-    {
385
-        return $this->get('MSG_context');
386
-    }
387
-
388
-
389
-    /**
390
-     * This returns the corresponding localized label for the given context slug, if possible from installed message
391
-     * types. Otherwise, this will just return the set context slug on this object.
392
-     *
393
-     * @return string
394
-     */
395
-    public function context_label()
396
-    {
397
-        /** @type EE_Message_Resource_Manager $message_resource_manager */
398
-        $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
399
-        $contexts = $message_resource_manager->get_all_contexts();
400
-        return isset($contexts[ $this->context() ]) ? $contexts[ $this->context() ] : $this->context();
401
-    }
402
-
403
-
404
-    /**
405
-     * Sets context
406
-     *
407
-     * @param string $context
408
-     */
409
-    public function set_context($context)
410
-    {
411
-        $this->set('MSG_context', $context);
412
-    }
413
-
414
-
415
-    /**
416
-     * Gets recipient_ID
417
-     *
418
-     * @return int
419
-     */
420
-    public function recipient_ID()
421
-    {
422
-        return $this->get('MSG_recipient_ID');
423
-    }
424
-
425
-
426
-    /**
427
-     * Sets recipient_ID
428
-     *
429
-     * @param string $recipient_ID
430
-     */
431
-    public function set_recipient_ID($recipient_ID)
432
-    {
433
-        $this->set('MSG_recipient_ID', $recipient_ID);
434
-    }
435
-
436
-
437
-    /**
438
-     * Gets recipient_type
439
-     *
440
-     * @return string
441
-     */
442
-    public function recipient_type()
443
-    {
444
-        return $this->get('MSG_recipient_type');
445
-    }
446
-
447
-
448
-    /**
449
-     * Return the related object matching the recipient type and ID.
450
-     *
451
-     * @return EE_Base_Class | null
452
-     */
453
-    public function recipient_object()
454
-    {
455
-        if (! $this->recipient_type() || ! $this->recipient_ID()) {
456
-            return null;
457
-        }
458
-
459
-        return $this->get_first_related($this->recipient_type());
460
-    }
461
-
462
-
463
-    /**
464
-     * Sets recipient_type
465
-     *
466
-     * @param string $recipient_type
467
-     */
468
-    public function set_recipient_type($recipient_type)
469
-    {
470
-        $this->set('MSG_recipient_type', $recipient_type);
471
-    }
472
-
473
-
474
-    /**
475
-     * Gets content
476
-     *
477
-     * @return string
478
-     */
479
-    public function content()
480
-    {
481
-        return $this->get('MSG_content');
482
-    }
483
-
484
-
485
-    /**
486
-     * Sets content
487
-     *
488
-     * @param string $content
489
-     */
490
-    public function set_content($content)
491
-    {
492
-        $this->set('MSG_content', $content);
493
-    }
494
-
495
-
496
-    /**
497
-     * Gets subject
498
-     *
499
-     * @return string
500
-     */
501
-    public function subject()
502
-    {
503
-        return $this->get('MSG_subject');
504
-    }
505
-
506
-
507
-    /**
508
-     * Sets subject
509
-     *
510
-     * @param string $subject
511
-     */
512
-    public function set_subject($subject)
513
-    {
514
-        $this->set('MSG_subject', $subject);
515
-    }
516
-
517
-
518
-    /**
519
-     * Gets to
520
-     *
521
-     * @return string
522
-     */
523
-    public function to()
524
-    {
525
-        $to = $this->get('MSG_to');
526
-        return empty($to) ? __('No recipient', 'event_espresso') : $to;
527
-    }
528
-
529
-
530
-    /**
531
-     * Sets to
532
-     *
533
-     * @param string $to
534
-     */
535
-    public function set_to($to)
536
-    {
537
-        $this->set('MSG_to', $to);
538
-    }
539
-
540
-
541
-    /**
542
-     * Gets from
543
-     *
544
-     * @return string
545
-     */
546
-    public function from()
547
-    {
548
-        return $this->get('MSG_from');
549
-    }
550
-
551
-
552
-    /**
553
-     * Sets from
554
-     *
555
-     * @param string $from
556
-     */
557
-    public function set_from($from)
558
-    {
559
-        $this->set('MSG_from', $from);
560
-    }
561
-
562
-
563
-    /**
564
-     * Gets priority
565
-     *
566
-     * @return int
567
-     */
568
-    public function priority()
569
-    {
570
-        return $this->get('MSG_priority');
571
-    }
572
-
573
-
574
-    /**
575
-     * Sets priority
576
-     * Note.  Send Now Messengers always override any priority that may be set on a Message.  So
577
-     * this method calls the send_now method to verify that.
578
-     *
579
-     * @param int $priority
580
-     */
581
-    public function set_priority($priority)
582
-    {
583
-        $priority = $this->send_now() ? EEM_Message::priority_high : $priority;
584
-        parent::set('MSG_priority', $priority);
585
-    }
586
-
587
-
588
-    /**
589
-     * Overrides parent::set method so we can capture any sets for priority.
590
-     *
591
-     * @see parent::set() for phpdocs
592
-     * @param string $field_name
593
-     * @param mixed  $field_value
594
-     * @param bool   $use_default
595
-     * @throws EE_Error
596
-     */
597
-    public function set($field_name, $field_value, $use_default = false)
598
-    {
599
-        if ($field_name === 'MSG_priority') {
600
-            $this->set_priority($field_value);
601
-        }
602
-        parent::set($field_name, $field_value, $use_default);
603
-    }
604
-
605
-
606
-    /**
607
-     * @return bool
608
-     * @throws \EE_Error
609
-     */
610
-    public function send_now()
611
-    {
612
-        $send_now = $this->valid_messenger() && $this->messenger_object()->send_now() ? EEM_Message::priority_high
613
-            : $this->priority();
614
-        return $send_now === EEM_Message::priority_high ? true : false;
615
-    }
616
-
617
-
618
-    /**
619
-     * Gets STS_ID
620
-     *
621
-     * @return string
622
-     */
623
-    public function STS_ID()
624
-    {
625
-        return $this->get('STS_ID');
626
-    }
627
-
628
-
629
-    /**
630
-     * Sets STS_ID
631
-     *
632
-     * @param string $STS_ID
633
-     */
634
-    public function set_STS_ID($STS_ID)
635
-    {
636
-        $this->set('STS_ID', $STS_ID);
637
-    }
638
-
639
-
640
-    /**
641
-     * Gets created
642
-     *
643
-     * @return string
644
-     */
645
-    public function created()
646
-    {
647
-        return $this->get('MSG_created');
648
-    }
649
-
650
-
651
-    /**
652
-     * Sets created
653
-     *
654
-     * @param string $created
655
-     */
656
-    public function set_created($created)
657
-    {
658
-        $this->set('MSG_created', $created);
659
-    }
660
-
661
-
662
-    /**
663
-     * Gets modified
664
-     *
665
-     * @return string
666
-     */
667
-    public function modified()
668
-    {
669
-        return $this->get('MSG_modified');
670
-    }
671
-
672
-
673
-    /**
674
-     * Sets modified
675
-     *
676
-     * @param string $modified
677
-     */
678
-    public function set_modified($modified)
679
-    {
680
-        $this->set('MSG_modified', $modified);
681
-    }
682
-
683
-
684
-    /**
685
-     * Sets generation data for this message.
686
-     *
687
-     * @param mixed $data
688
-     */
689
-    public function set_generation_data($data)
690
-    {
691
-        $this->set_field_or_extra_meta('MSG_generation_data', $data);
692
-    }
693
-
694
-
695
-    /**
696
-     * Returns any set generation data for this message.
697
-     *
698
-     * @return mixed|null
699
-     */
700
-    public function get_generation_data()
701
-    {
702
-        return $this->get_field_or_extra_meta('MSG_generation_data');
703
-    }
704
-
705
-
706
-    /**
707
-     * Gets any error message.
708
-     *
709
-     * @return mixed|null
710
-     */
711
-    public function error_message()
712
-    {
713
-        return $this->get_field_or_extra_meta('MSG_error');
714
-    }
715
-
716
-
717
-    /**
718
-     * Sets an error message.
719
-     *
720
-     * @param $message
721
-     * @return bool|int
722
-     */
723
-    public function set_error_message($message)
724
-    {
725
-        return $this->set_field_or_extra_meta('MSG_error', $message);
726
-    }
727
-
728
-
729
-    /**
730
-     * This retrieves the associated template pack with this message.
731
-     *
732
-     * @return EE_Messages_Template_Pack | null
733
-     */
734
-    public function get_template_pack()
735
-    {
736
-        /**
737
-         * This is deprecated functionality that will be removed eventually but included here now for backward compat.
738
-         */
739
-        if (! empty($this->template_pack)) {
740
-            return $this->template_pack;
741
-        }
742
-        /** @type EE_Message_Template_Group $grp */
743
-        $grp = $this->get_first_related('Message_Template_Group');
744
-        // if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
745
-        if (! $grp instanceof EE_Message_Template_Group) {
746
-            $grp = EEM_Message_Template_Group::instance()->get_one(
747
-                array(
748
-                    array(
749
-                        'MTP_messenger'    => $this->messenger(),
750
-                        'MTP_message_type' => $this->message_type(),
751
-                        'MTP_is_global'    => true,
752
-                    ),
753
-                )
754
-            );
755
-        }
756
-
757
-        return $grp instanceof EE_Message_Template_Group ? $grp->get_template_pack() : null;
758
-    }
759
-
760
-
761
-    /**
762
-     * Retrieves the variation used for generating this message.
763
-     *
764
-     * @return string
765
-     */
766
-    public function get_template_pack_variation()
767
-    {
768
-        /**
769
-         * This is deprecated functionality that will be removed eventually but included here now for backward compat.
770
-         */
771
-        if (! empty($this->template_variation)) {
772
-            return $this->template_variation;
773
-        }
774
-
775
-        /** @type EE_Message_Template_Group $grp */
776
-        $grp = $this->get_first_related('Message_Template_Group');
777
-
778
-        // if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
779
-        if (! $grp instanceof EE_Message_Template_Group) {
780
-            $grp = EEM_Message_Template_Group::instance()->get_one(
781
-                array(
782
-                    array(
783
-                        'MTP_messenger'    => $this->messenger(),
784
-                        'MTP_message_type' => $this->message_type(),
785
-                        'MTP_is_global'    => true,
786
-                    ),
787
-                )
788
-            );
789
-        }
790
-
791
-        return $grp instanceof EE_Message_Template_Group ? $grp->get_template_pack_variation() : '';
792
-    }
793
-
794
-    /**
795
-     * Return the link to the admin details for the object.
796
-     *
797
-     * @return string
798
-     */
799
-    public function get_admin_details_link()
800
-    {
801
-        EE_Registry::instance()->load_helper('URL');
802
-        EE_Registry::instance()->load_helper('MSG_Template');
803
-        switch ($this->STS_ID()) {
804
-            case EEM_Message::status_failed:
805
-            case EEM_Message::status_debug_only:
806
-                return EEH_MSG_Template::generate_error_display_trigger($this);
807
-                break;
808
-
809
-            case EEM_Message::status_sent:
810
-                return EEH_MSG_Template::generate_browser_trigger($this);
811
-                break;
812
-
813
-            default:
814
-                return '';
815
-        }
816
-    }
817
-
818
-    /**
819
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
820
-     *
821
-     * @return string
822
-     */
823
-    public function get_admin_edit_link()
824
-    {
825
-        return $this->get_admin_details_link();
826
-    }
827
-
828
-    /**
829
-     * Returns the link to a settings page for the object.
830
-     *
831
-     * @return string
832
-     */
833
-    public function get_admin_settings_link()
834
-    {
835
-        EE_Registry::instance()->load_helper('URL');
836
-        return EEH_URL::add_query_args_and_nonce(
837
-            array(
838
-                'page'   => 'espresso_messages',
839
-                'action' => 'settings',
840
-            ),
841
-            admin_url('admin.php')
842
-        );
843
-    }
844
-
845
-    /**
846
-     * Returns the link to the "overview" for the object (typically the "list table" view).
847
-     *
848
-     * @return string
849
-     */
850
-    public function get_admin_overview_link()
851
-    {
852
-        EE_Registry::instance()->load_helper('URL');
853
-        return EEH_URL::add_query_args_and_nonce(
854
-            array(
855
-                'page'   => 'espresso_messages',
856
-                'action' => 'default',
857
-            ),
858
-            admin_url('admin.php')
859
-        );
860
-    }
861
-
862
-
863
-    /**
864
-     * This sets the EEM_Message::status_messenger_executing class on the message and the appropriate error message for
865
-     * it.
866
-     * Note this also SAVES the current message object to the db because it adds an error message to accompany the
867
-     * status.
868
-     *
869
-     */
870
-    public function set_messenger_is_executing()
871
-    {
872
-        $this->set_STS_ID(EEM_Message::status_messenger_executing);
873
-        $this->set_error_message(
874
-            esc_html__(
875
-                'A message with this status indicates that there was a problem that occurred while the message was being
13
+	/**
14
+	 * @deprecated 4.9.0  Added for backward compat with add-on's
15
+	 * @type null
16
+	 */
17
+	public $template_pack;
18
+
19
+	/**
20
+	 * @deprecated 4.9.0 Added for backward compat with add-on's
21
+	 * @type null
22
+	 */
23
+	public $template_variation;
24
+
25
+	/**
26
+	 * @deprecated 4.9.0 Added for backward compat with add-on's
27
+	 * @type string
28
+	 */
29
+	public $content = '';
30
+
31
+
32
+	/**
33
+	 * @type EE_messenger $_messenger
34
+	 */
35
+	protected $_messenger = null;
36
+
37
+	/**
38
+	 * @type EE_message_type $_message_type
39
+	 */
40
+	protected $_message_type = null;
41
+
42
+
43
+	/**
44
+	 * @param array  $props_n_values
45
+	 * @param string $timezone
46
+	 * @param array  $date_formats incoming date formats in an array.  First value is the date_format, second is time
47
+	 *                             format.
48
+	 * @return EE_Message
49
+	 */
50
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
51
+	{
52
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__);
53
+		// if object doesn't exist, let's generate a unique token on instantiation so that its available even before saving to db.
54
+		if (! $has_object) {
55
+			EE_Registry::instance()->load_helper('URL');
56
+			$props_n_values['MSG_token'] = EEH_URL::generate_unique_token();
57
+		}
58
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
59
+	}
60
+
61
+
62
+	/**
63
+	 * @param array  $props_n_values
64
+	 * @param string $timezone
65
+	 * @return EE_Message
66
+	 */
67
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
68
+	{
69
+		return new self($props_n_values, true, $timezone);
70
+	}
71
+
72
+
73
+	/**
74
+	 * Gets MSG_token
75
+	 *
76
+	 * @return int
77
+	 */
78
+	public function MSG_token()
79
+	{
80
+		return $this->get('MSG_token');
81
+	}
82
+
83
+
84
+	/**
85
+	 * Sets MSG_token
86
+	 *
87
+	 * @param int $MSG_token
88
+	 */
89
+	public function set_MSG_token($MSG_token)
90
+	{
91
+		$this->set('MSG_token', $MSG_token);
92
+	}
93
+
94
+
95
+	/**
96
+	 * Gets GRP_ID
97
+	 *
98
+	 * @return int
99
+	 */
100
+	public function GRP_ID()
101
+	{
102
+		return $this->get('GRP_ID');
103
+	}
104
+
105
+
106
+	/**
107
+	 * Sets GRP_ID
108
+	 *
109
+	 * @param int $GRP_ID
110
+	 */
111
+	public function set_GRP_ID($GRP_ID)
112
+	{
113
+		$this->set('GRP_ID', $GRP_ID);
114
+	}
115
+
116
+
117
+	/**
118
+	 * Gets TXN_ID
119
+	 *
120
+	 * @return int
121
+	 */
122
+	public function TXN_ID()
123
+	{
124
+		return $this->get('TXN_ID');
125
+	}
126
+
127
+
128
+	/**
129
+	 * Sets TXN_ID
130
+	 *
131
+	 * @param int $TXN_ID
132
+	 */
133
+	public function set_TXN_ID($TXN_ID)
134
+	{
135
+		$this->set('TXN_ID', $TXN_ID);
136
+	}
137
+
138
+
139
+	/**
140
+	 * Gets messenger
141
+	 *
142
+	 * @return string
143
+	 */
144
+	public function messenger()
145
+	{
146
+		return $this->get('MSG_messenger');
147
+	}
148
+
149
+
150
+	/**
151
+	 * Sets messenger
152
+	 *
153
+	 * @param string $messenger
154
+	 */
155
+	public function set_messenger($messenger)
156
+	{
157
+		$this->set('MSG_messenger', $messenger);
158
+	}
159
+
160
+
161
+	/**
162
+	 * Returns corresponding messenger object for the set messenger on this message
163
+	 *
164
+	 * @return EE_messenger | null
165
+	 */
166
+	public function messenger_object()
167
+	{
168
+		return $this->_messenger;
169
+	}
170
+
171
+
172
+	/**
173
+	 * Sets messenger
174
+	 *
175
+	 * @param EE_messenger $messenger
176
+	 */
177
+	public function set_messenger_object(EE_messenger $messenger)
178
+	{
179
+		$this->_messenger = $messenger;
180
+	}
181
+
182
+
183
+	/**
184
+	 * validates messenger
185
+	 *
186
+	 * @param bool $throw_exceptions
187
+	 * @return bool
188
+	 * @throws \EE_Error
189
+	 */
190
+	public function valid_messenger($throw_exceptions = false)
191
+	{
192
+		if ($this->_messenger instanceof EE_messenger) {
193
+			return true;
194
+		}
195
+		if ($throw_exceptions) {
196
+			throw new EE_Error(
197
+				sprintf(
198
+					__(
199
+						'The "%1$s" messenger set for this message is missing or invalid. Please double-check the spelling and verify that the correct files exist.',
200
+						'event_espresso'
201
+					),
202
+					$this->messenger()
203
+				)
204
+			);
205
+		}
206
+		return false;
207
+	}
208
+
209
+
210
+	/**
211
+	 * This returns the set localized label for the messenger on this message.
212
+	 * Note, if unable to retrieve the EE_messenger object then will just return the messenger slug saved
213
+	 * with this message.
214
+	 *
215
+	 * @param   bool $plural whether to return the plural label or not.
216
+	 * @return string
217
+	 */
218
+	public function messenger_label($plural = false)
219
+	{
220
+		$label_type = $plural ? 'plural' : 'singular';
221
+		$messenger = $this->messenger_object();
222
+		return $messenger instanceof EE_messenger ? $messenger->label[ $label_type ] : $this->messenger();
223
+	}
224
+
225
+
226
+	/**
227
+	 * Gets message_type
228
+	 *
229
+	 * @return string
230
+	 */
231
+	public function message_type()
232
+	{
233
+		return $this->get('MSG_message_type');
234
+	}
235
+
236
+
237
+	/**
238
+	 * Sets message_type
239
+	 *
240
+	 * @param string $message_type
241
+	 */
242
+	public function set_message_type($message_type)
243
+	{
244
+		$this->set('MSG_message_type', $message_type);
245
+	}
246
+
247
+
248
+	/**
249
+	 * Returns the message type object for the set message type on this message
250
+	 *
251
+	 * @return EE_message_type | null
252
+	 */
253
+	public function message_type_object()
254
+	{
255
+		return $this->_message_type;
256
+	}
257
+
258
+
259
+	/**
260
+	 * Sets message_type
261
+	 *
262
+	 * @param EE_message_type $message_type
263
+	 * @param bool            $set_priority   This indicates whether to set the priority to whatever the priority is on
264
+	 *                                        the message type or not.
265
+	 */
266
+	public function set_message_type_object(EE_message_type $message_type, $set_priority = false)
267
+	{
268
+		$this->_message_type = $message_type;
269
+		if ($set_priority) {
270
+			$this->set_priority($this->_message_type->get_priority());
271
+		}
272
+	}
273
+
274
+
275
+	/**
276
+	 * validates message_type
277
+	 *
278
+	 * @param bool $throw_exceptions
279
+	 * @return bool
280
+	 * @throws \EE_Error
281
+	 */
282
+	public function valid_message_type($throw_exceptions = false)
283
+	{
284
+		if ($this->_message_type instanceof EE_message_type) {
285
+			return true;
286
+		}
287
+		if ($throw_exceptions) {
288
+			throw new EE_Error(
289
+				sprintf(
290
+					__(
291
+						'The %1$s message type set for this message is missing or invalid. Please double-check the spelling and verify that the correct files exist.',
292
+						'event_espresso'
293
+					),
294
+					$this->message_type()
295
+				)
296
+			);
297
+		}
298
+		return false;
299
+	}
300
+
301
+
302
+	/**
303
+	 * validates messenger and message_type (that they are valid EE_messenger and EE_message_type objects).
304
+	 *
305
+	 * @param bool $throw_exceptions
306
+	 * @return bool
307
+	 * @throws \EE_Error
308
+	 */
309
+	public function is_valid($throw_exceptions = false)
310
+	{
311
+		if ($this->valid_messenger($throw_exceptions) && $this->valid_message_type($throw_exceptions)) {
312
+			return true;
313
+		}
314
+		return false;
315
+	}
316
+
317
+
318
+	/**
319
+	 * This validates whether the internal messenger and message type objects are valid for sending.
320
+	 * Three checks are done:
321
+	 * 1. There is a valid messenger object.
322
+	 * 2. There is a valid message type object.
323
+	 * 3. The message type object is active for the messenger.
324
+	 *
325
+	 * @throws EE_Error  But only if $throw_exceptions is set to true.
326
+	 * @param bool $throw_exceptions
327
+	 * @return bool
328
+	 */
329
+	public function is_valid_for_sending_or_generation($throw_exceptions = false)
330
+	{
331
+		$valid = false;
332
+		if ($this->is_valid($throw_exceptions)) {
333
+			/** @var EE_Message_Resource_Manager $message_resource_manager */
334
+			$message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
335
+			$valid = $message_resource_manager->is_message_type_active_for_messenger(
336
+				$this->messenger(),
337
+				$this->message_type()
338
+			);
339
+			if (! $valid && $throw_exceptions) {
340
+				throw new EE_Error(
341
+					sprintf(
342
+						__(
343
+							'The %1$s message type is not a valid message type for the %2$s messenger so it will not be sent.',
344
+							'event_espresso'
345
+						),
346
+						$this->message_type(),
347
+						$this->messenger()
348
+					)
349
+				);
350
+			}
351
+		}
352
+		return $valid;
353
+	}
354
+
355
+
356
+	/**
357
+	 * This returns the set localized label for the message type on this message.
358
+	 * Note, if unable to retrieve the EE_message_type object then will just return the message type slug saved
359
+	 * with this message.
360
+	 *
361
+	 * @param   bool $plural whether to return the plural label or not.
362
+	 * @return string
363
+	 */
364
+	public function message_type_label($plural = false)
365
+	{
366
+		$label_type = $plural ? 'plural' : 'singular';
367
+		$message_type = $this->message_type_object();
368
+		return $message_type instanceof EE_message_type
369
+			? $message_type->label[ $label_type ]
370
+			: str_replace(
371
+				'_',
372
+				' ',
373
+				$this->message_type()
374
+			);
375
+	}
376
+
377
+
378
+	/**
379
+	 * Gets context
380
+	 *
381
+	 * @return string
382
+	 */
383
+	public function context()
384
+	{
385
+		return $this->get('MSG_context');
386
+	}
387
+
388
+
389
+	/**
390
+	 * This returns the corresponding localized label for the given context slug, if possible from installed message
391
+	 * types. Otherwise, this will just return the set context slug on this object.
392
+	 *
393
+	 * @return string
394
+	 */
395
+	public function context_label()
396
+	{
397
+		/** @type EE_Message_Resource_Manager $message_resource_manager */
398
+		$message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
399
+		$contexts = $message_resource_manager->get_all_contexts();
400
+		return isset($contexts[ $this->context() ]) ? $contexts[ $this->context() ] : $this->context();
401
+	}
402
+
403
+
404
+	/**
405
+	 * Sets context
406
+	 *
407
+	 * @param string $context
408
+	 */
409
+	public function set_context($context)
410
+	{
411
+		$this->set('MSG_context', $context);
412
+	}
413
+
414
+
415
+	/**
416
+	 * Gets recipient_ID
417
+	 *
418
+	 * @return int
419
+	 */
420
+	public function recipient_ID()
421
+	{
422
+		return $this->get('MSG_recipient_ID');
423
+	}
424
+
425
+
426
+	/**
427
+	 * Sets recipient_ID
428
+	 *
429
+	 * @param string $recipient_ID
430
+	 */
431
+	public function set_recipient_ID($recipient_ID)
432
+	{
433
+		$this->set('MSG_recipient_ID', $recipient_ID);
434
+	}
435
+
436
+
437
+	/**
438
+	 * Gets recipient_type
439
+	 *
440
+	 * @return string
441
+	 */
442
+	public function recipient_type()
443
+	{
444
+		return $this->get('MSG_recipient_type');
445
+	}
446
+
447
+
448
+	/**
449
+	 * Return the related object matching the recipient type and ID.
450
+	 *
451
+	 * @return EE_Base_Class | null
452
+	 */
453
+	public function recipient_object()
454
+	{
455
+		if (! $this->recipient_type() || ! $this->recipient_ID()) {
456
+			return null;
457
+		}
458
+
459
+		return $this->get_first_related($this->recipient_type());
460
+	}
461
+
462
+
463
+	/**
464
+	 * Sets recipient_type
465
+	 *
466
+	 * @param string $recipient_type
467
+	 */
468
+	public function set_recipient_type($recipient_type)
469
+	{
470
+		$this->set('MSG_recipient_type', $recipient_type);
471
+	}
472
+
473
+
474
+	/**
475
+	 * Gets content
476
+	 *
477
+	 * @return string
478
+	 */
479
+	public function content()
480
+	{
481
+		return $this->get('MSG_content');
482
+	}
483
+
484
+
485
+	/**
486
+	 * Sets content
487
+	 *
488
+	 * @param string $content
489
+	 */
490
+	public function set_content($content)
491
+	{
492
+		$this->set('MSG_content', $content);
493
+	}
494
+
495
+
496
+	/**
497
+	 * Gets subject
498
+	 *
499
+	 * @return string
500
+	 */
501
+	public function subject()
502
+	{
503
+		return $this->get('MSG_subject');
504
+	}
505
+
506
+
507
+	/**
508
+	 * Sets subject
509
+	 *
510
+	 * @param string $subject
511
+	 */
512
+	public function set_subject($subject)
513
+	{
514
+		$this->set('MSG_subject', $subject);
515
+	}
516
+
517
+
518
+	/**
519
+	 * Gets to
520
+	 *
521
+	 * @return string
522
+	 */
523
+	public function to()
524
+	{
525
+		$to = $this->get('MSG_to');
526
+		return empty($to) ? __('No recipient', 'event_espresso') : $to;
527
+	}
528
+
529
+
530
+	/**
531
+	 * Sets to
532
+	 *
533
+	 * @param string $to
534
+	 */
535
+	public function set_to($to)
536
+	{
537
+		$this->set('MSG_to', $to);
538
+	}
539
+
540
+
541
+	/**
542
+	 * Gets from
543
+	 *
544
+	 * @return string
545
+	 */
546
+	public function from()
547
+	{
548
+		return $this->get('MSG_from');
549
+	}
550
+
551
+
552
+	/**
553
+	 * Sets from
554
+	 *
555
+	 * @param string $from
556
+	 */
557
+	public function set_from($from)
558
+	{
559
+		$this->set('MSG_from', $from);
560
+	}
561
+
562
+
563
+	/**
564
+	 * Gets priority
565
+	 *
566
+	 * @return int
567
+	 */
568
+	public function priority()
569
+	{
570
+		return $this->get('MSG_priority');
571
+	}
572
+
573
+
574
+	/**
575
+	 * Sets priority
576
+	 * Note.  Send Now Messengers always override any priority that may be set on a Message.  So
577
+	 * this method calls the send_now method to verify that.
578
+	 *
579
+	 * @param int $priority
580
+	 */
581
+	public function set_priority($priority)
582
+	{
583
+		$priority = $this->send_now() ? EEM_Message::priority_high : $priority;
584
+		parent::set('MSG_priority', $priority);
585
+	}
586
+
587
+
588
+	/**
589
+	 * Overrides parent::set method so we can capture any sets for priority.
590
+	 *
591
+	 * @see parent::set() for phpdocs
592
+	 * @param string $field_name
593
+	 * @param mixed  $field_value
594
+	 * @param bool   $use_default
595
+	 * @throws EE_Error
596
+	 */
597
+	public function set($field_name, $field_value, $use_default = false)
598
+	{
599
+		if ($field_name === 'MSG_priority') {
600
+			$this->set_priority($field_value);
601
+		}
602
+		parent::set($field_name, $field_value, $use_default);
603
+	}
604
+
605
+
606
+	/**
607
+	 * @return bool
608
+	 * @throws \EE_Error
609
+	 */
610
+	public function send_now()
611
+	{
612
+		$send_now = $this->valid_messenger() && $this->messenger_object()->send_now() ? EEM_Message::priority_high
613
+			: $this->priority();
614
+		return $send_now === EEM_Message::priority_high ? true : false;
615
+	}
616
+
617
+
618
+	/**
619
+	 * Gets STS_ID
620
+	 *
621
+	 * @return string
622
+	 */
623
+	public function STS_ID()
624
+	{
625
+		return $this->get('STS_ID');
626
+	}
627
+
628
+
629
+	/**
630
+	 * Sets STS_ID
631
+	 *
632
+	 * @param string $STS_ID
633
+	 */
634
+	public function set_STS_ID($STS_ID)
635
+	{
636
+		$this->set('STS_ID', $STS_ID);
637
+	}
638
+
639
+
640
+	/**
641
+	 * Gets created
642
+	 *
643
+	 * @return string
644
+	 */
645
+	public function created()
646
+	{
647
+		return $this->get('MSG_created');
648
+	}
649
+
650
+
651
+	/**
652
+	 * Sets created
653
+	 *
654
+	 * @param string $created
655
+	 */
656
+	public function set_created($created)
657
+	{
658
+		$this->set('MSG_created', $created);
659
+	}
660
+
661
+
662
+	/**
663
+	 * Gets modified
664
+	 *
665
+	 * @return string
666
+	 */
667
+	public function modified()
668
+	{
669
+		return $this->get('MSG_modified');
670
+	}
671
+
672
+
673
+	/**
674
+	 * Sets modified
675
+	 *
676
+	 * @param string $modified
677
+	 */
678
+	public function set_modified($modified)
679
+	{
680
+		$this->set('MSG_modified', $modified);
681
+	}
682
+
683
+
684
+	/**
685
+	 * Sets generation data for this message.
686
+	 *
687
+	 * @param mixed $data
688
+	 */
689
+	public function set_generation_data($data)
690
+	{
691
+		$this->set_field_or_extra_meta('MSG_generation_data', $data);
692
+	}
693
+
694
+
695
+	/**
696
+	 * Returns any set generation data for this message.
697
+	 *
698
+	 * @return mixed|null
699
+	 */
700
+	public function get_generation_data()
701
+	{
702
+		return $this->get_field_or_extra_meta('MSG_generation_data');
703
+	}
704
+
705
+
706
+	/**
707
+	 * Gets any error message.
708
+	 *
709
+	 * @return mixed|null
710
+	 */
711
+	public function error_message()
712
+	{
713
+		return $this->get_field_or_extra_meta('MSG_error');
714
+	}
715
+
716
+
717
+	/**
718
+	 * Sets an error message.
719
+	 *
720
+	 * @param $message
721
+	 * @return bool|int
722
+	 */
723
+	public function set_error_message($message)
724
+	{
725
+		return $this->set_field_or_extra_meta('MSG_error', $message);
726
+	}
727
+
728
+
729
+	/**
730
+	 * This retrieves the associated template pack with this message.
731
+	 *
732
+	 * @return EE_Messages_Template_Pack | null
733
+	 */
734
+	public function get_template_pack()
735
+	{
736
+		/**
737
+		 * This is deprecated functionality that will be removed eventually but included here now for backward compat.
738
+		 */
739
+		if (! empty($this->template_pack)) {
740
+			return $this->template_pack;
741
+		}
742
+		/** @type EE_Message_Template_Group $grp */
743
+		$grp = $this->get_first_related('Message_Template_Group');
744
+		// if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
745
+		if (! $grp instanceof EE_Message_Template_Group) {
746
+			$grp = EEM_Message_Template_Group::instance()->get_one(
747
+				array(
748
+					array(
749
+						'MTP_messenger'    => $this->messenger(),
750
+						'MTP_message_type' => $this->message_type(),
751
+						'MTP_is_global'    => true,
752
+					),
753
+				)
754
+			);
755
+		}
756
+
757
+		return $grp instanceof EE_Message_Template_Group ? $grp->get_template_pack() : null;
758
+	}
759
+
760
+
761
+	/**
762
+	 * Retrieves the variation used for generating this message.
763
+	 *
764
+	 * @return string
765
+	 */
766
+	public function get_template_pack_variation()
767
+	{
768
+		/**
769
+		 * This is deprecated functionality that will be removed eventually but included here now for backward compat.
770
+		 */
771
+		if (! empty($this->template_variation)) {
772
+			return $this->template_variation;
773
+		}
774
+
775
+		/** @type EE_Message_Template_Group $grp */
776
+		$grp = $this->get_first_related('Message_Template_Group');
777
+
778
+		// if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
779
+		if (! $grp instanceof EE_Message_Template_Group) {
780
+			$grp = EEM_Message_Template_Group::instance()->get_one(
781
+				array(
782
+					array(
783
+						'MTP_messenger'    => $this->messenger(),
784
+						'MTP_message_type' => $this->message_type(),
785
+						'MTP_is_global'    => true,
786
+					),
787
+				)
788
+			);
789
+		}
790
+
791
+		return $grp instanceof EE_Message_Template_Group ? $grp->get_template_pack_variation() : '';
792
+	}
793
+
794
+	/**
795
+	 * Return the link to the admin details for the object.
796
+	 *
797
+	 * @return string
798
+	 */
799
+	public function get_admin_details_link()
800
+	{
801
+		EE_Registry::instance()->load_helper('URL');
802
+		EE_Registry::instance()->load_helper('MSG_Template');
803
+		switch ($this->STS_ID()) {
804
+			case EEM_Message::status_failed:
805
+			case EEM_Message::status_debug_only:
806
+				return EEH_MSG_Template::generate_error_display_trigger($this);
807
+				break;
808
+
809
+			case EEM_Message::status_sent:
810
+				return EEH_MSG_Template::generate_browser_trigger($this);
811
+				break;
812
+
813
+			default:
814
+				return '';
815
+		}
816
+	}
817
+
818
+	/**
819
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
820
+	 *
821
+	 * @return string
822
+	 */
823
+	public function get_admin_edit_link()
824
+	{
825
+		return $this->get_admin_details_link();
826
+	}
827
+
828
+	/**
829
+	 * Returns the link to a settings page for the object.
830
+	 *
831
+	 * @return string
832
+	 */
833
+	public function get_admin_settings_link()
834
+	{
835
+		EE_Registry::instance()->load_helper('URL');
836
+		return EEH_URL::add_query_args_and_nonce(
837
+			array(
838
+				'page'   => 'espresso_messages',
839
+				'action' => 'settings',
840
+			),
841
+			admin_url('admin.php')
842
+		);
843
+	}
844
+
845
+	/**
846
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
847
+	 *
848
+	 * @return string
849
+	 */
850
+	public function get_admin_overview_link()
851
+	{
852
+		EE_Registry::instance()->load_helper('URL');
853
+		return EEH_URL::add_query_args_and_nonce(
854
+			array(
855
+				'page'   => 'espresso_messages',
856
+				'action' => 'default',
857
+			),
858
+			admin_url('admin.php')
859
+		);
860
+	}
861
+
862
+
863
+	/**
864
+	 * This sets the EEM_Message::status_messenger_executing class on the message and the appropriate error message for
865
+	 * it.
866
+	 * Note this also SAVES the current message object to the db because it adds an error message to accompany the
867
+	 * status.
868
+	 *
869
+	 */
870
+	public function set_messenger_is_executing()
871
+	{
872
+		$this->set_STS_ID(EEM_Message::status_messenger_executing);
873
+		$this->set_error_message(
874
+			esc_html__(
875
+				'A message with this status indicates that there was a problem that occurred while the message was being
876 876
                 processed by the messenger.  It is still possible that the message was sent successfully, but at some
877 877
                 point during the processing there was a failure.  This usually is indicative of a timeout issue with PHP 
878 878
                 or memory limits being reached.  If you see this repeatedly you may want to consider upgrading the memory 
879 879
                 available to PHP on your server.',
880
-                'event_espresso'
881
-            )
882
-        );
883
-    }
880
+				'event_espresso'
881
+			)
882
+		);
883
+	}
884 884
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -51,7 +51,7 @@  discard block
 block discarded – undo
51 51
     {
52 52
         $has_object = parent::_check_for_object($props_n_values, __CLASS__);
53 53
         // if object doesn't exist, let's generate a unique token on instantiation so that its available even before saving to db.
54
-        if (! $has_object) {
54
+        if ( ! $has_object) {
55 55
             EE_Registry::instance()->load_helper('URL');
56 56
             $props_n_values['MSG_token'] = EEH_URL::generate_unique_token();
57 57
         }
@@ -219,7 +219,7 @@  discard block
 block discarded – undo
219 219
     {
220 220
         $label_type = $plural ? 'plural' : 'singular';
221 221
         $messenger = $this->messenger_object();
222
-        return $messenger instanceof EE_messenger ? $messenger->label[ $label_type ] : $this->messenger();
222
+        return $messenger instanceof EE_messenger ? $messenger->label[$label_type] : $this->messenger();
223 223
     }
224 224
 
225 225
 
@@ -336,7 +336,7 @@  discard block
 block discarded – undo
336 336
                 $this->messenger(),
337 337
                 $this->message_type()
338 338
             );
339
-            if (! $valid && $throw_exceptions) {
339
+            if ( ! $valid && $throw_exceptions) {
340 340
                 throw new EE_Error(
341 341
                     sprintf(
342 342
                         __(
@@ -366,7 +366,7 @@  discard block
 block discarded – undo
366 366
         $label_type = $plural ? 'plural' : 'singular';
367 367
         $message_type = $this->message_type_object();
368 368
         return $message_type instanceof EE_message_type
369
-            ? $message_type->label[ $label_type ]
369
+            ? $message_type->label[$label_type]
370 370
             : str_replace(
371 371
                 '_',
372 372
                 ' ',
@@ -397,7 +397,7 @@  discard block
 block discarded – undo
397 397
         /** @type EE_Message_Resource_Manager $message_resource_manager */
398 398
         $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
399 399
         $contexts = $message_resource_manager->get_all_contexts();
400
-        return isset($contexts[ $this->context() ]) ? $contexts[ $this->context() ] : $this->context();
400
+        return isset($contexts[$this->context()]) ? $contexts[$this->context()] : $this->context();
401 401
     }
402 402
 
403 403
 
@@ -452,7 +452,7 @@  discard block
 block discarded – undo
452 452
      */
453 453
     public function recipient_object()
454 454
     {
455
-        if (! $this->recipient_type() || ! $this->recipient_ID()) {
455
+        if ( ! $this->recipient_type() || ! $this->recipient_ID()) {
456 456
             return null;
457 457
         }
458 458
 
@@ -736,13 +736,13 @@  discard block
 block discarded – undo
736 736
         /**
737 737
          * This is deprecated functionality that will be removed eventually but included here now for backward compat.
738 738
          */
739
-        if (! empty($this->template_pack)) {
739
+        if ( ! empty($this->template_pack)) {
740 740
             return $this->template_pack;
741 741
         }
742 742
         /** @type EE_Message_Template_Group $grp */
743 743
         $grp = $this->get_first_related('Message_Template_Group');
744 744
         // if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
745
-        if (! $grp instanceof EE_Message_Template_Group) {
745
+        if ( ! $grp instanceof EE_Message_Template_Group) {
746 746
             $grp = EEM_Message_Template_Group::instance()->get_one(
747 747
                 array(
748 748
                     array(
@@ -768,7 +768,7 @@  discard block
 block discarded – undo
768 768
         /**
769 769
          * This is deprecated functionality that will be removed eventually but included here now for backward compat.
770 770
          */
771
-        if (! empty($this->template_variation)) {
771
+        if ( ! empty($this->template_variation)) {
772 772
             return $this->template_variation;
773 773
         }
774 774
 
@@ -776,7 +776,7 @@  discard block
 block discarded – undo
776 776
         $grp = $this->get_first_related('Message_Template_Group');
777 777
 
778 778
         // if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
779
-        if (! $grp instanceof EE_Message_Template_Group) {
779
+        if ( ! $grp instanceof EE_Message_Template_Group) {
780 780
             $grp = EEM_Message_Template_Group::instance()->get_one(
781 781
                 array(
782 782
                     array(
Please login to merge, or discard this patch.
core/db_classes/EE_CSV.class.php 2 patches
Indentation   +669 added lines, -669 removed lines patch added patch discarded remove patch
@@ -16,673 +16,673 @@
 block discarded – undo
16 16
 class EE_CSV
17 17
 {
18 18
 
19
-    // instance of the EE_CSV object
20
-    private static $_instance = null;
21
-
22
-
23
-    // multidimensional array to store update & error messages
24
-    // var $_notices = array( 'updates' => array(), 'errors' => array() );
25
-
26
-
27
-    private $_primary_keys;
28
-
29
-    /**
30
-     *
31
-     * @var EE_Registry
32
-     */
33
-    private $EE;
34
-    /**
35
-     * string used for 1st cell in exports, which indicates that the following 2 rows will be metadata keys and values
36
-     */
37
-    const metadata_header = 'Event Espresso Export Meta Data';
38
-
39
-    /**
40
-     *        private constructor to prevent direct creation
41
-     *
42
-     * @Constructor
43
-     * @access private
44
-     * @return void
45
-     */
46
-    private function __construct()
47
-    {
48
-        global $wpdb;
49
-
50
-        $this->_primary_keys = array(
51
-            $wpdb->prefix . 'esp_answer'                  => array('ANS_ID'),
52
-            $wpdb->prefix . 'esp_attendee'                => array('ATT_ID'),
53
-            $wpdb->prefix . 'esp_datetime'                => array('DTT_ID'),
54
-            $wpdb->prefix . 'esp_event_question_group'    => array('EQG_ID'),
55
-            $wpdb->prefix . 'esp_message_template'        => array('MTP_ID'),
56
-            $wpdb->prefix . 'esp_payment'                 => array('PAY_ID'),
57
-            $wpdb->prefix . 'esp_price'                   => array('PRC_ID'),
58
-            $wpdb->prefix . 'esp_price_type'              => array('PRT_ID'),
59
-            $wpdb->prefix . 'esp_question'                => array('QST_ID'),
60
-            $wpdb->prefix . 'esp_question_group'          => array('QSG_ID'),
61
-            $wpdb->prefix . 'esp_question_group_question' => array('QGQ_ID'),
62
-            $wpdb->prefix . 'esp_question_option'         => array('QSO_ID'),
63
-            $wpdb->prefix . 'esp_registration'            => array('REG_ID'),
64
-            $wpdb->prefix . 'esp_status'                  => array('STS_ID'),
65
-            $wpdb->prefix . 'esp_transaction'             => array('TXN_ID'),
66
-            $wpdb->prefix . 'esp_transaction'             => array('TXN_ID'),
67
-            $wpdb->prefix . 'events_detail'               => array('id'),
68
-            $wpdb->prefix . 'events_category_detail'      => array('id'),
69
-            $wpdb->prefix . 'events_category_rel'         => array('id'),
70
-            $wpdb->prefix . 'events_venue'                => array('id'),
71
-            $wpdb->prefix . 'events_venue_rel'            => array('emeta_id'),
72
-            $wpdb->prefix . 'events_locale'               => array('id'),
73
-            $wpdb->prefix . 'events_locale_rel'           => array('id'),
74
-            $wpdb->prefix . 'events_personnel'            => array('id'),
75
-            $wpdb->prefix . 'events_personnel_rel'        => array('id'),
76
-        );
77
-    }
78
-
79
-
80
-    /**
81
-     *        @ singleton method used to instantiate class object
82
-     *        @ access public
83
-     *
84
-     * @return EE_CSV
85
-     */
86
-    public static function instance()
87
-    {
88
-        // check if class object is instantiated
89
-        if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_CSV)) {
90
-            self::$_instance = new self();
91
-        }
92
-        return self::$_instance;
93
-    }
94
-
95
-    /**
96
-     * Opens a unicode or utf file (normal file_get_contents has difficulty readin ga unicode file. @see
97
-     * http://stackoverflow.com/questions/15092764/how-to-read-unicode-text-file-in-php
98
-     *
99
-     * @param string $file_path
100
-     * @return string
101
-     * @throws EE_Error
102
-     */
103
-    private function read_unicode_file($file_path)
104
-    {
105
-        $fc = "";
106
-        $fh = fopen($file_path, "rb");
107
-        if (! $fh) {
108
-            throw new EE_Error(sprintf(__("Cannot open file for read: %s<br>\n", 'event_espresso'), $file_path));
109
-        }
110
-        $flen = filesize($file_path);
111
-        $bc = fread($fh, $flen);
112
-        for ($i = 0; $i < $flen; $i++) {
113
-            $c = substr($bc, $i, 1);
114
-            if ((ord($c) != 0) && (ord($c) != 13)) {
115
-                $fc = $fc . $c;
116
-            }
117
-        }
118
-        if ((ord(substr($fc, 0, 1)) == 255) && (ord(substr($fc, 1, 1)) == 254)) {
119
-            $fc = substr($fc, 2);
120
-        }
121
-        return ($fc);
122
-    }
123
-
124
-
125
-    /**
126
-     * Generic CSV-functionality to turn an entire CSV file into a single array that's
127
-     * NOT in a specific format to EE. It's just a 2-level array, with top-level arrays
128
-     * representing each row in the CSV file, and the second-level arrays being each column in that row
129
-     *
130
-     * @param string $path_to_file
131
-     * @return array of arrays. Top-level array has rows, second-level array has each item
132
-     */
133
-    public function import_csv_to_multi_dimensional_array($path_to_file)
134
-    {
135
-        // needed to deal with Mac line endings
136
-        ini_set('auto_detect_line_endings', true);
137
-
138
-        // because fgetcsv does not correctly deal with backslashed quotes such as \"
139
-        // we'll read the file into a string
140
-        $file_contents = $this->read_unicode_file($path_to_file);
141
-        // replace backslashed quotes with CSV enclosures
142
-        $file_contents = str_replace('\\"', '"""', $file_contents);
143
-        // HEY YOU! PUT THAT FILE BACK!!!
144
-        file_put_contents($path_to_file, $file_contents);
145
-
146
-        if (($file_handle = fopen($path_to_file, "r")) !== false) {
147
-            # Set the parent multidimensional array key to 0.
148
-            $nn = 0;
149
-            $csvarray = array();
150
-
151
-            // in PHP 5.3 fgetcsv accepts a 5th parameter, but the pre 5.3 versions of fgetcsv choke if passed more than 4 - is that crazy or what?
152
-            if (version_compare(PHP_VERSION, '5.3.0') < 0) {
153
-                //  PHP 5.2- version
154
-                // loop through each row of the file
155
-                while (($data = fgetcsv($file_handle, 0, ',', '"')) !== false) {
156
-                    $csvarray[] = $data;
157
-                }
158
-            } else {
159
-                // loop through each row of the file
160
-                while (($data = fgetcsv($file_handle, 0, ',', '"', '\\')) !== false) {
161
-                    $csvarray[] = $data;
162
-                }
163
-            }
164
-            # Close the File.
165
-            fclose($file_handle);
166
-            return $csvarray;
167
-        } else {
168
-            EE_Error::add_error(
169
-                sprintf(__("An error occurred - the file: %s could not opened.", "event_espresso"), $path_to_file),
170
-                __FILE__,
171
-                __FUNCTION__,
172
-                __LINE__
173
-            );
174
-            return false;
175
-        }
176
-    }
177
-
178
-
179
-    /**
180
-     * @Import contents of csv file and store values in an array to be manipulated by other functions
181
-     * @access public
182
-     * @param string  $path_to_file         - the csv file to be imported including the path to it's location.
183
-     *                                      If $model_name is provided, assumes that each row in the CSV represents a
184
-     *                                      model object for that model If $model_name ISN'T provided, assumes that
185
-     *                                      before model object data, there is a row where the first entry is simply
186
-     *                                      'MODEL', and next entry is the model's name, (untranslated) like Event, and
187
-     *                                      then maybe a row of headers, and then the model data. Eg.
188
-     *                                      '<br>MODEL,Event,<br>EVT_ID,EVT_name,...<br>1,Monkey
189
-     *                                      Party,...<br>2,Llamarama,...<br>MODEL,Venue,<br>VNU_ID,VNU_name<br>1,The
190
-     *                                      Forest
191
-     * @param string  $model_name           model name if we know what model we're importing
192
-     * @param boolean $first_row_is_headers - whether the first row of data is headers or not - TRUE = headers, FALSE =
193
-     *                                      data
194
-     * @return mixed - array on success - multi dimensional with headers as keys (if headers exist) OR string on fail -
195
-     *               error message like the following array('Event'=>array( array('EVT_ID'=>1,'EVT_name'=>'bob
196
-     *               party',...), array('EVT_ID'=>2,'EVT_name'=>'llamarama',...),
197
-     *                                      ...
198
-     *                                      )
199
-     *                                      'Venue'=>array(
200
-     *                                      array('VNU_ID'=>1,'VNU_name'=>'the shack',...),
201
-     *                                      array('VNU_ID'=>2,'VNU_name'=>'tree house',...),
202
-     *                                      ...
203
-     *                                      )
204
-     *                                      ...
205
-     *                                      )
206
-     */
207
-    public function import_csv_to_model_data_array($path_to_file, $model_name = false, $first_row_is_headers = true)
208
-    {
209
-        $multi_dimensional_array = $this->import_csv_to_multi_dimensional_array($path_to_file);
210
-        if (! $multi_dimensional_array) {
211
-            return false;
212
-        }
213
-        // gotta start somewhere
214
-        $row = 1;
215
-        // array to store csv data in
216
-        $ee_formatted_data = array();
217
-        // array to store headers (column names)
218
-        $headers = array();
219
-        foreach ($multi_dimensional_array as $data) {
220
-            // if first cell is MODEL, then second cell is the MODEL name
221
-            if ($data[0] == 'MODEL') {
222
-                $model_name = $data[1];
223
-                // don't bother looking for model data in this row. The rest of this
224
-                // row should be blank
225
-                // AND pretend this is the first row again
226
-                $row = 1;
227
-                // reset headers
228
-                $headers = array();
229
-                continue;
230
-            }
231
-            if (strpos($data[0], EE_CSV::metadata_header) !== false) {
232
-                $model_name = EE_CSV::metadata_header;
233
-                // store like model data, we just won't try importing it etc.
234
-                $row = 1;
235
-                continue;
236
-            }
237
-
238
-
239
-            // how many columns are there?
240
-            $columns = count($data);
241
-
242
-            $model_entry = array();
243
-            // loop through each column
244
-            for ($i = 0; $i < $columns; $i++) {
245
-                // replace csv_enclosures with backslashed quotes
246
-                $data[ $i ] = str_replace('"""', '\\"', $data[ $i ]);
247
-                // do we need to grab the column names?
248
-                if ($row === 1) {
249
-                    if ($first_row_is_headers) {
250
-                        // store the column names to use for keys
251
-                        $column_name = $data[ $i ];
252
-                        // check it's not blank... sometimes CSV editign programs adda bunch of empty columns onto the end...
253
-                        if (! $column_name) {
254
-                            continue;
255
-                        }
256
-                        $matches = array();
257
-                        if ($model_name == EE_CSV::metadata_header) {
258
-                            $headers[ $i ] = $column_name;
259
-                        } else {
260
-                            // now get the db table name from it (the part between square brackets)
261
-                            $success = preg_match('~(.*)\[(.*)\]~', $column_name, $matches);
262
-                            if (! $success) {
263
-                                EE_Error::add_error(
264
-                                    sprintf(
265
-                                        __(
266
-                                            "The column titled %s is invalid for importing. It must be be in the format of 'Nice Name[model_field_name]' in row %s",
267
-                                            "event_espresso"
268
-                                        ),
269
-                                        $column_name,
270
-                                        implode(",", $data)
271
-                                    ),
272
-                                    __FILE__,
273
-                                    __FUNCTION__,
274
-                                    __LINE__
275
-                                );
276
-                                return false;
277
-                            }
278
-                            $headers[ $i ] = $matches[2];
279
-                        }
280
-                    } else {
281
-                        // no column names means our final array will just use counters for keys
282
-                        $model_entry[ $headers[ $i ] ] = $data[ $i ];
283
-                        $headers[ $i ] = $i;
284
-                    }
285
-                    // and we need to store csv data
286
-                } else {
287
-                    // this column isn' ta header, store it if there is a header for it
288
-                    if (isset($headers[ $i ])) {
289
-                        $model_entry[ $headers[ $i ] ] = $data[ $i ];
290
-                    }
291
-                }
292
-            }
293
-            // save the row's data IF it's a non-header-row
294
-            if (! $first_row_is_headers || ($first_row_is_headers && $row > 1)) {
295
-                $ee_formatted_data[ $model_name ][] = $model_entry;
296
-            }
297
-            // advance to next row
298
-            $row++;
299
-        }
300
-
301
-        // delete the uploaded file
302
-        unlink($path_to_file);
303
-        // echo '<pre style="height:auto;border:2px solid lightblue;">' . print_r( $ee_formatted_data, TRUE ) . '</pre><br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span>';
304
-        // die();
305
-
306
-        // it's good to give back
307
-        return $ee_formatted_data;
308
-    }
309
-
310
-
311
-    public function save_csv_to_db($csv_data_array, $model_name = false)
312
-    {
313
-        EE_Error::doing_it_wrong(
314
-            'save_csv_to_db',
315
-            __(
316
-                'Function moved to EE_Import and renamed to save_csv_data_array_to_db',
317
-                'event_espresso'
318
-            ),
319
-            '4.6.7'
320
-        );
321
-        return EE_Import::instance()->save_csv_data_array_to_db($csv_data_array, $model_name);
322
-    }
323
-
324
-    /**
325
-     * Sends HTTP headers to indicate that the browser should download a file,
326
-     * and starts writing the file to PHP's output. Returns the file handle so other functions can
327
-     * also write to it
328
-     *
329
-     * @param string $new_filename the name of the file that the user will download
330
-     * @return resource, like the results of fopen(), which can be used for fwrite, fputcsv2, etc.
331
-     */
332
-    public function begin_sending_csv($filename)
333
-    {
334
-        // grab file extension
335
-        $ext = substr(strrchr($filename, '.'), 1);
336
-        if ($ext == '.csv' or $ext == '.xls') {
337
-            str_replace($ext, '', $filename);
338
-        }
339
-        $filename .= '.csv';
340
-
341
-        // if somebody's been naughty and already started outputting stuff, trash it
342
-        // and start writing our stuff.
343
-        if (ob_get_length()) {
344
-            @ob_flush();
345
-            @flush();
346
-            @ob_end_flush();
347
-        }
348
-        @ob_start();
349
-        header("Pragma: public");
350
-        header("Expires: 0");
351
-        header("Pragma: no-cache");
352
-        header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
353
-        // header("Content-Type: application/force-download");
354
-        // header("Content-Type: application/octet-stream");
355
-        // header("Content-Type: application/download");
356
-        header('Content-disposition: attachment; filename=' . $filename);
357
-        header("Content-Type: text/csv; charset=utf-8");
358
-        do_action('AHEE__EE_CSV__begin_sending_csv__headers');
359
-        echo apply_filters(
360
-            'FHEE__EE_CSV__begin_sending_csv__start_writing',
361
-            "\xEF\xBB\xBF"
362
-        ); // makes excel open it as UTF-8. UTF-8 BOM, see http://stackoverflow.com/a/4440143/2773835
363
-        $fh = fopen('php://output', 'w');
364
-        return $fh;
365
-    }
366
-
367
-    /**
368
-     * Writes some meta data to the CSV as a bunch of columns. Initially we're only
369
-     * mentioning the version and timezone
370
-     *
371
-     * @param resource $filehandle
372
-     */
373
-    public function write_metadata_to_csv($filehandle)
374
-    {
375
-        $data_row = array(EE_CSV::metadata_header);// do NOT translate because this exact string is used when importing
376
-        $this->fputcsv2($filehandle, $data_row);
377
-        $meta_data = array(
378
-            0 => array(
379
-                'version'        => espresso_version(),
380
-                'timezone'       => EEH_DTT_Helper::get_timezone(),
381
-                'time_of_export' => current_time('mysql'),
382
-                'site_url'       => site_url(),
383
-            ),
384
-        );
385
-        $this->write_data_array_to_csv($filehandle, $meta_data);
386
-    }
387
-
388
-
389
-    /**
390
-     * Writes $data to the csv file open in $filehandle. uses the array indices of $data for column headers
391
-     *
392
-     * @param array   $data                 2D array, first numerically-indexed, and next-level-down preferably indexed
393
-     *                                      by string
394
-     * @param boolean $add_csv_column_names whether or not we should add the keys in the bottom-most array as a row for
395
-     *                                      headers in the CSV. Eg, if $data looked like
396
-     *                                      array(0=>array('EVT_ID'=>1,'EVT_name'=>'monkey'...), 1=>array(...),...))
397
-     *                                      then the first row we'd write to the CSV would be "EVT_ID,EVT_name,..."
398
-     * @return boolean if we successfully wrote to the CSV or not. If there's no $data, we consider that a success
399
-     *                 (because we wrote everything there was...nothing)
400
-     */
401
-    public function write_data_array_to_csv($filehandle, $data)
402
-    {
403
-
404
-
405
-        // determine if $data is actually a 2d array
406
-        if ($data && is_array($data) && is_array(EEH_Array::get_one_item_from_array($data))) {
407
-            // make sure top level is numerically indexed,
408
-
409
-            if (EEH_Array::is_associative_array($data)) {
410
-                throw new EE_Error(
411
-                    sprintf(
412
-                        __(
413
-                            "top-level array must be numerically indexed. Does these look like numbers to you? %s",
414
-                            "event_espresso"
415
-                        ),
416
-                        implode(",", array_keys($data))
417
-                    )
418
-                );
419
-            }
420
-            $item_in_top_level_array = EEH_Array::get_one_item_from_array($data);
421
-            // now, is the last item in the top-level array of $data an associative or numeric array?
422
-            if (EEH_Array::is_associative_array($item_in_top_level_array)) {
423
-                // its associative, so we want to output its keys as column headers
424
-                $keys = array_keys($item_in_top_level_array);
425
-                echo $this->fputcsv2($filehandle, $keys);
426
-            }
427
-            // start writing data
428
-            foreach ($data as $data_row) {
429
-                echo $this->fputcsv2($filehandle, $data_row);
430
-            }
431
-            return true;
432
-        } else {
433
-            // no data TO write... so we can assume that's a success
434
-            return true;
435
-        }
436
-        // //if 2nd level is indexed by strings, use those as csv column headers (ie, the first row)
437
-        //
438
-        //
439
-        // $no_table = TRUE;
440
-        //
441
-        // // loop through data and add each row to the file/stream as csv
442
-        // foreach ( $data as $model_name => $model_data ) {
443
-        // // test first row to see if it is data or a model name
444
-        // $model = EE_Registry::instance();->load_model($model_name);
445
-        // //if the model really exists,
446
-        // if ( $model ) {
447
-        //
448
-        // // we have a table name
449
-        // $no_table = FALSE;
450
-        //
451
-        // // put the tablename into an array cuz that's how fputcsv rolls
452
-        // $model_name_row = array( 'MODEL', $model_name );
453
-        //
454
-        // // add table name to csv output
455
-        // echo self::fputcsv2($filehandle, $model_name_row);
456
-        //
457
-        // // now get the rest of the data
458
-        // foreach ( $model_data as $row ) {
459
-        // // output the row
460
-        // echo self::fputcsv2($filehandle, $row);
461
-        // }
462
-        //
463
-        // }
464
-        //
465
-        // if ( $no_table ) {
466
-        // // no table so just put the data
467
-        // echo self::fputcsv2($filehandle, $model_data);
468
-        // }
469
-        //
470
-        // } // END OF foreach ( $data )
471
-    }
472
-
473
-    /**
474
-     * Should be called after begin_sending_csv(), and one or more write_data_array_to_csv()s.
475
-     * Calls exit to prevent polluting the CSV file with other junk
476
-     *
477
-     * @param resource $fh filehandle where we're writing the CSV to
478
-     */
479
-    public function end_sending_csv($fh)
480
-    {
481
-        fclose($fh);
482
-        exit(0);
483
-    }
484
-
485
-    /**
486
-     * Given an open file, writes all the model data to it in the format the importer expects.
487
-     * Usually preceded by begin_sending_csv($filename), and followed by end_sending_csv($filehandle).
488
-     *
489
-     * @param resource $filehandle
490
-     * @param array    $model_data_array is assumed to be a 3d array: 1st layer has keys of model names (eg 'Event'),
491
-     *                                   next layer is numerically indexed to represent each model object (eg, each
492
-     *                                   individual event), and the last layer has all the attributes o fthat model
493
-     *                                   object (eg, the event's id, name, etc)
494
-     * @return boolean success
495
-     */
496
-    public function write_model_data_to_csv($filehandle, $model_data_array)
497
-    {
498
-        $this->write_metadata_to_csv($filehandle);
499
-        foreach ($model_data_array as $model_name => $model_instance_arrays) {
500
-            // first: output a special row stating the model
501
-            echo $this->fputcsv2($filehandle, array('MODEL', $model_name));
502
-            // if we have items to put in the CSV, do it normally
503
-
504
-            if (! empty($model_instance_arrays)) {
505
-                $this->write_data_array_to_csv($filehandle, $model_instance_arrays);
506
-            } else {
507
-                // echo "no data to write... so just write the headers";
508
-                // so there's actually NO model objects for that model.
509
-                // probably still want to show the columns
510
-                $model = EE_Registry::instance()->load_model($model_name);
511
-                $column_names = array();
512
-                foreach ($model->field_settings() as $field) {
513
-                    $column_names[ $field->get_nicename() . "[" . $field->get_name() . "]" ] = null;
514
-                }
515
-                $this->write_data_array_to_csv($filehandle, array($column_names));
516
-            }
517
-        }
518
-    }
519
-
520
-    /**
521
-     * Writes the CSV file to the output buffer, with rows corresponding to $model_data_array,
522
-     * and dies (in order to avoid other plugins from messing up the csv output)
523
-     *
524
-     * @param string $filename         the filename you want to give the file
525
-     * @param array  $model_data_array 3d array, as described in EE_CSV::write_model_data_to_csv()
526
-     * @return bool | void writes CSV file to output and dies
527
-     */
528
-    public function export_multiple_model_data_to_csv($filename, $model_data_array)
529
-    {
530
-        $filehandle = $this->begin_sending_csv($filename);
531
-        $this->write_model_data_to_csv($filehandle, $model_data_array);
532
-        $this->end_sending_csv($filehandle);
533
-    }
534
-
535
-    /**
536
-     * @Export contents of an array to csv file
537
-     * @access public
538
-     * @param array  $data     - the array of data to be converted to csv and exported
539
-     * @param string $filename - name for newly created csv file
540
-     * @return TRUE on success, FALSE on fail
541
-     */
542
-    public function export_array_to_csv($data = false, $filename = false)
543
-    {
544
-
545
-        // no data file?? get outta here
546
-        if (! $data or ! is_array($data) or empty($data)) {
547
-            return false;
548
-        }
549
-
550
-        // no filename?? get outta here
551
-        if (! $filename) {
552
-            return false;
553
-        }
554
-
555
-
556
-        // somebody told me i might need this ???
557
-        global $wpdb;
558
-        $prefix = $wpdb->prefix;
559
-
560
-
561
-        $fh = $this->begin_sending_csv($filename);
562
-
563
-
564
-        $this->end_sending_csv($fh);
565
-    }
566
-
567
-
568
-    /**
569
-     * @Determine the maximum upload file size based on php.ini settings
570
-     * @access    public
571
-     * @param int $percent_of_max - desired percentage of the max upload_mb
572
-     * @return int KB
573
-     */
574
-    public function get_max_upload_size($percent_of_max = false)
575
-    {
576
-
577
-        $max_upload = (int) (ini_get('upload_max_filesize'));
578
-        $max_post = (int) (ini_get('post_max_size'));
579
-        $memory_limit = (int) (ini_get('memory_limit'));
580
-
581
-        // determine the smallest of the three values from above
582
-        $upload_mb = min($max_upload, $max_post, $memory_limit);
583
-
584
-        // convert MB to KB
585
-        $upload_mb = $upload_mb * 1024;
586
-
587
-        // don't want the full monty? then reduce the max uplaod size
588
-        if ($percent_of_max) {
589
-            // is percent_of_max like this -> 50 or like this -> 0.50 ?
590
-            if ($percent_of_max > 1) {
591
-                // chnages 50 to 0.50
592
-                $percent_of_max = $percent_of_max / 100;
593
-            }
594
-            // make upload_mb a percentage of the max upload_mb
595
-            $upload_mb = $upload_mb * $percent_of_max;
596
-        }
597
-
598
-        return $upload_mb;
599
-    }
600
-
601
-
602
-    /**
603
-     * @Drop   in replacement for PHP's fputcsv function - but this one works!!!
604
-     * @access private
605
-     * @param resource $fh         - file handle - what we are writing to
606
-     * @param array    $row        - individual row of csv data
607
-     * @param string   $delimiter  - csv delimiter
608
-     * @param string   $enclosure  - csv enclosure
609
-     * @param string   $mysql_null - allows php NULL to be overridden with MySQl's insertable NULL value
610
-     * @return void
611
-     */
612
-    private function fputcsv2($fh, array $row, $delimiter = ',', $enclosure = '"', $mysql_null = false)
613
-    {
614
-        // Allow user to filter the csv delimiter and enclosure for other countries csv standards
615
-        $delimiter = apply_filters('FHEE__EE_CSV__fputcsv2__delimiter', $delimiter);
616
-        $enclosure = apply_filters('FHEE__EE_CSV__fputcsv2__enclosure', $enclosure);
617
-
618
-        $delimiter_esc = preg_quote($delimiter, '/');
619
-        $enclosure_esc = preg_quote($enclosure, '/');
620
-
621
-        $output = array();
622
-        foreach ($row as $field_value) {
623
-            if (is_object($field_value) || is_array($field_value)) {
624
-                $field_value = serialize($field_value);
625
-            }
626
-            if ($field_value === null && $mysql_null) {
627
-                $output[] = 'NULL';
628
-                continue;
629
-            }
630
-
631
-            $output[] = preg_match("/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field_value) ?
632
-                ($enclosure . str_replace($enclosure, $enclosure . $enclosure, $field_value) . $enclosure)
633
-                : $field_value;
634
-        }
635
-
636
-        fwrite($fh, join($delimiter, $output) . PHP_EOL);
637
-    }
638
-
639
-
640
-    // /**
641
-    //  * @CSV    Import / Export messages
642
-    //  * @access public
643
-    //  * @return void
644
-    //  */
645
-    // public function csv_admin_notices()
646
-    // {
647
-    //
648
-    //     // We play both kinds of music here! Country AND Western! - err... I mean, cycle through both types of notices
649
-    //     foreach (array('updates', 'errors') as $type) {
650
-    //
651
-    //         // if particular notice type is not empty, then "You've got Mail"
652
-    //         if (! empty($this->_notices[ $type ])) {
653
-    //
654
-    //             // is it an update or an error ?
655
-    //             $msg_class = $type == 'updates' ? 'updated' : 'error';
656
-    //             echo '<div id="message" class="' . $msg_class . '">';
657
-    //             // display each notice, however many that may be
658
-    //             foreach ($this->_notices[ $type ] as $message) {
659
-    //                 echo '<p>' . $message . '</p>';
660
-    //             }
661
-    //             // wrap it up
662
-    //             echo '</div>';
663
-    //         }
664
-    //     }
665
-    // }
666
-
667
-    /**
668
-     * Gets the date format to use in teh csv. filterable
669
-     *
670
-     * @param string $current_format
671
-     * @return string
672
-     */
673
-    public function get_date_format_for_csv($current_format = null)
674
-    {
675
-        return apply_filters('FHEE__EE_CSV__get_date_format_for_csv__format', 'Y-m-d', $current_format);
676
-    }
677
-
678
-    /**
679
-     * Gets the time format we want to use in CSV reports. Filterable
680
-     *
681
-     * @param string $current_format
682
-     * @return string
683
-     */
684
-    public function get_time_format_for_csv($current_format = null)
685
-    {
686
-        return apply_filters('FHEE__EE_CSV__get_time_format_for_csv__format', 'H:i:s', $current_format);
687
-    }
19
+	// instance of the EE_CSV object
20
+	private static $_instance = null;
21
+
22
+
23
+	// multidimensional array to store update & error messages
24
+	// var $_notices = array( 'updates' => array(), 'errors' => array() );
25
+
26
+
27
+	private $_primary_keys;
28
+
29
+	/**
30
+	 *
31
+	 * @var EE_Registry
32
+	 */
33
+	private $EE;
34
+	/**
35
+	 * string used for 1st cell in exports, which indicates that the following 2 rows will be metadata keys and values
36
+	 */
37
+	const metadata_header = 'Event Espresso Export Meta Data';
38
+
39
+	/**
40
+	 *        private constructor to prevent direct creation
41
+	 *
42
+	 * @Constructor
43
+	 * @access private
44
+	 * @return void
45
+	 */
46
+	private function __construct()
47
+	{
48
+		global $wpdb;
49
+
50
+		$this->_primary_keys = array(
51
+			$wpdb->prefix . 'esp_answer'                  => array('ANS_ID'),
52
+			$wpdb->prefix . 'esp_attendee'                => array('ATT_ID'),
53
+			$wpdb->prefix . 'esp_datetime'                => array('DTT_ID'),
54
+			$wpdb->prefix . 'esp_event_question_group'    => array('EQG_ID'),
55
+			$wpdb->prefix . 'esp_message_template'        => array('MTP_ID'),
56
+			$wpdb->prefix . 'esp_payment'                 => array('PAY_ID'),
57
+			$wpdb->prefix . 'esp_price'                   => array('PRC_ID'),
58
+			$wpdb->prefix . 'esp_price_type'              => array('PRT_ID'),
59
+			$wpdb->prefix . 'esp_question'                => array('QST_ID'),
60
+			$wpdb->prefix . 'esp_question_group'          => array('QSG_ID'),
61
+			$wpdb->prefix . 'esp_question_group_question' => array('QGQ_ID'),
62
+			$wpdb->prefix . 'esp_question_option'         => array('QSO_ID'),
63
+			$wpdb->prefix . 'esp_registration'            => array('REG_ID'),
64
+			$wpdb->prefix . 'esp_status'                  => array('STS_ID'),
65
+			$wpdb->prefix . 'esp_transaction'             => array('TXN_ID'),
66
+			$wpdb->prefix . 'esp_transaction'             => array('TXN_ID'),
67
+			$wpdb->prefix . 'events_detail'               => array('id'),
68
+			$wpdb->prefix . 'events_category_detail'      => array('id'),
69
+			$wpdb->prefix . 'events_category_rel'         => array('id'),
70
+			$wpdb->prefix . 'events_venue'                => array('id'),
71
+			$wpdb->prefix . 'events_venue_rel'            => array('emeta_id'),
72
+			$wpdb->prefix . 'events_locale'               => array('id'),
73
+			$wpdb->prefix . 'events_locale_rel'           => array('id'),
74
+			$wpdb->prefix . 'events_personnel'            => array('id'),
75
+			$wpdb->prefix . 'events_personnel_rel'        => array('id'),
76
+		);
77
+	}
78
+
79
+
80
+	/**
81
+	 *        @ singleton method used to instantiate class object
82
+	 *        @ access public
83
+	 *
84
+	 * @return EE_CSV
85
+	 */
86
+	public static function instance()
87
+	{
88
+		// check if class object is instantiated
89
+		if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_CSV)) {
90
+			self::$_instance = new self();
91
+		}
92
+		return self::$_instance;
93
+	}
94
+
95
+	/**
96
+	 * Opens a unicode or utf file (normal file_get_contents has difficulty readin ga unicode file. @see
97
+	 * http://stackoverflow.com/questions/15092764/how-to-read-unicode-text-file-in-php
98
+	 *
99
+	 * @param string $file_path
100
+	 * @return string
101
+	 * @throws EE_Error
102
+	 */
103
+	private function read_unicode_file($file_path)
104
+	{
105
+		$fc = "";
106
+		$fh = fopen($file_path, "rb");
107
+		if (! $fh) {
108
+			throw new EE_Error(sprintf(__("Cannot open file for read: %s<br>\n", 'event_espresso'), $file_path));
109
+		}
110
+		$flen = filesize($file_path);
111
+		$bc = fread($fh, $flen);
112
+		for ($i = 0; $i < $flen; $i++) {
113
+			$c = substr($bc, $i, 1);
114
+			if ((ord($c) != 0) && (ord($c) != 13)) {
115
+				$fc = $fc . $c;
116
+			}
117
+		}
118
+		if ((ord(substr($fc, 0, 1)) == 255) && (ord(substr($fc, 1, 1)) == 254)) {
119
+			$fc = substr($fc, 2);
120
+		}
121
+		return ($fc);
122
+	}
123
+
124
+
125
+	/**
126
+	 * Generic CSV-functionality to turn an entire CSV file into a single array that's
127
+	 * NOT in a specific format to EE. It's just a 2-level array, with top-level arrays
128
+	 * representing each row in the CSV file, and the second-level arrays being each column in that row
129
+	 *
130
+	 * @param string $path_to_file
131
+	 * @return array of arrays. Top-level array has rows, second-level array has each item
132
+	 */
133
+	public function import_csv_to_multi_dimensional_array($path_to_file)
134
+	{
135
+		// needed to deal with Mac line endings
136
+		ini_set('auto_detect_line_endings', true);
137
+
138
+		// because fgetcsv does not correctly deal with backslashed quotes such as \"
139
+		// we'll read the file into a string
140
+		$file_contents = $this->read_unicode_file($path_to_file);
141
+		// replace backslashed quotes with CSV enclosures
142
+		$file_contents = str_replace('\\"', '"""', $file_contents);
143
+		// HEY YOU! PUT THAT FILE BACK!!!
144
+		file_put_contents($path_to_file, $file_contents);
145
+
146
+		if (($file_handle = fopen($path_to_file, "r")) !== false) {
147
+			# Set the parent multidimensional array key to 0.
148
+			$nn = 0;
149
+			$csvarray = array();
150
+
151
+			// in PHP 5.3 fgetcsv accepts a 5th parameter, but the pre 5.3 versions of fgetcsv choke if passed more than 4 - is that crazy or what?
152
+			if (version_compare(PHP_VERSION, '5.3.0') < 0) {
153
+				//  PHP 5.2- version
154
+				// loop through each row of the file
155
+				while (($data = fgetcsv($file_handle, 0, ',', '"')) !== false) {
156
+					$csvarray[] = $data;
157
+				}
158
+			} else {
159
+				// loop through each row of the file
160
+				while (($data = fgetcsv($file_handle, 0, ',', '"', '\\')) !== false) {
161
+					$csvarray[] = $data;
162
+				}
163
+			}
164
+			# Close the File.
165
+			fclose($file_handle);
166
+			return $csvarray;
167
+		} else {
168
+			EE_Error::add_error(
169
+				sprintf(__("An error occurred - the file: %s could not opened.", "event_espresso"), $path_to_file),
170
+				__FILE__,
171
+				__FUNCTION__,
172
+				__LINE__
173
+			);
174
+			return false;
175
+		}
176
+	}
177
+
178
+
179
+	/**
180
+	 * @Import contents of csv file and store values in an array to be manipulated by other functions
181
+	 * @access public
182
+	 * @param string  $path_to_file         - the csv file to be imported including the path to it's location.
183
+	 *                                      If $model_name is provided, assumes that each row in the CSV represents a
184
+	 *                                      model object for that model If $model_name ISN'T provided, assumes that
185
+	 *                                      before model object data, there is a row where the first entry is simply
186
+	 *                                      'MODEL', and next entry is the model's name, (untranslated) like Event, and
187
+	 *                                      then maybe a row of headers, and then the model data. Eg.
188
+	 *                                      '<br>MODEL,Event,<br>EVT_ID,EVT_name,...<br>1,Monkey
189
+	 *                                      Party,...<br>2,Llamarama,...<br>MODEL,Venue,<br>VNU_ID,VNU_name<br>1,The
190
+	 *                                      Forest
191
+	 * @param string  $model_name           model name if we know what model we're importing
192
+	 * @param boolean $first_row_is_headers - whether the first row of data is headers or not - TRUE = headers, FALSE =
193
+	 *                                      data
194
+	 * @return mixed - array on success - multi dimensional with headers as keys (if headers exist) OR string on fail -
195
+	 *               error message like the following array('Event'=>array( array('EVT_ID'=>1,'EVT_name'=>'bob
196
+	 *               party',...), array('EVT_ID'=>2,'EVT_name'=>'llamarama',...),
197
+	 *                                      ...
198
+	 *                                      )
199
+	 *                                      'Venue'=>array(
200
+	 *                                      array('VNU_ID'=>1,'VNU_name'=>'the shack',...),
201
+	 *                                      array('VNU_ID'=>2,'VNU_name'=>'tree house',...),
202
+	 *                                      ...
203
+	 *                                      )
204
+	 *                                      ...
205
+	 *                                      )
206
+	 */
207
+	public function import_csv_to_model_data_array($path_to_file, $model_name = false, $first_row_is_headers = true)
208
+	{
209
+		$multi_dimensional_array = $this->import_csv_to_multi_dimensional_array($path_to_file);
210
+		if (! $multi_dimensional_array) {
211
+			return false;
212
+		}
213
+		// gotta start somewhere
214
+		$row = 1;
215
+		// array to store csv data in
216
+		$ee_formatted_data = array();
217
+		// array to store headers (column names)
218
+		$headers = array();
219
+		foreach ($multi_dimensional_array as $data) {
220
+			// if first cell is MODEL, then second cell is the MODEL name
221
+			if ($data[0] == 'MODEL') {
222
+				$model_name = $data[1];
223
+				// don't bother looking for model data in this row. The rest of this
224
+				// row should be blank
225
+				// AND pretend this is the first row again
226
+				$row = 1;
227
+				// reset headers
228
+				$headers = array();
229
+				continue;
230
+			}
231
+			if (strpos($data[0], EE_CSV::metadata_header) !== false) {
232
+				$model_name = EE_CSV::metadata_header;
233
+				// store like model data, we just won't try importing it etc.
234
+				$row = 1;
235
+				continue;
236
+			}
237
+
238
+
239
+			// how many columns are there?
240
+			$columns = count($data);
241
+
242
+			$model_entry = array();
243
+			// loop through each column
244
+			for ($i = 0; $i < $columns; $i++) {
245
+				// replace csv_enclosures with backslashed quotes
246
+				$data[ $i ] = str_replace('"""', '\\"', $data[ $i ]);
247
+				// do we need to grab the column names?
248
+				if ($row === 1) {
249
+					if ($first_row_is_headers) {
250
+						// store the column names to use for keys
251
+						$column_name = $data[ $i ];
252
+						// check it's not blank... sometimes CSV editign programs adda bunch of empty columns onto the end...
253
+						if (! $column_name) {
254
+							continue;
255
+						}
256
+						$matches = array();
257
+						if ($model_name == EE_CSV::metadata_header) {
258
+							$headers[ $i ] = $column_name;
259
+						} else {
260
+							// now get the db table name from it (the part between square brackets)
261
+							$success = preg_match('~(.*)\[(.*)\]~', $column_name, $matches);
262
+							if (! $success) {
263
+								EE_Error::add_error(
264
+									sprintf(
265
+										__(
266
+											"The column titled %s is invalid for importing. It must be be in the format of 'Nice Name[model_field_name]' in row %s",
267
+											"event_espresso"
268
+										),
269
+										$column_name,
270
+										implode(",", $data)
271
+									),
272
+									__FILE__,
273
+									__FUNCTION__,
274
+									__LINE__
275
+								);
276
+								return false;
277
+							}
278
+							$headers[ $i ] = $matches[2];
279
+						}
280
+					} else {
281
+						// no column names means our final array will just use counters for keys
282
+						$model_entry[ $headers[ $i ] ] = $data[ $i ];
283
+						$headers[ $i ] = $i;
284
+					}
285
+					// and we need to store csv data
286
+				} else {
287
+					// this column isn' ta header, store it if there is a header for it
288
+					if (isset($headers[ $i ])) {
289
+						$model_entry[ $headers[ $i ] ] = $data[ $i ];
290
+					}
291
+				}
292
+			}
293
+			// save the row's data IF it's a non-header-row
294
+			if (! $first_row_is_headers || ($first_row_is_headers && $row > 1)) {
295
+				$ee_formatted_data[ $model_name ][] = $model_entry;
296
+			}
297
+			// advance to next row
298
+			$row++;
299
+		}
300
+
301
+		// delete the uploaded file
302
+		unlink($path_to_file);
303
+		// echo '<pre style="height:auto;border:2px solid lightblue;">' . print_r( $ee_formatted_data, TRUE ) . '</pre><br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span>';
304
+		// die();
305
+
306
+		// it's good to give back
307
+		return $ee_formatted_data;
308
+	}
309
+
310
+
311
+	public function save_csv_to_db($csv_data_array, $model_name = false)
312
+	{
313
+		EE_Error::doing_it_wrong(
314
+			'save_csv_to_db',
315
+			__(
316
+				'Function moved to EE_Import and renamed to save_csv_data_array_to_db',
317
+				'event_espresso'
318
+			),
319
+			'4.6.7'
320
+		);
321
+		return EE_Import::instance()->save_csv_data_array_to_db($csv_data_array, $model_name);
322
+	}
323
+
324
+	/**
325
+	 * Sends HTTP headers to indicate that the browser should download a file,
326
+	 * and starts writing the file to PHP's output. Returns the file handle so other functions can
327
+	 * also write to it
328
+	 *
329
+	 * @param string $new_filename the name of the file that the user will download
330
+	 * @return resource, like the results of fopen(), which can be used for fwrite, fputcsv2, etc.
331
+	 */
332
+	public function begin_sending_csv($filename)
333
+	{
334
+		// grab file extension
335
+		$ext = substr(strrchr($filename, '.'), 1);
336
+		if ($ext == '.csv' or $ext == '.xls') {
337
+			str_replace($ext, '', $filename);
338
+		}
339
+		$filename .= '.csv';
340
+
341
+		// if somebody's been naughty and already started outputting stuff, trash it
342
+		// and start writing our stuff.
343
+		if (ob_get_length()) {
344
+			@ob_flush();
345
+			@flush();
346
+			@ob_end_flush();
347
+		}
348
+		@ob_start();
349
+		header("Pragma: public");
350
+		header("Expires: 0");
351
+		header("Pragma: no-cache");
352
+		header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
353
+		// header("Content-Type: application/force-download");
354
+		// header("Content-Type: application/octet-stream");
355
+		// header("Content-Type: application/download");
356
+		header('Content-disposition: attachment; filename=' . $filename);
357
+		header("Content-Type: text/csv; charset=utf-8");
358
+		do_action('AHEE__EE_CSV__begin_sending_csv__headers');
359
+		echo apply_filters(
360
+			'FHEE__EE_CSV__begin_sending_csv__start_writing',
361
+			"\xEF\xBB\xBF"
362
+		); // makes excel open it as UTF-8. UTF-8 BOM, see http://stackoverflow.com/a/4440143/2773835
363
+		$fh = fopen('php://output', 'w');
364
+		return $fh;
365
+	}
366
+
367
+	/**
368
+	 * Writes some meta data to the CSV as a bunch of columns. Initially we're only
369
+	 * mentioning the version and timezone
370
+	 *
371
+	 * @param resource $filehandle
372
+	 */
373
+	public function write_metadata_to_csv($filehandle)
374
+	{
375
+		$data_row = array(EE_CSV::metadata_header);// do NOT translate because this exact string is used when importing
376
+		$this->fputcsv2($filehandle, $data_row);
377
+		$meta_data = array(
378
+			0 => array(
379
+				'version'        => espresso_version(),
380
+				'timezone'       => EEH_DTT_Helper::get_timezone(),
381
+				'time_of_export' => current_time('mysql'),
382
+				'site_url'       => site_url(),
383
+			),
384
+		);
385
+		$this->write_data_array_to_csv($filehandle, $meta_data);
386
+	}
387
+
388
+
389
+	/**
390
+	 * Writes $data to the csv file open in $filehandle. uses the array indices of $data for column headers
391
+	 *
392
+	 * @param array   $data                 2D array, first numerically-indexed, and next-level-down preferably indexed
393
+	 *                                      by string
394
+	 * @param boolean $add_csv_column_names whether or not we should add the keys in the bottom-most array as a row for
395
+	 *                                      headers in the CSV. Eg, if $data looked like
396
+	 *                                      array(0=>array('EVT_ID'=>1,'EVT_name'=>'monkey'...), 1=>array(...),...))
397
+	 *                                      then the first row we'd write to the CSV would be "EVT_ID,EVT_name,..."
398
+	 * @return boolean if we successfully wrote to the CSV or not. If there's no $data, we consider that a success
399
+	 *                 (because we wrote everything there was...nothing)
400
+	 */
401
+	public function write_data_array_to_csv($filehandle, $data)
402
+	{
403
+
404
+
405
+		// determine if $data is actually a 2d array
406
+		if ($data && is_array($data) && is_array(EEH_Array::get_one_item_from_array($data))) {
407
+			// make sure top level is numerically indexed,
408
+
409
+			if (EEH_Array::is_associative_array($data)) {
410
+				throw new EE_Error(
411
+					sprintf(
412
+						__(
413
+							"top-level array must be numerically indexed. Does these look like numbers to you? %s",
414
+							"event_espresso"
415
+						),
416
+						implode(",", array_keys($data))
417
+					)
418
+				);
419
+			}
420
+			$item_in_top_level_array = EEH_Array::get_one_item_from_array($data);
421
+			// now, is the last item in the top-level array of $data an associative or numeric array?
422
+			if (EEH_Array::is_associative_array($item_in_top_level_array)) {
423
+				// its associative, so we want to output its keys as column headers
424
+				$keys = array_keys($item_in_top_level_array);
425
+				echo $this->fputcsv2($filehandle, $keys);
426
+			}
427
+			// start writing data
428
+			foreach ($data as $data_row) {
429
+				echo $this->fputcsv2($filehandle, $data_row);
430
+			}
431
+			return true;
432
+		} else {
433
+			// no data TO write... so we can assume that's a success
434
+			return true;
435
+		}
436
+		// //if 2nd level is indexed by strings, use those as csv column headers (ie, the first row)
437
+		//
438
+		//
439
+		// $no_table = TRUE;
440
+		//
441
+		// // loop through data and add each row to the file/stream as csv
442
+		// foreach ( $data as $model_name => $model_data ) {
443
+		// // test first row to see if it is data or a model name
444
+		// $model = EE_Registry::instance();->load_model($model_name);
445
+		// //if the model really exists,
446
+		// if ( $model ) {
447
+		//
448
+		// // we have a table name
449
+		// $no_table = FALSE;
450
+		//
451
+		// // put the tablename into an array cuz that's how fputcsv rolls
452
+		// $model_name_row = array( 'MODEL', $model_name );
453
+		//
454
+		// // add table name to csv output
455
+		// echo self::fputcsv2($filehandle, $model_name_row);
456
+		//
457
+		// // now get the rest of the data
458
+		// foreach ( $model_data as $row ) {
459
+		// // output the row
460
+		// echo self::fputcsv2($filehandle, $row);
461
+		// }
462
+		//
463
+		// }
464
+		//
465
+		// if ( $no_table ) {
466
+		// // no table so just put the data
467
+		// echo self::fputcsv2($filehandle, $model_data);
468
+		// }
469
+		//
470
+		// } // END OF foreach ( $data )
471
+	}
472
+
473
+	/**
474
+	 * Should be called after begin_sending_csv(), and one or more write_data_array_to_csv()s.
475
+	 * Calls exit to prevent polluting the CSV file with other junk
476
+	 *
477
+	 * @param resource $fh filehandle where we're writing the CSV to
478
+	 */
479
+	public function end_sending_csv($fh)
480
+	{
481
+		fclose($fh);
482
+		exit(0);
483
+	}
484
+
485
+	/**
486
+	 * Given an open file, writes all the model data to it in the format the importer expects.
487
+	 * Usually preceded by begin_sending_csv($filename), and followed by end_sending_csv($filehandle).
488
+	 *
489
+	 * @param resource $filehandle
490
+	 * @param array    $model_data_array is assumed to be a 3d array: 1st layer has keys of model names (eg 'Event'),
491
+	 *                                   next layer is numerically indexed to represent each model object (eg, each
492
+	 *                                   individual event), and the last layer has all the attributes o fthat model
493
+	 *                                   object (eg, the event's id, name, etc)
494
+	 * @return boolean success
495
+	 */
496
+	public function write_model_data_to_csv($filehandle, $model_data_array)
497
+	{
498
+		$this->write_metadata_to_csv($filehandle);
499
+		foreach ($model_data_array as $model_name => $model_instance_arrays) {
500
+			// first: output a special row stating the model
501
+			echo $this->fputcsv2($filehandle, array('MODEL', $model_name));
502
+			// if we have items to put in the CSV, do it normally
503
+
504
+			if (! empty($model_instance_arrays)) {
505
+				$this->write_data_array_to_csv($filehandle, $model_instance_arrays);
506
+			} else {
507
+				// echo "no data to write... so just write the headers";
508
+				// so there's actually NO model objects for that model.
509
+				// probably still want to show the columns
510
+				$model = EE_Registry::instance()->load_model($model_name);
511
+				$column_names = array();
512
+				foreach ($model->field_settings() as $field) {
513
+					$column_names[ $field->get_nicename() . "[" . $field->get_name() . "]" ] = null;
514
+				}
515
+				$this->write_data_array_to_csv($filehandle, array($column_names));
516
+			}
517
+		}
518
+	}
519
+
520
+	/**
521
+	 * Writes the CSV file to the output buffer, with rows corresponding to $model_data_array,
522
+	 * and dies (in order to avoid other plugins from messing up the csv output)
523
+	 *
524
+	 * @param string $filename         the filename you want to give the file
525
+	 * @param array  $model_data_array 3d array, as described in EE_CSV::write_model_data_to_csv()
526
+	 * @return bool | void writes CSV file to output and dies
527
+	 */
528
+	public function export_multiple_model_data_to_csv($filename, $model_data_array)
529
+	{
530
+		$filehandle = $this->begin_sending_csv($filename);
531
+		$this->write_model_data_to_csv($filehandle, $model_data_array);
532
+		$this->end_sending_csv($filehandle);
533
+	}
534
+
535
+	/**
536
+	 * @Export contents of an array to csv file
537
+	 * @access public
538
+	 * @param array  $data     - the array of data to be converted to csv and exported
539
+	 * @param string $filename - name for newly created csv file
540
+	 * @return TRUE on success, FALSE on fail
541
+	 */
542
+	public function export_array_to_csv($data = false, $filename = false)
543
+	{
544
+
545
+		// no data file?? get outta here
546
+		if (! $data or ! is_array($data) or empty($data)) {
547
+			return false;
548
+		}
549
+
550
+		// no filename?? get outta here
551
+		if (! $filename) {
552
+			return false;
553
+		}
554
+
555
+
556
+		// somebody told me i might need this ???
557
+		global $wpdb;
558
+		$prefix = $wpdb->prefix;
559
+
560
+
561
+		$fh = $this->begin_sending_csv($filename);
562
+
563
+
564
+		$this->end_sending_csv($fh);
565
+	}
566
+
567
+
568
+	/**
569
+	 * @Determine the maximum upload file size based on php.ini settings
570
+	 * @access    public
571
+	 * @param int $percent_of_max - desired percentage of the max upload_mb
572
+	 * @return int KB
573
+	 */
574
+	public function get_max_upload_size($percent_of_max = false)
575
+	{
576
+
577
+		$max_upload = (int) (ini_get('upload_max_filesize'));
578
+		$max_post = (int) (ini_get('post_max_size'));
579
+		$memory_limit = (int) (ini_get('memory_limit'));
580
+
581
+		// determine the smallest of the three values from above
582
+		$upload_mb = min($max_upload, $max_post, $memory_limit);
583
+
584
+		// convert MB to KB
585
+		$upload_mb = $upload_mb * 1024;
586
+
587
+		// don't want the full monty? then reduce the max uplaod size
588
+		if ($percent_of_max) {
589
+			// is percent_of_max like this -> 50 or like this -> 0.50 ?
590
+			if ($percent_of_max > 1) {
591
+				// chnages 50 to 0.50
592
+				$percent_of_max = $percent_of_max / 100;
593
+			}
594
+			// make upload_mb a percentage of the max upload_mb
595
+			$upload_mb = $upload_mb * $percent_of_max;
596
+		}
597
+
598
+		return $upload_mb;
599
+	}
600
+
601
+
602
+	/**
603
+	 * @Drop   in replacement for PHP's fputcsv function - but this one works!!!
604
+	 * @access private
605
+	 * @param resource $fh         - file handle - what we are writing to
606
+	 * @param array    $row        - individual row of csv data
607
+	 * @param string   $delimiter  - csv delimiter
608
+	 * @param string   $enclosure  - csv enclosure
609
+	 * @param string   $mysql_null - allows php NULL to be overridden with MySQl's insertable NULL value
610
+	 * @return void
611
+	 */
612
+	private function fputcsv2($fh, array $row, $delimiter = ',', $enclosure = '"', $mysql_null = false)
613
+	{
614
+		// Allow user to filter the csv delimiter and enclosure for other countries csv standards
615
+		$delimiter = apply_filters('FHEE__EE_CSV__fputcsv2__delimiter', $delimiter);
616
+		$enclosure = apply_filters('FHEE__EE_CSV__fputcsv2__enclosure', $enclosure);
617
+
618
+		$delimiter_esc = preg_quote($delimiter, '/');
619
+		$enclosure_esc = preg_quote($enclosure, '/');
620
+
621
+		$output = array();
622
+		foreach ($row as $field_value) {
623
+			if (is_object($field_value) || is_array($field_value)) {
624
+				$field_value = serialize($field_value);
625
+			}
626
+			if ($field_value === null && $mysql_null) {
627
+				$output[] = 'NULL';
628
+				continue;
629
+			}
630
+
631
+			$output[] = preg_match("/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field_value) ?
632
+				($enclosure . str_replace($enclosure, $enclosure . $enclosure, $field_value) . $enclosure)
633
+				: $field_value;
634
+		}
635
+
636
+		fwrite($fh, join($delimiter, $output) . PHP_EOL);
637
+	}
638
+
639
+
640
+	// /**
641
+	//  * @CSV    Import / Export messages
642
+	//  * @access public
643
+	//  * @return void
644
+	//  */
645
+	// public function csv_admin_notices()
646
+	// {
647
+	//
648
+	//     // We play both kinds of music here! Country AND Western! - err... I mean, cycle through both types of notices
649
+	//     foreach (array('updates', 'errors') as $type) {
650
+	//
651
+	//         // if particular notice type is not empty, then "You've got Mail"
652
+	//         if (! empty($this->_notices[ $type ])) {
653
+	//
654
+	//             // is it an update or an error ?
655
+	//             $msg_class = $type == 'updates' ? 'updated' : 'error';
656
+	//             echo '<div id="message" class="' . $msg_class . '">';
657
+	//             // display each notice, however many that may be
658
+	//             foreach ($this->_notices[ $type ] as $message) {
659
+	//                 echo '<p>' . $message . '</p>';
660
+	//             }
661
+	//             // wrap it up
662
+	//             echo '</div>';
663
+	//         }
664
+	//     }
665
+	// }
666
+
667
+	/**
668
+	 * Gets the date format to use in teh csv. filterable
669
+	 *
670
+	 * @param string $current_format
671
+	 * @return string
672
+	 */
673
+	public function get_date_format_for_csv($current_format = null)
674
+	{
675
+		return apply_filters('FHEE__EE_CSV__get_date_format_for_csv__format', 'Y-m-d', $current_format);
676
+	}
677
+
678
+	/**
679
+	 * Gets the time format we want to use in CSV reports. Filterable
680
+	 *
681
+	 * @param string $current_format
682
+	 * @return string
683
+	 */
684
+	public function get_time_format_for_csv($current_format = null)
685
+	{
686
+		return apply_filters('FHEE__EE_CSV__get_time_format_for_csv__format', 'H:i:s', $current_format);
687
+	}
688 688
 }
Please login to merge, or discard this patch.
Spacing   +48 added lines, -48 removed lines patch added patch discarded remove patch
@@ -48,31 +48,31 @@  discard block
 block discarded – undo
48 48
         global $wpdb;
49 49
 
50 50
         $this->_primary_keys = array(
51
-            $wpdb->prefix . 'esp_answer'                  => array('ANS_ID'),
52
-            $wpdb->prefix . 'esp_attendee'                => array('ATT_ID'),
53
-            $wpdb->prefix . 'esp_datetime'                => array('DTT_ID'),
54
-            $wpdb->prefix . 'esp_event_question_group'    => array('EQG_ID'),
55
-            $wpdb->prefix . 'esp_message_template'        => array('MTP_ID'),
56
-            $wpdb->prefix . 'esp_payment'                 => array('PAY_ID'),
57
-            $wpdb->prefix . 'esp_price'                   => array('PRC_ID'),
58
-            $wpdb->prefix . 'esp_price_type'              => array('PRT_ID'),
59
-            $wpdb->prefix . 'esp_question'                => array('QST_ID'),
60
-            $wpdb->prefix . 'esp_question_group'          => array('QSG_ID'),
61
-            $wpdb->prefix . 'esp_question_group_question' => array('QGQ_ID'),
62
-            $wpdb->prefix . 'esp_question_option'         => array('QSO_ID'),
63
-            $wpdb->prefix . 'esp_registration'            => array('REG_ID'),
64
-            $wpdb->prefix . 'esp_status'                  => array('STS_ID'),
65
-            $wpdb->prefix . 'esp_transaction'             => array('TXN_ID'),
66
-            $wpdb->prefix . 'esp_transaction'             => array('TXN_ID'),
67
-            $wpdb->prefix . 'events_detail'               => array('id'),
68
-            $wpdb->prefix . 'events_category_detail'      => array('id'),
69
-            $wpdb->prefix . 'events_category_rel'         => array('id'),
70
-            $wpdb->prefix . 'events_venue'                => array('id'),
71
-            $wpdb->prefix . 'events_venue_rel'            => array('emeta_id'),
72
-            $wpdb->prefix . 'events_locale'               => array('id'),
73
-            $wpdb->prefix . 'events_locale_rel'           => array('id'),
74
-            $wpdb->prefix . 'events_personnel'            => array('id'),
75
-            $wpdb->prefix . 'events_personnel_rel'        => array('id'),
51
+            $wpdb->prefix.'esp_answer'                  => array('ANS_ID'),
52
+            $wpdb->prefix.'esp_attendee'                => array('ATT_ID'),
53
+            $wpdb->prefix.'esp_datetime'                => array('DTT_ID'),
54
+            $wpdb->prefix.'esp_event_question_group'    => array('EQG_ID'),
55
+            $wpdb->prefix.'esp_message_template'        => array('MTP_ID'),
56
+            $wpdb->prefix.'esp_payment'                 => array('PAY_ID'),
57
+            $wpdb->prefix.'esp_price'                   => array('PRC_ID'),
58
+            $wpdb->prefix.'esp_price_type'              => array('PRT_ID'),
59
+            $wpdb->prefix.'esp_question'                => array('QST_ID'),
60
+            $wpdb->prefix.'esp_question_group'          => array('QSG_ID'),
61
+            $wpdb->prefix.'esp_question_group_question' => array('QGQ_ID'),
62
+            $wpdb->prefix.'esp_question_option'         => array('QSO_ID'),
63
+            $wpdb->prefix.'esp_registration'            => array('REG_ID'),
64
+            $wpdb->prefix.'esp_status'                  => array('STS_ID'),
65
+            $wpdb->prefix.'esp_transaction'             => array('TXN_ID'),
66
+            $wpdb->prefix.'esp_transaction'             => array('TXN_ID'),
67
+            $wpdb->prefix.'events_detail'               => array('id'),
68
+            $wpdb->prefix.'events_category_detail'      => array('id'),
69
+            $wpdb->prefix.'events_category_rel'         => array('id'),
70
+            $wpdb->prefix.'events_venue'                => array('id'),
71
+            $wpdb->prefix.'events_venue_rel'            => array('emeta_id'),
72
+            $wpdb->prefix.'events_locale'               => array('id'),
73
+            $wpdb->prefix.'events_locale_rel'           => array('id'),
74
+            $wpdb->prefix.'events_personnel'            => array('id'),
75
+            $wpdb->prefix.'events_personnel_rel'        => array('id'),
76 76
         );
77 77
     }
78 78
 
@@ -104,7 +104,7 @@  discard block
 block discarded – undo
104 104
     {
105 105
         $fc = "";
106 106
         $fh = fopen($file_path, "rb");
107
-        if (! $fh) {
107
+        if ( ! $fh) {
108 108
             throw new EE_Error(sprintf(__("Cannot open file for read: %s<br>\n", 'event_espresso'), $file_path));
109 109
         }
110 110
         $flen = filesize($file_path);
@@ -112,7 +112,7 @@  discard block
 block discarded – undo
112 112
         for ($i = 0; $i < $flen; $i++) {
113 113
             $c = substr($bc, $i, 1);
114 114
             if ((ord($c) != 0) && (ord($c) != 13)) {
115
-                $fc = $fc . $c;
115
+                $fc = $fc.$c;
116 116
             }
117 117
         }
118 118
         if ((ord(substr($fc, 0, 1)) == 255) && (ord(substr($fc, 1, 1)) == 254)) {
@@ -207,7 +207,7 @@  discard block
 block discarded – undo
207 207
     public function import_csv_to_model_data_array($path_to_file, $model_name = false, $first_row_is_headers = true)
208 208
     {
209 209
         $multi_dimensional_array = $this->import_csv_to_multi_dimensional_array($path_to_file);
210
-        if (! $multi_dimensional_array) {
210
+        if ( ! $multi_dimensional_array) {
211 211
             return false;
212 212
         }
213 213
         // gotta start somewhere
@@ -243,23 +243,23 @@  discard block
 block discarded – undo
243 243
             // loop through each column
244 244
             for ($i = 0; $i < $columns; $i++) {
245 245
                 // replace csv_enclosures with backslashed quotes
246
-                $data[ $i ] = str_replace('"""', '\\"', $data[ $i ]);
246
+                $data[$i] = str_replace('"""', '\\"', $data[$i]);
247 247
                 // do we need to grab the column names?
248 248
                 if ($row === 1) {
249 249
                     if ($first_row_is_headers) {
250 250
                         // store the column names to use for keys
251
-                        $column_name = $data[ $i ];
251
+                        $column_name = $data[$i];
252 252
                         // check it's not blank... sometimes CSV editign programs adda bunch of empty columns onto the end...
253
-                        if (! $column_name) {
253
+                        if ( ! $column_name) {
254 254
                             continue;
255 255
                         }
256 256
                         $matches = array();
257 257
                         if ($model_name == EE_CSV::metadata_header) {
258
-                            $headers[ $i ] = $column_name;
258
+                            $headers[$i] = $column_name;
259 259
                         } else {
260 260
                             // now get the db table name from it (the part between square brackets)
261 261
                             $success = preg_match('~(.*)\[(.*)\]~', $column_name, $matches);
262
-                            if (! $success) {
262
+                            if ( ! $success) {
263 263
                                 EE_Error::add_error(
264 264
                                     sprintf(
265 265
                                         __(
@@ -275,24 +275,24 @@  discard block
 block discarded – undo
275 275
                                 );
276 276
                                 return false;
277 277
                             }
278
-                            $headers[ $i ] = $matches[2];
278
+                            $headers[$i] = $matches[2];
279 279
                         }
280 280
                     } else {
281 281
                         // no column names means our final array will just use counters for keys
282
-                        $model_entry[ $headers[ $i ] ] = $data[ $i ];
283
-                        $headers[ $i ] = $i;
282
+                        $model_entry[$headers[$i]] = $data[$i];
283
+                        $headers[$i] = $i;
284 284
                     }
285 285
                     // and we need to store csv data
286 286
                 } else {
287 287
                     // this column isn' ta header, store it if there is a header for it
288
-                    if (isset($headers[ $i ])) {
289
-                        $model_entry[ $headers[ $i ] ] = $data[ $i ];
288
+                    if (isset($headers[$i])) {
289
+                        $model_entry[$headers[$i]] = $data[$i];
290 290
                     }
291 291
                 }
292 292
             }
293 293
             // save the row's data IF it's a non-header-row
294
-            if (! $first_row_is_headers || ($first_row_is_headers && $row > 1)) {
295
-                $ee_formatted_data[ $model_name ][] = $model_entry;
294
+            if ( ! $first_row_is_headers || ($first_row_is_headers && $row > 1)) {
295
+                $ee_formatted_data[$model_name][] = $model_entry;
296 296
             }
297 297
             // advance to next row
298 298
             $row++;
@@ -353,7 +353,7 @@  discard block
 block discarded – undo
353 353
         // header("Content-Type: application/force-download");
354 354
         // header("Content-Type: application/octet-stream");
355 355
         // header("Content-Type: application/download");
356
-        header('Content-disposition: attachment; filename=' . $filename);
356
+        header('Content-disposition: attachment; filename='.$filename);
357 357
         header("Content-Type: text/csv; charset=utf-8");
358 358
         do_action('AHEE__EE_CSV__begin_sending_csv__headers');
359 359
         echo apply_filters(
@@ -372,7 +372,7 @@  discard block
 block discarded – undo
372 372
      */
373 373
     public function write_metadata_to_csv($filehandle)
374 374
     {
375
-        $data_row = array(EE_CSV::metadata_header);// do NOT translate because this exact string is used when importing
375
+        $data_row = array(EE_CSV::metadata_header); // do NOT translate because this exact string is used when importing
376 376
         $this->fputcsv2($filehandle, $data_row);
377 377
         $meta_data = array(
378 378
             0 => array(
@@ -501,7 +501,7 @@  discard block
 block discarded – undo
501 501
             echo $this->fputcsv2($filehandle, array('MODEL', $model_name));
502 502
             // if we have items to put in the CSV, do it normally
503 503
 
504
-            if (! empty($model_instance_arrays)) {
504
+            if ( ! empty($model_instance_arrays)) {
505 505
                 $this->write_data_array_to_csv($filehandle, $model_instance_arrays);
506 506
             } else {
507 507
                 // echo "no data to write... so just write the headers";
@@ -510,7 +510,7 @@  discard block
 block discarded – undo
510 510
                 $model = EE_Registry::instance()->load_model($model_name);
511 511
                 $column_names = array();
512 512
                 foreach ($model->field_settings() as $field) {
513
-                    $column_names[ $field->get_nicename() . "[" . $field->get_name() . "]" ] = null;
513
+                    $column_names[$field->get_nicename()."[".$field->get_name()."]"] = null;
514 514
                 }
515 515
                 $this->write_data_array_to_csv($filehandle, array($column_names));
516 516
             }
@@ -543,12 +543,12 @@  discard block
 block discarded – undo
543 543
     {
544 544
 
545 545
         // no data file?? get outta here
546
-        if (! $data or ! is_array($data) or empty($data)) {
546
+        if ( ! $data or ! is_array($data) or empty($data)) {
547 547
             return false;
548 548
         }
549 549
 
550 550
         // no filename?? get outta here
551
-        if (! $filename) {
551
+        if ( ! $filename) {
552 552
             return false;
553 553
         }
554 554
 
@@ -629,11 +629,11 @@  discard block
 block discarded – undo
629 629
             }
630 630
 
631 631
             $output[] = preg_match("/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field_value) ?
632
-                ($enclosure . str_replace($enclosure, $enclosure . $enclosure, $field_value) . $enclosure)
632
+                ($enclosure.str_replace($enclosure, $enclosure.$enclosure, $field_value).$enclosure)
633 633
                 : $field_value;
634 634
         }
635 635
 
636
-        fwrite($fh, join($delimiter, $output) . PHP_EOL);
636
+        fwrite($fh, join($delimiter, $output).PHP_EOL);
637 637
     }
638 638
 
639 639
 
Please login to merge, or discard this patch.
core/db_classes/EE_Extra_Join.class.php 1 patch
Indentation   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -12,25 +12,25 @@
 block discarded – undo
12 12
 class EE_Extra_Join extends EE_Base_Class
13 13
 {
14 14
 
15
-    /**
16
-     * @param array $props_n_values
17
-     * @param null  $timezone
18
-     * @return EE_Extra_Join|mixed
19
-     */
20
-    public static function new_instance($props_n_values = array(), $timezone = null)
21
-    {
22
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone);
23
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone);
24
-    }
15
+	/**
16
+	 * @param array $props_n_values
17
+	 * @param null  $timezone
18
+	 * @return EE_Extra_Join|mixed
19
+	 */
20
+	public static function new_instance($props_n_values = array(), $timezone = null)
21
+	{
22
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone);
23
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone);
24
+	}
25 25
 
26 26
 
27
-    /**
28
-     * @param array $props_n_values
29
-     * @param null  $timezone
30
-     * @return EE_Extra_Join
31
-     */
32
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
33
-    {
34
-        return new self($props_n_values, true, $timezone);
35
-    }
27
+	/**
28
+	 * @param array $props_n_values
29
+	 * @param null  $timezone
30
+	 * @return EE_Extra_Join
31
+	 */
32
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
33
+	{
34
+		return new self($props_n_values, true, $timezone);
35
+	}
36 36
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Currency_Payment_Method.class.php 1 patch
Indentation   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -14,40 +14,40 @@
 block discarded – undo
14 14
 class EE_Currency_Payment_Method extends EE_Base_Class
15 15
 {
16 16
 
17
-    /** Currency to Payment Method Link ID @var CPM_ID */
18
-    protected $_CPM_ID = null;
19
-    /** Currency Code @var CUR_code */
20
-    protected $_CUR_code = null;
21
-    /** Payment Method ID @var PMD_ID */
22
-    protected $_PMD_ID = null;
23
-    protected $_Payment_Method;
24
-    protected $_Currency;
17
+	/** Currency to Payment Method Link ID @var CPM_ID */
18
+	protected $_CPM_ID = null;
19
+	/** Currency Code @var CUR_code */
20
+	protected $_CUR_code = null;
21
+	/** Payment Method ID @var PMD_ID */
22
+	protected $_PMD_ID = null;
23
+	protected $_Payment_Method;
24
+	protected $_Currency;
25 25
 
26 26
 
27
-    /**
28
-     *
29
-     * @param array  $props_n_values          incoming values
30
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
31
-     *                                        used.)
32
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
33
-     *                                        date_format and the second value is the time format
34
-     * @return EE_Attendee
35
-     */
36
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
37
-    {
38
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
39
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
40
-    }
27
+	/**
28
+	 *
29
+	 * @param array  $props_n_values          incoming values
30
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
31
+	 *                                        used.)
32
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
33
+	 *                                        date_format and the second value is the time format
34
+	 * @return EE_Attendee
35
+	 */
36
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
37
+	{
38
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
39
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
40
+	}
41 41
 
42 42
 
43
-    /**
44
-     * @param array  $props_n_values  incoming values from the database
45
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
46
-     *                                the website will be used.
47
-     * @return EE_Attendee
48
-     */
49
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
50
-    {
51
-        return new self($props_n_values, true, $timezone);
52
-    }
43
+	/**
44
+	 * @param array  $props_n_values  incoming values from the database
45
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
46
+	 *                                the website will be used.
47
+	 * @return EE_Attendee
48
+	 */
49
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
50
+	{
51
+		return new self($props_n_values, true, $timezone);
52
+	}
53 53
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Transaction.class.php 2 patches
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -42,7 +42,7 @@  discard block
 block discarded – undo
42 42
         $txn = $has_object
43 43
             ? $has_object
44 44
             : new self($props_n_values, false, $timezone, $date_formats);
45
-        if (! $has_object) {
45
+        if ( ! $has_object) {
46 46
             $txn->set_old_txn_status($txn->status_ID());
47 47
         }
48 48
         return $txn;
@@ -74,7 +74,7 @@  discard block
 block discarded – undo
74 74
     public function lock()
75 75
     {
76 76
         // attempt to set lock, but if that fails...
77
-        if (! $this->add_extra_meta('lock', time(), true)) {
77
+        if ( ! $this->add_extra_meta('lock', time(), true)) {
78 78
             // then attempt to remove the lock in case it is expired
79 79
             if ($this->_remove_expired_lock()) {
80 80
                 // if removal was successful, then try setting lock again
@@ -122,7 +122,7 @@  discard block
 block discarded – undo
122 122
     public function is_locked()
123 123
     {
124 124
         // if TXN is not locked, then return false immediately
125
-        if (! $this->_get_lock()) {
125
+        if ( ! $this->_get_lock()) {
126 126
             return false;
127 127
         }
128 128
         // if not, then let's try and remove the lock in case it's expired...
@@ -527,7 +527,7 @@  discard block
 block discarded – undo
527 527
                 $icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
528 528
                 break;
529 529
         }
530
-        return $icon . $status[ $this->status_ID() ];
530
+        return $icon.$status[$this->status_ID()];
531 531
     }
532 532
 
533 533
 
@@ -631,7 +631,7 @@  discard block
 block discarded – undo
631 631
     public function invoice_url($type = 'html')
632 632
     {
633 633
         $REG = $this->primary_registration();
634
-        if (! $REG instanceof EE_Registration) {
634
+        if ( ! $REG instanceof EE_Registration) {
635 635
             return '';
636 636
         }
637 637
         return $REG->invoice_url($type);
@@ -673,7 +673,7 @@  discard block
 block discarded – undo
673 673
     public function receipt_url($type = 'html')
674 674
     {
675 675
         $REG = $this->primary_registration();
676
-        if (! $REG instanceof EE_Registration) {
676
+        if ( ! $REG instanceof EE_Registration) {
677 677
             return '';
678 678
         }
679 679
         return $REG->receipt_url($type);
@@ -818,7 +818,7 @@  discard block
 block discarded – undo
818 818
     public function total_line_item($create_if_not_found = true)
819 819
     {
820 820
         $item = $this->get_first_related('Line_Item', array(array('LIN_type' => EEM_Line_Item::type_total)));
821
-        if (! $item && $create_if_not_found) {
821
+        if ( ! $item && $create_if_not_found) {
822 822
             $item = EEH_Line_Item::create_total_line_item($this);
823 823
         }
824 824
         return $item;
@@ -863,7 +863,7 @@  discard block
 block discarded – undo
863 863
     public function billing_info()
864 864
     {
865 865
         $payment_method = $this->payment_method();
866
-        if (! $payment_method) {
866
+        if ( ! $payment_method) {
867 867
             EE_Error::add_error(
868 868
                 __(
869 869
                     'Could not find billing info for transaction because no gateway has been used for it yet',
@@ -876,7 +876,7 @@  discard block
 block discarded – undo
876 876
             return null;
877 877
         }
878 878
         $primary_reg = $this->primary_registration();
879
-        if (! $primary_reg) {
879
+        if ( ! $primary_reg) {
880 880
             EE_Error::add_error(
881 881
                 __(
882 882
                     'Cannot get billing info for gateway %s on transaction because no primary registration exists',
@@ -889,7 +889,7 @@  discard block
 block discarded – undo
889 889
             return null;
890 890
         }
891 891
         $attendee = $primary_reg->attendee();
892
-        if (! $attendee) {
892
+        if ( ! $attendee) {
893 893
             EE_Error::add_error(
894 894
                 __(
895 895
                     'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
@@ -1025,7 +1025,7 @@  discard block
 block discarded – undo
1025 1025
     public function update_based_on_payments()
1026 1026
     {
1027 1027
         EE_Error::doing_it_wrong(
1028
-            __CLASS__ . '::' . __FUNCTION__,
1028
+            __CLASS__.'::'.__FUNCTION__,
1029 1029
             sprintf(
1030 1030
                 __('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1031 1031
                 'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
@@ -1085,13 +1085,13 @@  discard block
 block discarded – undo
1085 1085
     private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1086 1086
     {
1087 1087
         $reg_steps = $this->reg_steps();
1088
-        if (! is_array($reg_steps) || empty($reg_steps)) {
1088
+        if ( ! is_array($reg_steps) || empty($reg_steps)) {
1089 1089
             return false;
1090 1090
         }
1091 1091
         // loop thru reg steps array)
1092 1092
         foreach ($reg_steps as $slug => $reg_step_completed) {
1093 1093
             // if NOT checking ALL steps (only checking one step)
1094
-            if (! $check_all) {
1094
+            if ( ! $check_all) {
1095 1095
                 // and this is the one
1096 1096
                 if ($slug === $reg_step_slug) {
1097 1097
                     return $reg_step_completed;
@@ -1258,30 +1258,30 @@  discard block
 block discarded – undo
1258 1258
         // get reg steps array
1259 1259
         $txn_reg_steps = $this->reg_steps();
1260 1260
         // if reg step does NOT exist
1261
-        if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1261
+        if ( ! isset($txn_reg_steps[$reg_step_slug])) {
1262 1262
             return false;
1263 1263
         }
1264 1264
         // if  we're trying to complete a step that is already completed
1265
-        if ($txn_reg_steps[ $reg_step_slug ] === true) {
1265
+        if ($txn_reg_steps[$reg_step_slug] === true) {
1266 1266
             return true;
1267 1267
         }
1268 1268
         // if  we're trying to complete a step that hasn't even started
1269
-        if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1269
+        if ($status === true && $txn_reg_steps[$reg_step_slug] === false) {
1270 1270
             return false;
1271 1271
         }
1272 1272
         // if current status value matches the incoming value (no change)
1273 1273
         // type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1274
-        if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1274
+        if ((int) $txn_reg_steps[$reg_step_slug] === (int) $status) {
1275 1275
             // this will happen in cases where multiple AJAX requests occur during the same step
1276 1276
             return true;
1277 1277
         }
1278 1278
         // if we're trying to set a start time, but it has already been set...
1279
-        if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1279
+        if (is_numeric($status) && is_numeric($txn_reg_steps[$reg_step_slug])) {
1280 1280
             // skip the update below, but don't return FALSE so that errors won't be displayed
1281 1281
             return true;
1282 1282
         }
1283 1283
         // update completed status
1284
-        $txn_reg_steps[ $reg_step_slug ] = $status;
1284
+        $txn_reg_steps[$reg_step_slug] = $status;
1285 1285
         $this->set_reg_steps($txn_reg_steps);
1286 1286
         $this->save();
1287 1287
         return true;
@@ -1301,7 +1301,7 @@  discard block
 block discarded – undo
1301 1301
     {
1302 1302
         // get reg steps array
1303 1303
         $txn_reg_steps = $this->reg_steps();
1304
-        unset($txn_reg_steps[ $reg_step_slug ]);
1304
+        unset($txn_reg_steps[$reg_step_slug]);
1305 1305
         $this->set_reg_steps($txn_reg_steps);
1306 1306
     }
1307 1307
 
@@ -1374,12 +1374,12 @@  discard block
 block discarded – undo
1374 1374
             return;
1375 1375
         }
1376 1376
         $payments = $this->get_many_related('Payment');
1377
-        if (! empty($payments)) {
1377
+        if ( ! empty($payments)) {
1378 1378
             foreach ($payments as $payment) {
1379 1379
                 if ($payment instanceof EE_Payment) {
1380 1380
                     // kk this TXN should NOT be abandoned
1381 1381
                     $this->update_status_based_on_total_paid();
1382
-                    if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1382
+                    if ( ! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1383 1383
                         EE_Error::add_attention(
1384 1384
                             sprintf(
1385 1385
                                 esc_html__(
Please login to merge, or discard this patch.
Indentation   +1698 added lines, -1698 removed lines patch added patch discarded remove patch
@@ -13,1702 +13,1702 @@
 block discarded – undo
13 13
 class EE_Transaction extends EE_Base_Class implements EEI_Transaction
14 14
 {
15 15
 
16
-    /**
17
-     * The length of time in seconds that a lock is applied before being considered expired.
18
-     * It is not long because a transaction should only be locked for the duration of the request that locked it
19
-     */
20
-    const LOCK_EXPIRATION = 2;
21
-
22
-    /**
23
-     * txn status upon initial construction.
24
-     *
25
-     * @var string
26
-     */
27
-    protected $_old_txn_status;
28
-
29
-
30
-    /**
31
-     * @param array  $props_n_values          incoming values
32
-     * @param string $timezone                incoming timezone
33
-     *                                        (if not set the timezone set for the website will be used.)
34
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
35
-     *                                        date_format and the second value is the time format
36
-     * @return EE_Transaction
37
-     * @throws EE_Error
38
-     * @throws InvalidArgumentException
39
-     * @throws InvalidDataTypeException
40
-     * @throws InvalidInterfaceException
41
-     * @throws ReflectionException
42
-     */
43
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
44
-    {
45
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
46
-        $txn = $has_object
47
-            ? $has_object
48
-            : new self($props_n_values, false, $timezone, $date_formats);
49
-        if (! $has_object) {
50
-            $txn->set_old_txn_status($txn->status_ID());
51
-        }
52
-        return $txn;
53
-    }
54
-
55
-
56
-    /**
57
-     * @param array  $props_n_values  incoming values from the database
58
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
59
-     *                                the website will be used.
60
-     * @return EE_Transaction
61
-     * @throws EE_Error
62
-     * @throws InvalidArgumentException
63
-     * @throws InvalidDataTypeException
64
-     * @throws InvalidInterfaceException
65
-     * @throws ReflectionException
66
-     */
67
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
68
-    {
69
-        $txn = new self($props_n_values, true, $timezone);
70
-        $txn->set_old_txn_status($txn->status_ID());
71
-        return $txn;
72
-    }
73
-
74
-
75
-    /**
76
-     * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
77
-     * If a lock has already been set, then we will attempt to remove it in case it has expired.
78
-     * If that also fails, then an exception is thrown.
79
-     *
80
-     * @throws EE_Error
81
-     * @throws InvalidArgumentException
82
-     * @throws InvalidDataTypeException
83
-     * @throws InvalidInterfaceException
84
-     * @throws ReflectionException
85
-     */
86
-    public function lock()
87
-    {
88
-        // attempt to set lock, but if that fails...
89
-        if (! $this->add_extra_meta('lock', time(), true)) {
90
-            // then attempt to remove the lock in case it is expired
91
-            if ($this->_remove_expired_lock()) {
92
-                // if removal was successful, then try setting lock again
93
-                $this->lock();
94
-            } else {
95
-                // but if the lock can not be removed, then throw an exception
96
-                throw new EE_Error(
97
-                    sprintf(
98
-                        __(
99
-                            'Could not lock Transaction %1$d because it is already locked, meaning another part of the system is currently editing it. It should already be unlocked by the time you read this, so please refresh the page and try again.',
100
-                            'event_espresso'
101
-                        ),
102
-                        $this->ID()
103
-                    )
104
-                );
105
-            }
106
-        }
107
-    }
108
-
109
-
110
-    /**
111
-     * removes transaction lock applied in EE_Transaction::lock()
112
-     *
113
-     * @return int
114
-     * @throws EE_Error
115
-     * @throws InvalidArgumentException
116
-     * @throws InvalidDataTypeException
117
-     * @throws InvalidInterfaceException
118
-     * @throws ReflectionException
119
-     */
120
-    public function unlock()
121
-    {
122
-        return $this->delete_extra_meta('lock');
123
-    }
124
-
125
-
126
-    /**
127
-     * Decides whether or not now is the right time to update the transaction.
128
-     * This is useful because we don't always know if it is safe to update the transaction
129
-     * and its related data. why?
130
-     * because it's possible that the transaction is being used in another
131
-     * request and could overwrite anything we save.
132
-     * So we want to only update the txn once we know that won't happen.
133
-     * We also check that the lock isn't expired, and remove it if it is
134
-     *
135
-     * @return boolean
136
-     * @throws EE_Error
137
-     * @throws InvalidArgumentException
138
-     * @throws InvalidDataTypeException
139
-     * @throws InvalidInterfaceException
140
-     * @throws ReflectionException
141
-     */
142
-    public function is_locked()
143
-    {
144
-        // if TXN is not locked, then return false immediately
145
-        if (! $this->_get_lock()) {
146
-            return false;
147
-        }
148
-        // if not, then let's try and remove the lock in case it's expired...
149
-        // _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
150
-        // and a positive number if the lock was removed (ie: number of locks deleted),
151
-        // so we need to return the opposite
152
-        return ! $this->_remove_expired_lock() ? true : false;
153
-    }
154
-
155
-
156
-    /**
157
-     * Gets the meta field indicating that this TXN is locked
158
-     *
159
-     * @return int
160
-     * @throws EE_Error
161
-     * @throws InvalidArgumentException
162
-     * @throws InvalidDataTypeException
163
-     * @throws InvalidInterfaceException
164
-     * @throws ReflectionException
165
-     */
166
-    protected function _get_lock()
167
-    {
168
-        return (int) $this->get_extra_meta('lock', true, 0);
169
-    }
170
-
171
-
172
-    /**
173
-     * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
174
-     *
175
-     * @return int
176
-     * @throws EE_Error
177
-     * @throws InvalidArgumentException
178
-     * @throws InvalidDataTypeException
179
-     * @throws InvalidInterfaceException
180
-     * @throws ReflectionException
181
-     */
182
-    protected function _remove_expired_lock()
183
-    {
184
-        $locked = $this->_get_lock();
185
-        if ($locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked) {
186
-            return $this->unlock();
187
-        }
188
-        return 0;
189
-    }
190
-
191
-
192
-    /**
193
-     * Set transaction total
194
-     *
195
-     * @param float $total total value of transaction
196
-     * @throws EE_Error
197
-     * @throws InvalidArgumentException
198
-     * @throws InvalidDataTypeException
199
-     * @throws InvalidInterfaceException
200
-     * @throws ReflectionException
201
-     */
202
-    public function set_total($total = 0.00)
203
-    {
204
-        $this->set('TXN_total', (float) $total);
205
-    }
206
-
207
-
208
-    /**
209
-     * Set Total Amount Paid to Date
210
-     *
211
-     * @param float $total_paid total amount paid to date (sum of all payments)
212
-     * @throws EE_Error
213
-     * @throws InvalidArgumentException
214
-     * @throws InvalidDataTypeException
215
-     * @throws InvalidInterfaceException
216
-     * @throws ReflectionException
217
-     */
218
-    public function set_paid($total_paid = 0.00)
219
-    {
220
-        $this->set('TXN_paid', (float) $total_paid);
221
-    }
222
-
223
-
224
-    /**
225
-     * Set transaction status
226
-     *
227
-     * @param string $status        whether the transaction is open, declined, accepted,
228
-     *                              or any number of custom values that can be set
229
-     * @throws EE_Error
230
-     * @throws InvalidArgumentException
231
-     * @throws InvalidDataTypeException
232
-     * @throws InvalidInterfaceException
233
-     * @throws ReflectionException
234
-     */
235
-    public function set_status($status = '')
236
-    {
237
-        $this->set('STS_ID', $status);
238
-    }
239
-
240
-
241
-    /**
242
-     * Set hash salt
243
-     *
244
-     * @param string $hash_salt required for some payment gateways
245
-     * @throws EE_Error
246
-     * @throws InvalidArgumentException
247
-     * @throws InvalidDataTypeException
248
-     * @throws InvalidInterfaceException
249
-     * @throws ReflectionException
250
-     */
251
-    public function set_hash_salt($hash_salt = '')
252
-    {
253
-        $this->set('TXN_hash_salt', $hash_salt);
254
-    }
255
-
256
-
257
-    /**
258
-     * Sets TXN_reg_steps array
259
-     *
260
-     * @param array $txn_reg_steps
261
-     * @throws EE_Error
262
-     * @throws InvalidArgumentException
263
-     * @throws InvalidDataTypeException
264
-     * @throws InvalidInterfaceException
265
-     * @throws ReflectionException
266
-     */
267
-    public function set_reg_steps(array $txn_reg_steps)
268
-    {
269
-        $this->set('TXN_reg_steps', $txn_reg_steps);
270
-    }
271
-
272
-
273
-    /**
274
-     * Gets TXN_reg_steps
275
-     *
276
-     * @return array
277
-     * @throws EE_Error
278
-     * @throws InvalidArgumentException
279
-     * @throws InvalidDataTypeException
280
-     * @throws InvalidInterfaceException
281
-     * @throws ReflectionException
282
-     */
283
-    public function reg_steps()
284
-    {
285
-        $TXN_reg_steps = $this->get('TXN_reg_steps');
286
-        return is_array($TXN_reg_steps) ? (array) $TXN_reg_steps : array();
287
-    }
288
-
289
-
290
-    /**
291
-     * @return string of transaction's total cost, with currency symbol and decimal
292
-     * @throws EE_Error
293
-     * @throws InvalidArgumentException
294
-     * @throws InvalidDataTypeException
295
-     * @throws InvalidInterfaceException
296
-     * @throws ReflectionException
297
-     */
298
-    public function pretty_total()
299
-    {
300
-        return $this->get_pretty('TXN_total');
301
-    }
302
-
303
-
304
-    /**
305
-     * Gets the amount paid in a pretty string (formatted and with currency symbol)
306
-     *
307
-     * @return string
308
-     * @throws EE_Error
309
-     * @throws InvalidArgumentException
310
-     * @throws InvalidDataTypeException
311
-     * @throws InvalidInterfaceException
312
-     * @throws ReflectionException
313
-     */
314
-    public function pretty_paid()
315
-    {
316
-        return $this->get_pretty('TXN_paid');
317
-    }
318
-
319
-
320
-    /**
321
-     * calculate the amount remaining for this transaction and return;
322
-     *
323
-     * @return float amount remaining
324
-     * @throws EE_Error
325
-     * @throws InvalidArgumentException
326
-     * @throws InvalidDataTypeException
327
-     * @throws InvalidInterfaceException
328
-     * @throws ReflectionException
329
-     */
330
-    public function remaining()
331
-    {
332
-        return $this->total() - $this->paid();
333
-    }
334
-
335
-
336
-    /**
337
-     * get Transaction Total
338
-     *
339
-     * @return float
340
-     * @throws EE_Error
341
-     * @throws InvalidArgumentException
342
-     * @throws InvalidDataTypeException
343
-     * @throws InvalidInterfaceException
344
-     * @throws ReflectionException
345
-     */
346
-    public function total()
347
-    {
348
-        return (float) $this->get('TXN_total');
349
-    }
350
-
351
-
352
-    /**
353
-     * get Total Amount Paid to Date
354
-     *
355
-     * @return float
356
-     * @throws EE_Error
357
-     * @throws InvalidArgumentException
358
-     * @throws InvalidDataTypeException
359
-     * @throws InvalidInterfaceException
360
-     * @throws ReflectionException
361
-     */
362
-    public function paid()
363
-    {
364
-        return (float) $this->get('TXN_paid');
365
-    }
366
-
367
-
368
-    /**
369
-     * @return mixed|null
370
-     * @throws EE_Error
371
-     * @throws InvalidArgumentException
372
-     * @throws InvalidDataTypeException
373
-     * @throws InvalidInterfaceException
374
-     * @throws ReflectionException
375
-     */
376
-    public function get_cart_session()
377
-    {
378
-        $session_data = (array) $this->get('TXN_session_data');
379
-        return isset($session_data['cart']) && $session_data['cart'] instanceof EE_Cart
380
-            ? $session_data['cart']
381
-            : null;
382
-    }
383
-
384
-
385
-    /**
386
-     * get Transaction session data
387
-     *
388
-     * @return array|mixed
389
-     * @throws EE_Error
390
-     * @throws InvalidArgumentException
391
-     * @throws InvalidDataTypeException
392
-     * @throws InvalidInterfaceException
393
-     * @throws ReflectionException
394
-     */
395
-    public function session_data()
396
-    {
397
-        $session_data = $this->get('TXN_session_data');
398
-        if (empty($session_data)) {
399
-            $session_data = array(
400
-                'id'            => null,
401
-                'user_id'       => null,
402
-                'ip_address'    => null,
403
-                'user_agent'    => null,
404
-                'init_access'   => null,
405
-                'last_access'   => null,
406
-                'pages_visited' => array(),
407
-            );
408
-        }
409
-        return $session_data;
410
-    }
411
-
412
-
413
-    /**
414
-     * Set session data within the TXN object
415
-     *
416
-     * @param EE_Session|array $session_data
417
-     * @throws EE_Error
418
-     * @throws InvalidArgumentException
419
-     * @throws InvalidDataTypeException
420
-     * @throws InvalidInterfaceException
421
-     * @throws ReflectionException
422
-     */
423
-    public function set_txn_session_data($session_data)
424
-    {
425
-        if ($session_data instanceof EE_Session) {
426
-            $this->set('TXN_session_data', $session_data->get_session_data(null, true));
427
-        } else {
428
-            $this->set('TXN_session_data', $session_data);
429
-        }
430
-    }
431
-
432
-
433
-    /**
434
-     * get Transaction hash salt
435
-     *
436
-     * @return mixed
437
-     * @throws EE_Error
438
-     * @throws InvalidArgumentException
439
-     * @throws InvalidDataTypeException
440
-     * @throws InvalidInterfaceException
441
-     * @throws ReflectionException
442
-     */
443
-    public function hash_salt_()
444
-    {
445
-        return $this->get('TXN_hash_salt');
446
-    }
447
-
448
-
449
-    /**
450
-     * Returns the transaction datetime as either:
451
-     *            - unix timestamp format ($format = false, $gmt = true)
452
-     *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
453
-     *              has no affect with this option)), this also may include a timezone abbreviation if the
454
-     *              set timezone in this class differs from what the timezone is on the blog.
455
-     *            - formatted date string including the UTC (timezone) offset (default).
456
-     *
457
-     * @param boolean $format   - whether to return a unix timestamp (default) or formatted date string
458
-     * @param boolean $gmt      - whether to return a unix timestamp with UTC offset applied (default)
459
-     *                          or no UTC offset applied
460
-     * @return string | int
461
-     * @throws EE_Error
462
-     * @throws InvalidArgumentException
463
-     * @throws InvalidDataTypeException
464
-     * @throws InvalidInterfaceException
465
-     * @throws ReflectionException
466
-     */
467
-    public function datetime($format = false, $gmt = false)
468
-    {
469
-        if ($format) {
470
-            return $this->get_pretty('TXN_timestamp');
471
-        }
472
-        if ($gmt) {
473
-            return $this->get_raw('TXN_timestamp');
474
-        }
475
-        return $this->get('TXN_timestamp');
476
-    }
477
-
478
-
479
-    /**
480
-     * Gets registrations on this transaction
481
-     *
482
-     * @param array   $query_params array of query parameters
483
-     * @param boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
484
-     * @return EE_Base_Class[]|EE_Registration[]
485
-     * @throws EE_Error
486
-     * @throws InvalidArgumentException
487
-     * @throws InvalidDataTypeException
488
-     * @throws InvalidInterfaceException
489
-     * @throws ReflectionException
490
-     */
491
-    public function registrations($query_params = array(), $get_cached = false)
492
-    {
493
-        $query_params = (empty($query_params) || ! is_array($query_params))
494
-            ? array(
495
-                'order_by' => array(
496
-                    'Event.EVT_name'     => 'ASC',
497
-                    'Attendee.ATT_lname' => 'ASC',
498
-                    'Attendee.ATT_fname' => 'ASC',
499
-                ),
500
-            )
501
-            : $query_params;
502
-        $query_params = $get_cached ? array() : $query_params;
503
-        return $this->get_many_related('Registration', $query_params);
504
-    }
505
-
506
-
507
-    /**
508
-     * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event
509
-     * function for getting attendees and how many registrations they each have for an event)
510
-     *
511
-     * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
512
-     * @throws EE_Error
513
-     * @throws InvalidArgumentException
514
-     * @throws InvalidDataTypeException
515
-     * @throws InvalidInterfaceException
516
-     * @throws ReflectionException
517
-     */
518
-    public function attendees()
519
-    {
520
-        return $this->get_many_related('Attendee', array(array('Registration.Transaction.TXN_ID' => $this->ID())));
521
-    }
522
-
523
-
524
-    /**
525
-     * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
526
-     *
527
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
528
-     * @return EE_Base_Class[]|EE_Payment[]
529
-     * @throws EE_Error
530
-     * @throws InvalidArgumentException
531
-     * @throws InvalidDataTypeException
532
-     * @throws InvalidInterfaceException
533
-     * @throws ReflectionException
534
-     */
535
-    public function payments($query_params = array())
536
-    {
537
-        return $this->get_many_related('Payment', $query_params);
538
-    }
539
-
540
-
541
-    /**
542
-     * gets only approved payments for this transaction
543
-     *
544
-     * @return EE_Base_Class[]|EE_Payment[]
545
-     * @throws EE_Error
546
-     * @throws InvalidArgumentException
547
-     * @throws ReflectionException
548
-     * @throws InvalidDataTypeException
549
-     * @throws InvalidInterfaceException
550
-     */
551
-    public function approved_payments()
552
-    {
553
-        EE_Registry::instance()->load_model('Payment');
554
-        return $this->get_many_related(
555
-            'Payment',
556
-            array(
557
-                array('STS_ID' => EEM_Payment::status_id_approved),
558
-                'order_by' => array('PAY_timestamp' => 'DESC'),
559
-            )
560
-        );
561
-    }
562
-
563
-
564
-    /**
565
-     * Gets all payments which have not been approved
566
-     *
567
-     * @return EE_Base_Class[]|EEI_Payment[]
568
-     * @throws EE_Error if a model is misconfigured somehow
569
-     * @throws InvalidArgumentException
570
-     * @throws InvalidDataTypeException
571
-     * @throws InvalidInterfaceException
572
-     * @throws ReflectionException
573
-     */
574
-    public function pending_payments()
575
-    {
576
-        return $this->get_many_related(
577
-            'Payment',
578
-            array(
579
-                array(
580
-                    'STS_ID' => EEM_Payment::status_id_pending,
581
-                ),
582
-                'order_by' => array(
583
-                    'PAY_timestamp' => 'DESC',
584
-                ),
585
-            )
586
-        );
587
-    }
588
-
589
-
590
-    /**
591
-     * echoes $this->pretty_status()
592
-     *
593
-     * @param bool $show_icons
594
-     * @throws EE_Error
595
-     * @throws InvalidArgumentException
596
-     * @throws InvalidDataTypeException
597
-     * @throws InvalidInterfaceException
598
-     * @throws ReflectionException
599
-     */
600
-    public function e_pretty_status($show_icons = false)
601
-    {
602
-        echo $this->pretty_status($show_icons);
603
-    }
604
-
605
-
606
-    /**
607
-     * returns a pretty version of the status, good for displaying to users
608
-     *
609
-     * @param bool $show_icons
610
-     * @return string
611
-     * @throws EE_Error
612
-     * @throws InvalidArgumentException
613
-     * @throws InvalidDataTypeException
614
-     * @throws InvalidInterfaceException
615
-     * @throws ReflectionException
616
-     */
617
-    public function pretty_status($show_icons = false)
618
-    {
619
-        $status = EEM_Status::instance()->localized_status(
620
-            array($this->status_ID() => __('unknown', 'event_espresso')),
621
-            false,
622
-            'sentence'
623
-        );
624
-        $icon = '';
625
-        switch ($this->status_ID()) {
626
-            case EEM_Transaction::complete_status_code:
627
-                $icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
628
-                break;
629
-            case EEM_Transaction::incomplete_status_code:
630
-                $icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>'
631
-                    : '';
632
-                break;
633
-            case EEM_Transaction::abandoned_status_code:
634
-                $icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
635
-                break;
636
-            case EEM_Transaction::failed_status_code:
637
-                $icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
638
-                break;
639
-            case EEM_Transaction::overpaid_status_code:
640
-                $icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
641
-                break;
642
-        }
643
-        return $icon . $status[ $this->status_ID() ];
644
-    }
645
-
646
-
647
-    /**
648
-     * get Transaction Status
649
-     *
650
-     * @return mixed
651
-     * @throws EE_Error
652
-     * @throws InvalidArgumentException
653
-     * @throws InvalidDataTypeException
654
-     * @throws InvalidInterfaceException
655
-     * @throws ReflectionException
656
-     */
657
-    public function status_ID()
658
-    {
659
-        return $this->get('STS_ID');
660
-    }
661
-
662
-
663
-    /**
664
-     * Returns TRUE or FALSE for whether or not this transaction cost any money
665
-     *
666
-     * @return boolean
667
-     * @throws EE_Error
668
-     * @throws InvalidArgumentException
669
-     * @throws InvalidDataTypeException
670
-     * @throws InvalidInterfaceException
671
-     * @throws ReflectionException
672
-     */
673
-    public function is_free()
674
-    {
675
-        return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
676
-    }
677
-
678
-
679
-    /**
680
-     * Returns whether this transaction is complete
681
-     * Useful in templates and other logic for deciding if we should ask for another payment...
682
-     *
683
-     * @return boolean
684
-     * @throws EE_Error
685
-     * @throws InvalidArgumentException
686
-     * @throws InvalidDataTypeException
687
-     * @throws InvalidInterfaceException
688
-     * @throws ReflectionException
689
-     */
690
-    public function is_completed()
691
-    {
692
-        return $this->status_ID() === EEM_Transaction::complete_status_code;
693
-    }
694
-
695
-
696
-    /**
697
-     * Returns whether this transaction is incomplete
698
-     * Useful in templates and other logic for deciding if we should ask for another payment...
699
-     *
700
-     * @return boolean
701
-     * @throws EE_Error
702
-     * @throws InvalidArgumentException
703
-     * @throws InvalidDataTypeException
704
-     * @throws InvalidInterfaceException
705
-     * @throws ReflectionException
706
-     */
707
-    public function is_incomplete()
708
-    {
709
-        return $this->status_ID() === EEM_Transaction::incomplete_status_code;
710
-    }
711
-
712
-
713
-    /**
714
-     * Returns whether this transaction is overpaid
715
-     * Useful in templates and other logic for deciding if monies need to be refunded
716
-     *
717
-     * @return boolean
718
-     * @throws EE_Error
719
-     * @throws InvalidArgumentException
720
-     * @throws InvalidDataTypeException
721
-     * @throws InvalidInterfaceException
722
-     * @throws ReflectionException
723
-     */
724
-    public function is_overpaid()
725
-    {
726
-        return $this->status_ID() === EEM_Transaction::overpaid_status_code;
727
-    }
728
-
729
-
730
-    /**
731
-     * Returns whether this transaction was abandoned
732
-     * meaning that the transaction/registration process was somehow interrupted and never completed
733
-     * but that contact information exists for at least one registrant
734
-     *
735
-     * @return boolean
736
-     * @throws EE_Error
737
-     * @throws InvalidArgumentException
738
-     * @throws InvalidDataTypeException
739
-     * @throws InvalidInterfaceException
740
-     * @throws ReflectionException
741
-     */
742
-    public function is_abandoned()
743
-    {
744
-        return $this->status_ID() === EEM_Transaction::abandoned_status_code;
745
-    }
746
-
747
-
748
-    /**
749
-     * Returns whether this transaction failed
750
-     * meaning that the transaction/registration process was somehow interrupted and never completed
751
-     * and that NO contact information exists for any registrants
752
-     *
753
-     * @return boolean
754
-     * @throws EE_Error
755
-     * @throws InvalidArgumentException
756
-     * @throws InvalidDataTypeException
757
-     * @throws InvalidInterfaceException
758
-     * @throws ReflectionException
759
-     */
760
-    public function failed()
761
-    {
762
-        return $this->status_ID() === EEM_Transaction::failed_status_code;
763
-    }
764
-
765
-
766
-    /**
767
-     * This returns the url for the invoice of this transaction
768
-     *
769
-     * @param string $type 'html' or 'pdf' (default is pdf)
770
-     * @return string
771
-     * @throws EE_Error
772
-     * @throws InvalidArgumentException
773
-     * @throws InvalidDataTypeException
774
-     * @throws InvalidInterfaceException
775
-     * @throws ReflectionException
776
-     */
777
-    public function invoice_url($type = 'html')
778
-    {
779
-        $REG = $this->primary_registration();
780
-        if (! $REG instanceof EE_Registration) {
781
-            return '';
782
-        }
783
-        return $REG->invoice_url($type);
784
-    }
785
-
786
-
787
-    /**
788
-     * Gets the primary registration only
789
-     *
790
-     * @return EE_Base_Class|EE_Registration
791
-     * @throws EE_Error
792
-     * @throws InvalidArgumentException
793
-     * @throws InvalidDataTypeException
794
-     * @throws InvalidInterfaceException
795
-     * @throws ReflectionException
796
-     */
797
-    public function primary_registration()
798
-    {
799
-        $registrations = (array) $this->get_many_related(
800
-            'Registration',
801
-            array(array('REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT))
802
-        );
803
-        foreach ($registrations as $registration) {
804
-            // valid registration that is NOT cancelled or declined ?
805
-            if ($registration instanceof EE_Registration
806
-                && ! in_array($registration->status_ID(), EEM_Registration::closed_reg_statuses(), true)
807
-            ) {
808
-                return $registration;
809
-            }
810
-        }
811
-        // nothing valid found, so just return first thing from array of results
812
-        return reset($registrations);
813
-    }
814
-
815
-
816
-    /**
817
-     * Gets the URL for viewing the receipt
818
-     *
819
-     * @param string $type 'pdf' or 'html' (default is 'html')
820
-     * @return string
821
-     * @throws EE_Error
822
-     * @throws InvalidArgumentException
823
-     * @throws InvalidDataTypeException
824
-     * @throws InvalidInterfaceException
825
-     * @throws ReflectionException
826
-     */
827
-    public function receipt_url($type = 'html')
828
-    {
829
-        $REG = $this->primary_registration();
830
-        if (! $REG instanceof EE_Registration) {
831
-            return '';
832
-        }
833
-        return $REG->receipt_url($type);
834
-    }
835
-
836
-
837
-    /**
838
-     * Gets the URL of the thank you page with this registration REG_url_link added as
839
-     * a query parameter
840
-     *
841
-     * @return string
842
-     * @throws EE_Error
843
-     * @throws InvalidArgumentException
844
-     * @throws InvalidDataTypeException
845
-     * @throws InvalidInterfaceException
846
-     * @throws ReflectionException
847
-     */
848
-    public function payment_overview_url()
849
-    {
850
-        $primary_registration = $this->primary_registration();
851
-        return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : false;
852
-    }
853
-
854
-
855
-    /**
856
-     * @return string
857
-     * @throws EE_Error
858
-     * @throws InvalidArgumentException
859
-     * @throws InvalidDataTypeException
860
-     * @throws InvalidInterfaceException
861
-     * @throws ReflectionException
862
-     */
863
-    public function gateway_response_on_transaction()
864
-    {
865
-        $payment = $this->get_first_related('Payment');
866
-        return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
867
-    }
868
-
869
-
870
-    /**
871
-     * Get the status object of this object
872
-     *
873
-     * @return EE_Base_Class|EE_Status
874
-     * @throws EE_Error
875
-     * @throws InvalidArgumentException
876
-     * @throws InvalidDataTypeException
877
-     * @throws InvalidInterfaceException
878
-     * @throws ReflectionException
879
-     */
880
-    public function status_obj()
881
-    {
882
-        return $this->get_first_related('Status');
883
-    }
884
-
885
-
886
-    /**
887
-     * Gets all the extra meta info on this payment
888
-     *
889
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
890
-     * @return EE_Base_Class[]|EE_Extra_Meta
891
-     * @throws EE_Error
892
-     * @throws InvalidArgumentException
893
-     * @throws InvalidDataTypeException
894
-     * @throws InvalidInterfaceException
895
-     * @throws ReflectionException
896
-     */
897
-    public function extra_meta($query_params = array())
898
-    {
899
-        return $this->get_many_related('Extra_Meta', $query_params);
900
-    }
901
-
902
-
903
-    /**
904
-     * Wrapper for _add_relation_to
905
-     *
906
-     * @param EE_Registration $registration
907
-     * @return EE_Base_Class the relation was added to
908
-     * @throws EE_Error
909
-     * @throws InvalidArgumentException
910
-     * @throws InvalidDataTypeException
911
-     * @throws InvalidInterfaceException
912
-     * @throws ReflectionException
913
-     */
914
-    public function add_registration(EE_Registration $registration)
915
-    {
916
-        return $this->_add_relation_to($registration, 'Registration');
917
-    }
918
-
919
-
920
-    /**
921
-     * Removes the given registration from being related (even before saving this transaction).
922
-     * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
923
-     *
924
-     * @param int $registration_or_id
925
-     * @return EE_Base_Class that was removed from being related
926
-     * @throws EE_Error
927
-     * @throws InvalidArgumentException
928
-     * @throws InvalidDataTypeException
929
-     * @throws InvalidInterfaceException
930
-     * @throws ReflectionException
931
-     */
932
-    public function remove_registration_with_id($registration_or_id)
933
-    {
934
-        return $this->_remove_relation_to($registration_or_id, 'Registration');
935
-    }
936
-
937
-
938
-    /**
939
-     * Gets all the line items which are for ACTUAL items
940
-     *
941
-     * @return EE_Line_Item[]
942
-     * @throws EE_Error
943
-     * @throws InvalidArgumentException
944
-     * @throws InvalidDataTypeException
945
-     * @throws InvalidInterfaceException
946
-     * @throws ReflectionException
947
-     */
948
-    public function items_purchased()
949
-    {
950
-        return $this->line_items(array(array('LIN_type' => EEM_Line_Item::type_line_item)));
951
-    }
952
-
953
-
954
-    /**
955
-     * Wrapper for _add_relation_to
956
-     *
957
-     * @param EE_Line_Item $line_item
958
-     * @return EE_Base_Class the relation was added to
959
-     * @throws EE_Error
960
-     * @throws InvalidArgumentException
961
-     * @throws InvalidDataTypeException
962
-     * @throws InvalidInterfaceException
963
-     * @throws ReflectionException
964
-     */
965
-    public function add_line_item(EE_Line_Item $line_item)
966
-    {
967
-        return $this->_add_relation_to($line_item, 'Line_Item');
968
-    }
969
-
970
-
971
-    /**
972
-     * Gets ALL the line items related to this transaction (unstructured)
973
-     *
974
-     * @param array $query_params
975
-     * @return EE_Base_Class[]|EE_Line_Item[]
976
-     * @throws EE_Error
977
-     * @throws InvalidArgumentException
978
-     * @throws InvalidDataTypeException
979
-     * @throws InvalidInterfaceException
980
-     * @throws ReflectionException
981
-     */
982
-    public function line_items($query_params = array())
983
-    {
984
-        return $this->get_many_related('Line_Item', $query_params);
985
-    }
986
-
987
-
988
-    /**
989
-     * Gets all the line items which are taxes on the total
990
-     *
991
-     * @return EE_Line_Item[]
992
-     * @throws EE_Error
993
-     * @throws InvalidArgumentException
994
-     * @throws InvalidDataTypeException
995
-     * @throws InvalidInterfaceException
996
-     * @throws ReflectionException
997
-     */
998
-    public function tax_items()
999
-    {
1000
-        return $this->line_items(array(array('LIN_type' => EEM_Line_Item::type_tax)));
1001
-    }
1002
-
1003
-
1004
-    /**
1005
-     * Gets the total line item (which is a parent of all other related line items,
1006
-     * meaning it takes them all into account on its total)
1007
-     *
1008
-     * @param bool $create_if_not_found
1009
-     * @return \EE_Line_Item
1010
-     * @throws EE_Error
1011
-     * @throws InvalidArgumentException
1012
-     * @throws InvalidDataTypeException
1013
-     * @throws InvalidInterfaceException
1014
-     * @throws ReflectionException
1015
-     */
1016
-    public function total_line_item($create_if_not_found = true)
1017
-    {
1018
-        $item = $this->get_first_related('Line_Item', array(array('LIN_type' => EEM_Line_Item::type_total)));
1019
-        if (! $item && $create_if_not_found) {
1020
-            $item = EEH_Line_Item::create_total_line_item($this);
1021
-        }
1022
-        return $item;
1023
-    }
1024
-
1025
-
1026
-    /**
1027
-     * Returns the total amount of tax on this transaction
1028
-     * (assumes there's only one tax subtotal line item)
1029
-     *
1030
-     * @return float
1031
-     * @throws EE_Error
1032
-     * @throws InvalidArgumentException
1033
-     * @throws InvalidDataTypeException
1034
-     * @throws InvalidInterfaceException
1035
-     * @throws ReflectionException
1036
-     */
1037
-    public function tax_total()
1038
-    {
1039
-        $tax_line_item = $this->tax_total_line_item();
1040
-        if ($tax_line_item) {
1041
-            return (float) $tax_line_item->total();
1042
-        }
1043
-        return (float) 0;
1044
-    }
1045
-
1046
-
1047
-    /**
1048
-     * Gets the tax subtotal line item (assumes there's only one)
1049
-     *
1050
-     * @return EE_Line_Item
1051
-     * @throws EE_Error
1052
-     * @throws InvalidArgumentException
1053
-     * @throws InvalidDataTypeException
1054
-     * @throws InvalidInterfaceException
1055
-     * @throws ReflectionException
1056
-     */
1057
-    public function tax_total_line_item()
1058
-    {
1059
-        return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
1060
-    }
1061
-
1062
-
1063
-    /**
1064
-     * Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
1065
-     *
1066
-     * @return EE_Form_Section_Proper
1067
-     * @throws EE_Error
1068
-     * @throws InvalidArgumentException
1069
-     * @throws InvalidDataTypeException
1070
-     * @throws InvalidInterfaceException
1071
-     * @throws ReflectionException
1072
-     */
1073
-    public function billing_info()
1074
-    {
1075
-        $payment_method = $this->payment_method();
1076
-        if (! $payment_method) {
1077
-            EE_Error::add_error(
1078
-                __(
1079
-                    'Could not find billing info for transaction because no gateway has been used for it yet',
1080
-                    'event_espresso'
1081
-                ),
1082
-                __FILE__,
1083
-                __FUNCTION__,
1084
-                __LINE__
1085
-            );
1086
-            return null;
1087
-        }
1088
-        $primary_reg = $this->primary_registration();
1089
-        if (! $primary_reg) {
1090
-            EE_Error::add_error(
1091
-                __(
1092
-                    'Cannot get billing info for gateway %s on transaction because no primary registration exists',
1093
-                    'event_espresso'
1094
-                ),
1095
-                __FILE__,
1096
-                __FUNCTION__,
1097
-                __LINE__
1098
-            );
1099
-            return null;
1100
-        }
1101
-        $attendee = $primary_reg->attendee();
1102
-        if (! $attendee) {
1103
-            EE_Error::add_error(
1104
-                __(
1105
-                    'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
1106
-                    'event_espresso'
1107
-                ),
1108
-                __FILE__,
1109
-                __FUNCTION__,
1110
-                __LINE__
1111
-            );
1112
-            return null;
1113
-        }
1114
-        return $attendee->billing_info_for_payment_method($payment_method);
1115
-    }
1116
-
1117
-
1118
-    /**
1119
-     * Gets PMD_ID
1120
-     *
1121
-     * @return int
1122
-     * @throws EE_Error
1123
-     * @throws InvalidArgumentException
1124
-     * @throws InvalidDataTypeException
1125
-     * @throws InvalidInterfaceException
1126
-     * @throws ReflectionException
1127
-     */
1128
-    public function payment_method_ID()
1129
-    {
1130
-        return $this->get('PMD_ID');
1131
-    }
1132
-
1133
-
1134
-    /**
1135
-     * Sets PMD_ID
1136
-     *
1137
-     * @param int $PMD_ID
1138
-     * @throws EE_Error
1139
-     * @throws InvalidArgumentException
1140
-     * @throws InvalidDataTypeException
1141
-     * @throws InvalidInterfaceException
1142
-     * @throws ReflectionException
1143
-     */
1144
-    public function set_payment_method_ID($PMD_ID)
1145
-    {
1146
-        $this->set('PMD_ID', $PMD_ID);
1147
-    }
1148
-
1149
-
1150
-    /**
1151
-     * Gets the last-used payment method on this transaction
1152
-     * (we COULD just use the last-made payment, but some payment methods, namely
1153
-     * offline ones, dont' create payments)
1154
-     *
1155
-     * @return EE_Payment_Method
1156
-     * @throws EE_Error
1157
-     * @throws InvalidArgumentException
1158
-     * @throws InvalidDataTypeException
1159
-     * @throws InvalidInterfaceException
1160
-     * @throws ReflectionException
1161
-     */
1162
-    public function payment_method()
1163
-    {
1164
-        $pm = $this->get_first_related('Payment_Method');
1165
-        if ($pm instanceof EE_Payment_Method) {
1166
-            return $pm;
1167
-        }
1168
-        $last_payment = $this->last_payment();
1169
-        if ($last_payment instanceof EE_Payment && $last_payment->payment_method()) {
1170
-            return $last_payment->payment_method();
1171
-        }
1172
-        return null;
1173
-    }
1174
-
1175
-
1176
-    /**
1177
-     * Gets the last payment made
1178
-     *
1179
-     * @return EE_Base_Class|EE_Payment
1180
-     * @throws EE_Error
1181
-     * @throws InvalidArgumentException
1182
-     * @throws InvalidDataTypeException
1183
-     * @throws InvalidInterfaceException
1184
-     * @throws ReflectionException
1185
-     */
1186
-    public function last_payment()
1187
-    {
1188
-        return $this->get_first_related('Payment', array('order_by' => array('PAY_ID' => 'desc')));
1189
-    }
1190
-
1191
-
1192
-    /**
1193
-     * Gets all the line items which are unrelated to tickets on this transaction
1194
-     *
1195
-     * @return EE_Line_Item[]
1196
-     * @throws EE_Error
1197
-     * @throws InvalidArgumentException
1198
-     * @throws InvalidDataTypeException
1199
-     * @throws InvalidInterfaceException
1200
-     * @throws ReflectionException
1201
-     */
1202
-    public function non_ticket_line_items()
1203
-    {
1204
-        return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction($this->ID());
1205
-    }
1206
-
1207
-
1208
-    /**
1209
-     * possibly toggles TXN status
1210
-     *
1211
-     * @param  boolean $update whether to save the TXN
1212
-     * @return bool whether the TXN was saved
1213
-     * @throws EE_Error
1214
-     * @throws InvalidArgumentException
1215
-     * @throws InvalidDataTypeException
1216
-     * @throws InvalidInterfaceException
1217
-     * @throws ReflectionException
1218
-     * @throws RuntimeException
1219
-     */
1220
-    public function update_status_based_on_total_paid($update = true)
1221
-    {
1222
-        // set transaction status based on comparison of TXN_paid vs TXN_total
1223
-        if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
1224
-            $new_txn_status = EEM_Transaction::overpaid_status_code;
1225
-        } elseif (EEH_Money::compare_floats($this->paid(), $this->total())) {
1226
-            $new_txn_status = EEM_Transaction::complete_status_code;
1227
-        } elseif (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
1228
-            $new_txn_status = EEM_Transaction::incomplete_status_code;
1229
-        } else {
1230
-            throw new RuntimeException(
1231
-                __('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
1232
-            );
1233
-        }
1234
-        if ($new_txn_status !== $this->status_ID()) {
1235
-            $this->set_status($new_txn_status);
1236
-            if ($update) {
1237
-                return $this->save() ? true : false;
1238
-            }
1239
-        }
1240
-        return false;
1241
-    }
1242
-
1243
-
1244
-    /**
1245
-     * Updates the transaction's status and total_paid based on all the payments
1246
-     * that apply to it
1247
-     *
1248
-     * @deprecated
1249
-     * @return array|bool
1250
-     * @throws EE_Error
1251
-     * @throws InvalidArgumentException
1252
-     * @throws ReflectionException
1253
-     * @throws InvalidDataTypeException
1254
-     * @throws InvalidInterfaceException
1255
-     */
1256
-    public function update_based_on_payments()
1257
-    {
1258
-        EE_Error::doing_it_wrong(
1259
-            __CLASS__ . '::' . __FUNCTION__,
1260
-            sprintf(
1261
-                __('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1262
-                'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
1263
-            ),
1264
-            '4.6.0'
1265
-        );
1266
-        /** @type EE_Transaction_Processor $transaction_processor */
1267
-        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1268
-        return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
1269
-    }
1270
-
1271
-
1272
-    /**
1273
-     * @return string
1274
-     */
1275
-    public function old_txn_status()
1276
-    {
1277
-        return $this->_old_txn_status;
1278
-    }
1279
-
1280
-
1281
-    /**
1282
-     * @param string $old_txn_status
1283
-     */
1284
-    public function set_old_txn_status($old_txn_status)
1285
-    {
1286
-        // only set the first time
1287
-        if ($this->_old_txn_status === null) {
1288
-            $this->_old_txn_status = $old_txn_status;
1289
-        }
1290
-    }
1291
-
1292
-
1293
-    /**
1294
-     * reg_status_updated
1295
-     *
1296
-     * @return bool
1297
-     * @throws EE_Error
1298
-     * @throws InvalidArgumentException
1299
-     * @throws InvalidDataTypeException
1300
-     * @throws InvalidInterfaceException
1301
-     * @throws ReflectionException
1302
-     */
1303
-    public function txn_status_updated()
1304
-    {
1305
-        return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null;
1306
-    }
1307
-
1308
-
1309
-    /**
1310
-     * _reg_steps_completed
1311
-     * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1312
-     * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1313
-     * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1314
-     *
1315
-     * @param string $reg_step_slug
1316
-     * @param bool   $check_all
1317
-     * @return bool|int
1318
-     * @throws EE_Error
1319
-     * @throws InvalidArgumentException
1320
-     * @throws InvalidDataTypeException
1321
-     * @throws InvalidInterfaceException
1322
-     * @throws ReflectionException
1323
-     */
1324
-    private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1325
-    {
1326
-        $reg_steps = $this->reg_steps();
1327
-        if (! is_array($reg_steps) || empty($reg_steps)) {
1328
-            return false;
1329
-        }
1330
-        // loop thru reg steps array)
1331
-        foreach ($reg_steps as $slug => $reg_step_completed) {
1332
-            // if NOT checking ALL steps (only checking one step)
1333
-            if (! $check_all) {
1334
-                // and this is the one
1335
-                if ($slug === $reg_step_slug) {
1336
-                    return $reg_step_completed;
1337
-                }
1338
-                // skip to next reg step in loop
1339
-                continue;
1340
-            }
1341
-            // $check_all must be true, else we would never have gotten to this point
1342
-            if ($slug === $reg_step_slug) {
1343
-                // if we reach this point, then we are testing either:
1344
-                // all_reg_steps_completed_except() or
1345
-                // all_reg_steps_completed_except_final_step(),
1346
-                // and since this is the reg step EXCEPTION being tested
1347
-                // we want to return true (yes true) if this reg step is NOT completed
1348
-                // ie: "is everything completed except the final step?"
1349
-                // "that is correct... the final step is not completed, but all others are."
1350
-                return $reg_step_completed !== true;
1351
-            }
1352
-            if ($reg_step_completed !== true) {
1353
-                // if any reg step is NOT completed, then ALL steps are not completed
1354
-                return false;
1355
-            }
1356
-        }
1357
-        return true;
1358
-    }
1359
-
1360
-
1361
-    /**
1362
-     * all_reg_steps_completed
1363
-     * returns:
1364
-     *    true if ALL reg steps have been marked as completed
1365
-     *        or false if any step is not completed
1366
-     *
1367
-     * @return bool
1368
-     * @throws EE_Error
1369
-     * @throws InvalidArgumentException
1370
-     * @throws InvalidDataTypeException
1371
-     * @throws InvalidInterfaceException
1372
-     * @throws ReflectionException
1373
-     */
1374
-    public function all_reg_steps_completed()
1375
-    {
1376
-        return $this->_reg_steps_completed();
1377
-    }
1378
-
1379
-
1380
-    /**
1381
-     * all_reg_steps_completed_except
1382
-     * returns:
1383
-     *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1384
-     *        or false if any other step is not completed
1385
-     *        or false if ALL steps are completed including the exception you are testing !!!
1386
-     *
1387
-     * @param string $exception
1388
-     * @return bool
1389
-     * @throws EE_Error
1390
-     * @throws InvalidArgumentException
1391
-     * @throws InvalidDataTypeException
1392
-     * @throws InvalidInterfaceException
1393
-     * @throws ReflectionException
1394
-     */
1395
-    public function all_reg_steps_completed_except($exception = '')
1396
-    {
1397
-        return $this->_reg_steps_completed($exception);
1398
-    }
1399
-
1400
-
1401
-    /**
1402
-     * all_reg_steps_completed_except
1403
-     * returns:
1404
-     *        true if ALL reg steps, except the final step, have been marked as completed
1405
-     *        or false if any step is not completed
1406
-     *    or false if ALL steps are completed including the final step !!!
1407
-     *
1408
-     * @return bool
1409
-     * @throws EE_Error
1410
-     * @throws InvalidArgumentException
1411
-     * @throws InvalidDataTypeException
1412
-     * @throws InvalidInterfaceException
1413
-     * @throws ReflectionException
1414
-     */
1415
-    public function all_reg_steps_completed_except_final_step()
1416
-    {
1417
-        return $this->_reg_steps_completed('finalize_registration');
1418
-    }
1419
-
1420
-
1421
-    /**
1422
-     * reg_step_completed
1423
-     * returns:
1424
-     *    true if a specific reg step has been marked as completed
1425
-     *    a Unix timestamp if it has been initialized but not yet completed,
1426
-     *    or false if it has not yet been initialized
1427
-     *
1428
-     * @param string $reg_step_slug
1429
-     * @return bool|int
1430
-     * @throws EE_Error
1431
-     * @throws InvalidArgumentException
1432
-     * @throws InvalidDataTypeException
1433
-     * @throws InvalidInterfaceException
1434
-     * @throws ReflectionException
1435
-     */
1436
-    public function reg_step_completed($reg_step_slug)
1437
-    {
1438
-        return $this->_reg_steps_completed($reg_step_slug, false);
1439
-    }
1440
-
1441
-
1442
-    /**
1443
-     * completed_final_reg_step
1444
-     * returns:
1445
-     *    true if the finalize_registration reg step has been marked as completed
1446
-     *    a Unix timestamp if it has been initialized but not yet completed,
1447
-     *    or false if it has not yet been initialized
1448
-     *
1449
-     * @return bool|int
1450
-     * @throws EE_Error
1451
-     * @throws InvalidArgumentException
1452
-     * @throws InvalidDataTypeException
1453
-     * @throws InvalidInterfaceException
1454
-     * @throws ReflectionException
1455
-     */
1456
-    public function final_reg_step_completed()
1457
-    {
1458
-        return $this->_reg_steps_completed('finalize_registration', false);
1459
-    }
1460
-
1461
-
1462
-    /**
1463
-     * set_reg_step_initiated
1464
-     * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1465
-     *
1466
-     * @param string $reg_step_slug
1467
-     * @return boolean
1468
-     * @throws EE_Error
1469
-     * @throws InvalidArgumentException
1470
-     * @throws InvalidDataTypeException
1471
-     * @throws InvalidInterfaceException
1472
-     * @throws ReflectionException
1473
-     */
1474
-    public function set_reg_step_initiated($reg_step_slug)
1475
-    {
1476
-        return $this->_set_reg_step_completed_status($reg_step_slug, time());
1477
-    }
1478
-
1479
-
1480
-    /**
1481
-     * set_reg_step_completed
1482
-     * given a valid TXN_reg_step, this sets the step as completed
1483
-     *
1484
-     * @param string $reg_step_slug
1485
-     * @return boolean
1486
-     * @throws EE_Error
1487
-     * @throws InvalidArgumentException
1488
-     * @throws InvalidDataTypeException
1489
-     * @throws InvalidInterfaceException
1490
-     * @throws ReflectionException
1491
-     */
1492
-    public function set_reg_step_completed($reg_step_slug)
1493
-    {
1494
-        return $this->_set_reg_step_completed_status($reg_step_slug, true);
1495
-    }
1496
-
1497
-
1498
-    /**
1499
-     * set_reg_step_completed
1500
-     * given a valid TXN_reg_step slug, this sets the step as NOT completed
1501
-     *
1502
-     * @param string $reg_step_slug
1503
-     * @return boolean
1504
-     * @throws EE_Error
1505
-     * @throws InvalidArgumentException
1506
-     * @throws InvalidDataTypeException
1507
-     * @throws InvalidInterfaceException
1508
-     * @throws ReflectionException
1509
-     */
1510
-    public function set_reg_step_not_completed($reg_step_slug)
1511
-    {
1512
-        return $this->_set_reg_step_completed_status($reg_step_slug, false);
1513
-    }
1514
-
1515
-
1516
-    /**
1517
-     * set_reg_step_completed
1518
-     * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1519
-     *
1520
-     * @param  string      $reg_step_slug
1521
-     * @param  boolean|int $status
1522
-     * @return boolean
1523
-     * @throws EE_Error
1524
-     * @throws InvalidArgumentException
1525
-     * @throws InvalidDataTypeException
1526
-     * @throws InvalidInterfaceException
1527
-     * @throws ReflectionException
1528
-     */
1529
-    private function _set_reg_step_completed_status($reg_step_slug, $status)
1530
-    {
1531
-        // validate status
1532
-        $status = is_bool($status) || is_int($status) ? $status : false;
1533
-        // get reg steps array
1534
-        $txn_reg_steps = $this->reg_steps();
1535
-        // if reg step does NOT exist
1536
-        if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1537
-            return false;
1538
-        }
1539
-        // if  we're trying to complete a step that is already completed
1540
-        if ($txn_reg_steps[ $reg_step_slug ] === true) {
1541
-            return true;
1542
-        }
1543
-        // if  we're trying to complete a step that hasn't even started
1544
-        if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1545
-            return false;
1546
-        }
1547
-        // if current status value matches the incoming value (no change)
1548
-        // type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1549
-        if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1550
-            // this will happen in cases where multiple AJAX requests occur during the same step
1551
-            return true;
1552
-        }
1553
-        // if we're trying to set a start time, but it has already been set...
1554
-        if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1555
-            // skip the update below, but don't return FALSE so that errors won't be displayed
1556
-            return true;
1557
-        }
1558
-        // update completed status
1559
-        $txn_reg_steps[ $reg_step_slug ] = $status;
1560
-        $this->set_reg_steps($txn_reg_steps);
1561
-        $this->save();
1562
-        return true;
1563
-    }
1564
-
1565
-
1566
-    /**
1567
-     * remove_reg_step
1568
-     * given a valid TXN_reg_step slug, this will remove (unset)
1569
-     * the reg step from the TXN reg step array
1570
-     *
1571
-     * @param string $reg_step_slug
1572
-     * @return void
1573
-     * @throws EE_Error
1574
-     * @throws InvalidArgumentException
1575
-     * @throws InvalidDataTypeException
1576
-     * @throws InvalidInterfaceException
1577
-     * @throws ReflectionException
1578
-     */
1579
-    public function remove_reg_step($reg_step_slug)
1580
-    {
1581
-        // get reg steps array
1582
-        $txn_reg_steps = $this->reg_steps();
1583
-        unset($txn_reg_steps[ $reg_step_slug ]);
1584
-        $this->set_reg_steps($txn_reg_steps);
1585
-    }
1586
-
1587
-
1588
-    /**
1589
-     * toggle_failed_transaction_status
1590
-     * upgrades a TXNs status from failed to abandoned,
1591
-     * meaning that contact information has been captured for at least one registrant
1592
-     *
1593
-     * @param bool $save
1594
-     * @return bool
1595
-     * @throws EE_Error
1596
-     * @throws InvalidArgumentException
1597
-     * @throws InvalidDataTypeException
1598
-     * @throws InvalidInterfaceException
1599
-     * @throws ReflectionException
1600
-     */
1601
-    public function toggle_failed_transaction_status($save = true)
1602
-    {
1603
-        // if TXN status is still set as "failed"...
1604
-        if ($this->status_ID() === EEM_Transaction::failed_status_code) {
1605
-            $this->set_status(EEM_Transaction::abandoned_status_code);
1606
-            if ($save) {
1607
-                $this->save();
1608
-            }
1609
-            return true;
1610
-        }
1611
-        return false;
1612
-    }
1613
-
1614
-
1615
-    /**
1616
-     * toggle_abandoned_transaction_status
1617
-     * upgrades a TXNs status from failed or abandoned to incomplete
1618
-     *
1619
-     * @return bool
1620
-     * @throws EE_Error
1621
-     * @throws InvalidArgumentException
1622
-     * @throws InvalidDataTypeException
1623
-     * @throws InvalidInterfaceException
1624
-     * @throws ReflectionException
1625
-     */
1626
-    public function toggle_abandoned_transaction_status()
1627
-    {
1628
-        // if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1629
-        $txn_status = $this->status_ID();
1630
-        if ($txn_status === EEM_Transaction::failed_status_code
1631
-            || $txn_status === EEM_Transaction::abandoned_status_code
1632
-        ) {
1633
-            // if a contact record for the primary registrant has been created
1634
-            if ($this->primary_registration() instanceof EE_Registration
1635
-                && $this->primary_registration()->attendee() instanceof EE_Attendee
1636
-            ) {
1637
-                $this->set_status(EEM_Transaction::incomplete_status_code);
1638
-            } else {
1639
-                // no contact record? yer abandoned!
1640
-                $this->set_status(EEM_Transaction::abandoned_status_code);
1641
-            }
1642
-            return true;
1643
-        }
1644
-        return false;
1645
-    }
1646
-
1647
-
1648
-    /**
1649
-     * checks if an Abandoned TXN has any related payments, and if so,
1650
-     * updates the TXN status based on the amount paid
1651
-     *
1652
-     * @throws EE_Error
1653
-     * @throws InvalidDataTypeException
1654
-     * @throws InvalidInterfaceException
1655
-     * @throws InvalidArgumentException
1656
-     * @throws RuntimeException
1657
-     * @throws ReflectionException
1658
-     */
1659
-    public function verify_abandoned_transaction_status()
1660
-    {
1661
-        if ($this->status_ID() !== EEM_Transaction::abandoned_status_code) {
1662
-            return;
1663
-        }
1664
-        $payments = $this->get_many_related('Payment');
1665
-        if (! empty($payments)) {
1666
-            foreach ($payments as $payment) {
1667
-                if ($payment instanceof EE_Payment) {
1668
-                    // kk this TXN should NOT be abandoned
1669
-                    $this->update_status_based_on_total_paid();
1670
-                    if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1671
-                        EE_Error::add_attention(
1672
-                            sprintf(
1673
-                                esc_html__(
1674
-                                    'The status for Transaction #%1$d has been updated from "Abandoned" to "%2$s", because at least one payment has been made towards it. If the payment appears in the "Payment Details" table below, you may need to edit its status and/or other details as well.',
1675
-                                    'event_espresso'
1676
-                                ),
1677
-                                $this->ID(),
1678
-                                $this->pretty_status()
1679
-                            )
1680
-                        );
1681
-                    }
1682
-                    // get final reg step status
1683
-                    $finalized = $this->final_reg_step_completed();
1684
-                    // if the 'finalize_registration' step has been initiated (has a timestamp)
1685
-                    // but has not yet been fully completed (TRUE)
1686
-                    if (is_int($finalized) && $finalized !== false && $finalized !== true) {
1687
-                        $this->set_reg_step_completed('finalize_registration');
1688
-                        $this->save();
1689
-                    }
1690
-                }
1691
-            }
1692
-        }
1693
-    }
1694
-
1695
-
1696
-    /**
1697
-     * @since 4.10.4.p
1698
-     * @throws EE_Error
1699
-     * @throws InvalidArgumentException
1700
-     * @throws InvalidDataTypeException
1701
-     * @throws InvalidInterfaceException
1702
-     * @throws ReflectionException
1703
-     * @throws RuntimeException
1704
-     */
1705
-    public function recalculateLineItems()
1706
-    {
1707
-        $total_line_item = $this->total_line_item(false);
1708
-        if ($total_line_item instanceof EE_Line_Item) {
1709
-            EEH_Line_Item::resetIsTaxableForTickets($total_line_item);
1710
-            return EEH_Line_Item::apply_taxes($total_line_item, true);
1711
-        }
1712
-        return false;
1713
-    }
16
+	/**
17
+	 * The length of time in seconds that a lock is applied before being considered expired.
18
+	 * It is not long because a transaction should only be locked for the duration of the request that locked it
19
+	 */
20
+	const LOCK_EXPIRATION = 2;
21
+
22
+	/**
23
+	 * txn status upon initial construction.
24
+	 *
25
+	 * @var string
26
+	 */
27
+	protected $_old_txn_status;
28
+
29
+
30
+	/**
31
+	 * @param array  $props_n_values          incoming values
32
+	 * @param string $timezone                incoming timezone
33
+	 *                                        (if not set the timezone set for the website will be used.)
34
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
35
+	 *                                        date_format and the second value is the time format
36
+	 * @return EE_Transaction
37
+	 * @throws EE_Error
38
+	 * @throws InvalidArgumentException
39
+	 * @throws InvalidDataTypeException
40
+	 * @throws InvalidInterfaceException
41
+	 * @throws ReflectionException
42
+	 */
43
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
44
+	{
45
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
46
+		$txn = $has_object
47
+			? $has_object
48
+			: new self($props_n_values, false, $timezone, $date_formats);
49
+		if (! $has_object) {
50
+			$txn->set_old_txn_status($txn->status_ID());
51
+		}
52
+		return $txn;
53
+	}
54
+
55
+
56
+	/**
57
+	 * @param array  $props_n_values  incoming values from the database
58
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
59
+	 *                                the website will be used.
60
+	 * @return EE_Transaction
61
+	 * @throws EE_Error
62
+	 * @throws InvalidArgumentException
63
+	 * @throws InvalidDataTypeException
64
+	 * @throws InvalidInterfaceException
65
+	 * @throws ReflectionException
66
+	 */
67
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
68
+	{
69
+		$txn = new self($props_n_values, true, $timezone);
70
+		$txn->set_old_txn_status($txn->status_ID());
71
+		return $txn;
72
+	}
73
+
74
+
75
+	/**
76
+	 * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
77
+	 * If a lock has already been set, then we will attempt to remove it in case it has expired.
78
+	 * If that also fails, then an exception is thrown.
79
+	 *
80
+	 * @throws EE_Error
81
+	 * @throws InvalidArgumentException
82
+	 * @throws InvalidDataTypeException
83
+	 * @throws InvalidInterfaceException
84
+	 * @throws ReflectionException
85
+	 */
86
+	public function lock()
87
+	{
88
+		// attempt to set lock, but if that fails...
89
+		if (! $this->add_extra_meta('lock', time(), true)) {
90
+			// then attempt to remove the lock in case it is expired
91
+			if ($this->_remove_expired_lock()) {
92
+				// if removal was successful, then try setting lock again
93
+				$this->lock();
94
+			} else {
95
+				// but if the lock can not be removed, then throw an exception
96
+				throw new EE_Error(
97
+					sprintf(
98
+						__(
99
+							'Could not lock Transaction %1$d because it is already locked, meaning another part of the system is currently editing it. It should already be unlocked by the time you read this, so please refresh the page and try again.',
100
+							'event_espresso'
101
+						),
102
+						$this->ID()
103
+					)
104
+				);
105
+			}
106
+		}
107
+	}
108
+
109
+
110
+	/**
111
+	 * removes transaction lock applied in EE_Transaction::lock()
112
+	 *
113
+	 * @return int
114
+	 * @throws EE_Error
115
+	 * @throws InvalidArgumentException
116
+	 * @throws InvalidDataTypeException
117
+	 * @throws InvalidInterfaceException
118
+	 * @throws ReflectionException
119
+	 */
120
+	public function unlock()
121
+	{
122
+		return $this->delete_extra_meta('lock');
123
+	}
124
+
125
+
126
+	/**
127
+	 * Decides whether or not now is the right time to update the transaction.
128
+	 * This is useful because we don't always know if it is safe to update the transaction
129
+	 * and its related data. why?
130
+	 * because it's possible that the transaction is being used in another
131
+	 * request and could overwrite anything we save.
132
+	 * So we want to only update the txn once we know that won't happen.
133
+	 * We also check that the lock isn't expired, and remove it if it is
134
+	 *
135
+	 * @return boolean
136
+	 * @throws EE_Error
137
+	 * @throws InvalidArgumentException
138
+	 * @throws InvalidDataTypeException
139
+	 * @throws InvalidInterfaceException
140
+	 * @throws ReflectionException
141
+	 */
142
+	public function is_locked()
143
+	{
144
+		// if TXN is not locked, then return false immediately
145
+		if (! $this->_get_lock()) {
146
+			return false;
147
+		}
148
+		// if not, then let's try and remove the lock in case it's expired...
149
+		// _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
150
+		// and a positive number if the lock was removed (ie: number of locks deleted),
151
+		// so we need to return the opposite
152
+		return ! $this->_remove_expired_lock() ? true : false;
153
+	}
154
+
155
+
156
+	/**
157
+	 * Gets the meta field indicating that this TXN is locked
158
+	 *
159
+	 * @return int
160
+	 * @throws EE_Error
161
+	 * @throws InvalidArgumentException
162
+	 * @throws InvalidDataTypeException
163
+	 * @throws InvalidInterfaceException
164
+	 * @throws ReflectionException
165
+	 */
166
+	protected function _get_lock()
167
+	{
168
+		return (int) $this->get_extra_meta('lock', true, 0);
169
+	}
170
+
171
+
172
+	/**
173
+	 * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
174
+	 *
175
+	 * @return int
176
+	 * @throws EE_Error
177
+	 * @throws InvalidArgumentException
178
+	 * @throws InvalidDataTypeException
179
+	 * @throws InvalidInterfaceException
180
+	 * @throws ReflectionException
181
+	 */
182
+	protected function _remove_expired_lock()
183
+	{
184
+		$locked = $this->_get_lock();
185
+		if ($locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked) {
186
+			return $this->unlock();
187
+		}
188
+		return 0;
189
+	}
190
+
191
+
192
+	/**
193
+	 * Set transaction total
194
+	 *
195
+	 * @param float $total total value of transaction
196
+	 * @throws EE_Error
197
+	 * @throws InvalidArgumentException
198
+	 * @throws InvalidDataTypeException
199
+	 * @throws InvalidInterfaceException
200
+	 * @throws ReflectionException
201
+	 */
202
+	public function set_total($total = 0.00)
203
+	{
204
+		$this->set('TXN_total', (float) $total);
205
+	}
206
+
207
+
208
+	/**
209
+	 * Set Total Amount Paid to Date
210
+	 *
211
+	 * @param float $total_paid total amount paid to date (sum of all payments)
212
+	 * @throws EE_Error
213
+	 * @throws InvalidArgumentException
214
+	 * @throws InvalidDataTypeException
215
+	 * @throws InvalidInterfaceException
216
+	 * @throws ReflectionException
217
+	 */
218
+	public function set_paid($total_paid = 0.00)
219
+	{
220
+		$this->set('TXN_paid', (float) $total_paid);
221
+	}
222
+
223
+
224
+	/**
225
+	 * Set transaction status
226
+	 *
227
+	 * @param string $status        whether the transaction is open, declined, accepted,
228
+	 *                              or any number of custom values that can be set
229
+	 * @throws EE_Error
230
+	 * @throws InvalidArgumentException
231
+	 * @throws InvalidDataTypeException
232
+	 * @throws InvalidInterfaceException
233
+	 * @throws ReflectionException
234
+	 */
235
+	public function set_status($status = '')
236
+	{
237
+		$this->set('STS_ID', $status);
238
+	}
239
+
240
+
241
+	/**
242
+	 * Set hash salt
243
+	 *
244
+	 * @param string $hash_salt required for some payment gateways
245
+	 * @throws EE_Error
246
+	 * @throws InvalidArgumentException
247
+	 * @throws InvalidDataTypeException
248
+	 * @throws InvalidInterfaceException
249
+	 * @throws ReflectionException
250
+	 */
251
+	public function set_hash_salt($hash_salt = '')
252
+	{
253
+		$this->set('TXN_hash_salt', $hash_salt);
254
+	}
255
+
256
+
257
+	/**
258
+	 * Sets TXN_reg_steps array
259
+	 *
260
+	 * @param array $txn_reg_steps
261
+	 * @throws EE_Error
262
+	 * @throws InvalidArgumentException
263
+	 * @throws InvalidDataTypeException
264
+	 * @throws InvalidInterfaceException
265
+	 * @throws ReflectionException
266
+	 */
267
+	public function set_reg_steps(array $txn_reg_steps)
268
+	{
269
+		$this->set('TXN_reg_steps', $txn_reg_steps);
270
+	}
271
+
272
+
273
+	/**
274
+	 * Gets TXN_reg_steps
275
+	 *
276
+	 * @return array
277
+	 * @throws EE_Error
278
+	 * @throws InvalidArgumentException
279
+	 * @throws InvalidDataTypeException
280
+	 * @throws InvalidInterfaceException
281
+	 * @throws ReflectionException
282
+	 */
283
+	public function reg_steps()
284
+	{
285
+		$TXN_reg_steps = $this->get('TXN_reg_steps');
286
+		return is_array($TXN_reg_steps) ? (array) $TXN_reg_steps : array();
287
+	}
288
+
289
+
290
+	/**
291
+	 * @return string of transaction's total cost, with currency symbol and decimal
292
+	 * @throws EE_Error
293
+	 * @throws InvalidArgumentException
294
+	 * @throws InvalidDataTypeException
295
+	 * @throws InvalidInterfaceException
296
+	 * @throws ReflectionException
297
+	 */
298
+	public function pretty_total()
299
+	{
300
+		return $this->get_pretty('TXN_total');
301
+	}
302
+
303
+
304
+	/**
305
+	 * Gets the amount paid in a pretty string (formatted and with currency symbol)
306
+	 *
307
+	 * @return string
308
+	 * @throws EE_Error
309
+	 * @throws InvalidArgumentException
310
+	 * @throws InvalidDataTypeException
311
+	 * @throws InvalidInterfaceException
312
+	 * @throws ReflectionException
313
+	 */
314
+	public function pretty_paid()
315
+	{
316
+		return $this->get_pretty('TXN_paid');
317
+	}
318
+
319
+
320
+	/**
321
+	 * calculate the amount remaining for this transaction and return;
322
+	 *
323
+	 * @return float amount remaining
324
+	 * @throws EE_Error
325
+	 * @throws InvalidArgumentException
326
+	 * @throws InvalidDataTypeException
327
+	 * @throws InvalidInterfaceException
328
+	 * @throws ReflectionException
329
+	 */
330
+	public function remaining()
331
+	{
332
+		return $this->total() - $this->paid();
333
+	}
334
+
335
+
336
+	/**
337
+	 * get Transaction Total
338
+	 *
339
+	 * @return float
340
+	 * @throws EE_Error
341
+	 * @throws InvalidArgumentException
342
+	 * @throws InvalidDataTypeException
343
+	 * @throws InvalidInterfaceException
344
+	 * @throws ReflectionException
345
+	 */
346
+	public function total()
347
+	{
348
+		return (float) $this->get('TXN_total');
349
+	}
350
+
351
+
352
+	/**
353
+	 * get Total Amount Paid to Date
354
+	 *
355
+	 * @return float
356
+	 * @throws EE_Error
357
+	 * @throws InvalidArgumentException
358
+	 * @throws InvalidDataTypeException
359
+	 * @throws InvalidInterfaceException
360
+	 * @throws ReflectionException
361
+	 */
362
+	public function paid()
363
+	{
364
+		return (float) $this->get('TXN_paid');
365
+	}
366
+
367
+
368
+	/**
369
+	 * @return mixed|null
370
+	 * @throws EE_Error
371
+	 * @throws InvalidArgumentException
372
+	 * @throws InvalidDataTypeException
373
+	 * @throws InvalidInterfaceException
374
+	 * @throws ReflectionException
375
+	 */
376
+	public function get_cart_session()
377
+	{
378
+		$session_data = (array) $this->get('TXN_session_data');
379
+		return isset($session_data['cart']) && $session_data['cart'] instanceof EE_Cart
380
+			? $session_data['cart']
381
+			: null;
382
+	}
383
+
384
+
385
+	/**
386
+	 * get Transaction session data
387
+	 *
388
+	 * @return array|mixed
389
+	 * @throws EE_Error
390
+	 * @throws InvalidArgumentException
391
+	 * @throws InvalidDataTypeException
392
+	 * @throws InvalidInterfaceException
393
+	 * @throws ReflectionException
394
+	 */
395
+	public function session_data()
396
+	{
397
+		$session_data = $this->get('TXN_session_data');
398
+		if (empty($session_data)) {
399
+			$session_data = array(
400
+				'id'            => null,
401
+				'user_id'       => null,
402
+				'ip_address'    => null,
403
+				'user_agent'    => null,
404
+				'init_access'   => null,
405
+				'last_access'   => null,
406
+				'pages_visited' => array(),
407
+			);
408
+		}
409
+		return $session_data;
410
+	}
411
+
412
+
413
+	/**
414
+	 * Set session data within the TXN object
415
+	 *
416
+	 * @param EE_Session|array $session_data
417
+	 * @throws EE_Error
418
+	 * @throws InvalidArgumentException
419
+	 * @throws InvalidDataTypeException
420
+	 * @throws InvalidInterfaceException
421
+	 * @throws ReflectionException
422
+	 */
423
+	public function set_txn_session_data($session_data)
424
+	{
425
+		if ($session_data instanceof EE_Session) {
426
+			$this->set('TXN_session_data', $session_data->get_session_data(null, true));
427
+		} else {
428
+			$this->set('TXN_session_data', $session_data);
429
+		}
430
+	}
431
+
432
+
433
+	/**
434
+	 * get Transaction hash salt
435
+	 *
436
+	 * @return mixed
437
+	 * @throws EE_Error
438
+	 * @throws InvalidArgumentException
439
+	 * @throws InvalidDataTypeException
440
+	 * @throws InvalidInterfaceException
441
+	 * @throws ReflectionException
442
+	 */
443
+	public function hash_salt_()
444
+	{
445
+		return $this->get('TXN_hash_salt');
446
+	}
447
+
448
+
449
+	/**
450
+	 * Returns the transaction datetime as either:
451
+	 *            - unix timestamp format ($format = false, $gmt = true)
452
+	 *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
453
+	 *              has no affect with this option)), this also may include a timezone abbreviation if the
454
+	 *              set timezone in this class differs from what the timezone is on the blog.
455
+	 *            - formatted date string including the UTC (timezone) offset (default).
456
+	 *
457
+	 * @param boolean $format   - whether to return a unix timestamp (default) or formatted date string
458
+	 * @param boolean $gmt      - whether to return a unix timestamp with UTC offset applied (default)
459
+	 *                          or no UTC offset applied
460
+	 * @return string | int
461
+	 * @throws EE_Error
462
+	 * @throws InvalidArgumentException
463
+	 * @throws InvalidDataTypeException
464
+	 * @throws InvalidInterfaceException
465
+	 * @throws ReflectionException
466
+	 */
467
+	public function datetime($format = false, $gmt = false)
468
+	{
469
+		if ($format) {
470
+			return $this->get_pretty('TXN_timestamp');
471
+		}
472
+		if ($gmt) {
473
+			return $this->get_raw('TXN_timestamp');
474
+		}
475
+		return $this->get('TXN_timestamp');
476
+	}
477
+
478
+
479
+	/**
480
+	 * Gets registrations on this transaction
481
+	 *
482
+	 * @param array   $query_params array of query parameters
483
+	 * @param boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
484
+	 * @return EE_Base_Class[]|EE_Registration[]
485
+	 * @throws EE_Error
486
+	 * @throws InvalidArgumentException
487
+	 * @throws InvalidDataTypeException
488
+	 * @throws InvalidInterfaceException
489
+	 * @throws ReflectionException
490
+	 */
491
+	public function registrations($query_params = array(), $get_cached = false)
492
+	{
493
+		$query_params = (empty($query_params) || ! is_array($query_params))
494
+			? array(
495
+				'order_by' => array(
496
+					'Event.EVT_name'     => 'ASC',
497
+					'Attendee.ATT_lname' => 'ASC',
498
+					'Attendee.ATT_fname' => 'ASC',
499
+				),
500
+			)
501
+			: $query_params;
502
+		$query_params = $get_cached ? array() : $query_params;
503
+		return $this->get_many_related('Registration', $query_params);
504
+	}
505
+
506
+
507
+	/**
508
+	 * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event
509
+	 * function for getting attendees and how many registrations they each have for an event)
510
+	 *
511
+	 * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
512
+	 * @throws EE_Error
513
+	 * @throws InvalidArgumentException
514
+	 * @throws InvalidDataTypeException
515
+	 * @throws InvalidInterfaceException
516
+	 * @throws ReflectionException
517
+	 */
518
+	public function attendees()
519
+	{
520
+		return $this->get_many_related('Attendee', array(array('Registration.Transaction.TXN_ID' => $this->ID())));
521
+	}
522
+
523
+
524
+	/**
525
+	 * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
526
+	 *
527
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
528
+	 * @return EE_Base_Class[]|EE_Payment[]
529
+	 * @throws EE_Error
530
+	 * @throws InvalidArgumentException
531
+	 * @throws InvalidDataTypeException
532
+	 * @throws InvalidInterfaceException
533
+	 * @throws ReflectionException
534
+	 */
535
+	public function payments($query_params = array())
536
+	{
537
+		return $this->get_many_related('Payment', $query_params);
538
+	}
539
+
540
+
541
+	/**
542
+	 * gets only approved payments for this transaction
543
+	 *
544
+	 * @return EE_Base_Class[]|EE_Payment[]
545
+	 * @throws EE_Error
546
+	 * @throws InvalidArgumentException
547
+	 * @throws ReflectionException
548
+	 * @throws InvalidDataTypeException
549
+	 * @throws InvalidInterfaceException
550
+	 */
551
+	public function approved_payments()
552
+	{
553
+		EE_Registry::instance()->load_model('Payment');
554
+		return $this->get_many_related(
555
+			'Payment',
556
+			array(
557
+				array('STS_ID' => EEM_Payment::status_id_approved),
558
+				'order_by' => array('PAY_timestamp' => 'DESC'),
559
+			)
560
+		);
561
+	}
562
+
563
+
564
+	/**
565
+	 * Gets all payments which have not been approved
566
+	 *
567
+	 * @return EE_Base_Class[]|EEI_Payment[]
568
+	 * @throws EE_Error if a model is misconfigured somehow
569
+	 * @throws InvalidArgumentException
570
+	 * @throws InvalidDataTypeException
571
+	 * @throws InvalidInterfaceException
572
+	 * @throws ReflectionException
573
+	 */
574
+	public function pending_payments()
575
+	{
576
+		return $this->get_many_related(
577
+			'Payment',
578
+			array(
579
+				array(
580
+					'STS_ID' => EEM_Payment::status_id_pending,
581
+				),
582
+				'order_by' => array(
583
+					'PAY_timestamp' => 'DESC',
584
+				),
585
+			)
586
+		);
587
+	}
588
+
589
+
590
+	/**
591
+	 * echoes $this->pretty_status()
592
+	 *
593
+	 * @param bool $show_icons
594
+	 * @throws EE_Error
595
+	 * @throws InvalidArgumentException
596
+	 * @throws InvalidDataTypeException
597
+	 * @throws InvalidInterfaceException
598
+	 * @throws ReflectionException
599
+	 */
600
+	public function e_pretty_status($show_icons = false)
601
+	{
602
+		echo $this->pretty_status($show_icons);
603
+	}
604
+
605
+
606
+	/**
607
+	 * returns a pretty version of the status, good for displaying to users
608
+	 *
609
+	 * @param bool $show_icons
610
+	 * @return string
611
+	 * @throws EE_Error
612
+	 * @throws InvalidArgumentException
613
+	 * @throws InvalidDataTypeException
614
+	 * @throws InvalidInterfaceException
615
+	 * @throws ReflectionException
616
+	 */
617
+	public function pretty_status($show_icons = false)
618
+	{
619
+		$status = EEM_Status::instance()->localized_status(
620
+			array($this->status_ID() => __('unknown', 'event_espresso')),
621
+			false,
622
+			'sentence'
623
+		);
624
+		$icon = '';
625
+		switch ($this->status_ID()) {
626
+			case EEM_Transaction::complete_status_code:
627
+				$icon = $show_icons ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>' : '';
628
+				break;
629
+			case EEM_Transaction::incomplete_status_code:
630
+				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>'
631
+					: '';
632
+				break;
633
+			case EEM_Transaction::abandoned_status_code:
634
+				$icon = $show_icons ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>' : '';
635
+				break;
636
+			case EEM_Transaction::failed_status_code:
637
+				$icon = $show_icons ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>' : '';
638
+				break;
639
+			case EEM_Transaction::overpaid_status_code:
640
+				$icon = $show_icons ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>' : '';
641
+				break;
642
+		}
643
+		return $icon . $status[ $this->status_ID() ];
644
+	}
645
+
646
+
647
+	/**
648
+	 * get Transaction Status
649
+	 *
650
+	 * @return mixed
651
+	 * @throws EE_Error
652
+	 * @throws InvalidArgumentException
653
+	 * @throws InvalidDataTypeException
654
+	 * @throws InvalidInterfaceException
655
+	 * @throws ReflectionException
656
+	 */
657
+	public function status_ID()
658
+	{
659
+		return $this->get('STS_ID');
660
+	}
661
+
662
+
663
+	/**
664
+	 * Returns TRUE or FALSE for whether or not this transaction cost any money
665
+	 *
666
+	 * @return boolean
667
+	 * @throws EE_Error
668
+	 * @throws InvalidArgumentException
669
+	 * @throws InvalidDataTypeException
670
+	 * @throws InvalidInterfaceException
671
+	 * @throws ReflectionException
672
+	 */
673
+	public function is_free()
674
+	{
675
+		return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
676
+	}
677
+
678
+
679
+	/**
680
+	 * Returns whether this transaction is complete
681
+	 * Useful in templates and other logic for deciding if we should ask for another payment...
682
+	 *
683
+	 * @return boolean
684
+	 * @throws EE_Error
685
+	 * @throws InvalidArgumentException
686
+	 * @throws InvalidDataTypeException
687
+	 * @throws InvalidInterfaceException
688
+	 * @throws ReflectionException
689
+	 */
690
+	public function is_completed()
691
+	{
692
+		return $this->status_ID() === EEM_Transaction::complete_status_code;
693
+	}
694
+
695
+
696
+	/**
697
+	 * Returns whether this transaction is incomplete
698
+	 * Useful in templates and other logic for deciding if we should ask for another payment...
699
+	 *
700
+	 * @return boolean
701
+	 * @throws EE_Error
702
+	 * @throws InvalidArgumentException
703
+	 * @throws InvalidDataTypeException
704
+	 * @throws InvalidInterfaceException
705
+	 * @throws ReflectionException
706
+	 */
707
+	public function is_incomplete()
708
+	{
709
+		return $this->status_ID() === EEM_Transaction::incomplete_status_code;
710
+	}
711
+
712
+
713
+	/**
714
+	 * Returns whether this transaction is overpaid
715
+	 * Useful in templates and other logic for deciding if monies need to be refunded
716
+	 *
717
+	 * @return boolean
718
+	 * @throws EE_Error
719
+	 * @throws InvalidArgumentException
720
+	 * @throws InvalidDataTypeException
721
+	 * @throws InvalidInterfaceException
722
+	 * @throws ReflectionException
723
+	 */
724
+	public function is_overpaid()
725
+	{
726
+		return $this->status_ID() === EEM_Transaction::overpaid_status_code;
727
+	}
728
+
729
+
730
+	/**
731
+	 * Returns whether this transaction was abandoned
732
+	 * meaning that the transaction/registration process was somehow interrupted and never completed
733
+	 * but that contact information exists for at least one registrant
734
+	 *
735
+	 * @return boolean
736
+	 * @throws EE_Error
737
+	 * @throws InvalidArgumentException
738
+	 * @throws InvalidDataTypeException
739
+	 * @throws InvalidInterfaceException
740
+	 * @throws ReflectionException
741
+	 */
742
+	public function is_abandoned()
743
+	{
744
+		return $this->status_ID() === EEM_Transaction::abandoned_status_code;
745
+	}
746
+
747
+
748
+	/**
749
+	 * Returns whether this transaction failed
750
+	 * meaning that the transaction/registration process was somehow interrupted and never completed
751
+	 * and that NO contact information exists for any registrants
752
+	 *
753
+	 * @return boolean
754
+	 * @throws EE_Error
755
+	 * @throws InvalidArgumentException
756
+	 * @throws InvalidDataTypeException
757
+	 * @throws InvalidInterfaceException
758
+	 * @throws ReflectionException
759
+	 */
760
+	public function failed()
761
+	{
762
+		return $this->status_ID() === EEM_Transaction::failed_status_code;
763
+	}
764
+
765
+
766
+	/**
767
+	 * This returns the url for the invoice of this transaction
768
+	 *
769
+	 * @param string $type 'html' or 'pdf' (default is pdf)
770
+	 * @return string
771
+	 * @throws EE_Error
772
+	 * @throws InvalidArgumentException
773
+	 * @throws InvalidDataTypeException
774
+	 * @throws InvalidInterfaceException
775
+	 * @throws ReflectionException
776
+	 */
777
+	public function invoice_url($type = 'html')
778
+	{
779
+		$REG = $this->primary_registration();
780
+		if (! $REG instanceof EE_Registration) {
781
+			return '';
782
+		}
783
+		return $REG->invoice_url($type);
784
+	}
785
+
786
+
787
+	/**
788
+	 * Gets the primary registration only
789
+	 *
790
+	 * @return EE_Base_Class|EE_Registration
791
+	 * @throws EE_Error
792
+	 * @throws InvalidArgumentException
793
+	 * @throws InvalidDataTypeException
794
+	 * @throws InvalidInterfaceException
795
+	 * @throws ReflectionException
796
+	 */
797
+	public function primary_registration()
798
+	{
799
+		$registrations = (array) $this->get_many_related(
800
+			'Registration',
801
+			array(array('REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT))
802
+		);
803
+		foreach ($registrations as $registration) {
804
+			// valid registration that is NOT cancelled or declined ?
805
+			if ($registration instanceof EE_Registration
806
+				&& ! in_array($registration->status_ID(), EEM_Registration::closed_reg_statuses(), true)
807
+			) {
808
+				return $registration;
809
+			}
810
+		}
811
+		// nothing valid found, so just return first thing from array of results
812
+		return reset($registrations);
813
+	}
814
+
815
+
816
+	/**
817
+	 * Gets the URL for viewing the receipt
818
+	 *
819
+	 * @param string $type 'pdf' or 'html' (default is 'html')
820
+	 * @return string
821
+	 * @throws EE_Error
822
+	 * @throws InvalidArgumentException
823
+	 * @throws InvalidDataTypeException
824
+	 * @throws InvalidInterfaceException
825
+	 * @throws ReflectionException
826
+	 */
827
+	public function receipt_url($type = 'html')
828
+	{
829
+		$REG = $this->primary_registration();
830
+		if (! $REG instanceof EE_Registration) {
831
+			return '';
832
+		}
833
+		return $REG->receipt_url($type);
834
+	}
835
+
836
+
837
+	/**
838
+	 * Gets the URL of the thank you page with this registration REG_url_link added as
839
+	 * a query parameter
840
+	 *
841
+	 * @return string
842
+	 * @throws EE_Error
843
+	 * @throws InvalidArgumentException
844
+	 * @throws InvalidDataTypeException
845
+	 * @throws InvalidInterfaceException
846
+	 * @throws ReflectionException
847
+	 */
848
+	public function payment_overview_url()
849
+	{
850
+		$primary_registration = $this->primary_registration();
851
+		return $primary_registration instanceof EE_Registration ? $primary_registration->payment_overview_url() : false;
852
+	}
853
+
854
+
855
+	/**
856
+	 * @return string
857
+	 * @throws EE_Error
858
+	 * @throws InvalidArgumentException
859
+	 * @throws InvalidDataTypeException
860
+	 * @throws InvalidInterfaceException
861
+	 * @throws ReflectionException
862
+	 */
863
+	public function gateway_response_on_transaction()
864
+	{
865
+		$payment = $this->get_first_related('Payment');
866
+		return $payment instanceof EE_Payment ? $payment->gateway_response() : '';
867
+	}
868
+
869
+
870
+	/**
871
+	 * Get the status object of this object
872
+	 *
873
+	 * @return EE_Base_Class|EE_Status
874
+	 * @throws EE_Error
875
+	 * @throws InvalidArgumentException
876
+	 * @throws InvalidDataTypeException
877
+	 * @throws InvalidInterfaceException
878
+	 * @throws ReflectionException
879
+	 */
880
+	public function status_obj()
881
+	{
882
+		return $this->get_first_related('Status');
883
+	}
884
+
885
+
886
+	/**
887
+	 * Gets all the extra meta info on this payment
888
+	 *
889
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
890
+	 * @return EE_Base_Class[]|EE_Extra_Meta
891
+	 * @throws EE_Error
892
+	 * @throws InvalidArgumentException
893
+	 * @throws InvalidDataTypeException
894
+	 * @throws InvalidInterfaceException
895
+	 * @throws ReflectionException
896
+	 */
897
+	public function extra_meta($query_params = array())
898
+	{
899
+		return $this->get_many_related('Extra_Meta', $query_params);
900
+	}
901
+
902
+
903
+	/**
904
+	 * Wrapper for _add_relation_to
905
+	 *
906
+	 * @param EE_Registration $registration
907
+	 * @return EE_Base_Class the relation was added to
908
+	 * @throws EE_Error
909
+	 * @throws InvalidArgumentException
910
+	 * @throws InvalidDataTypeException
911
+	 * @throws InvalidInterfaceException
912
+	 * @throws ReflectionException
913
+	 */
914
+	public function add_registration(EE_Registration $registration)
915
+	{
916
+		return $this->_add_relation_to($registration, 'Registration');
917
+	}
918
+
919
+
920
+	/**
921
+	 * Removes the given registration from being related (even before saving this transaction).
922
+	 * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
923
+	 *
924
+	 * @param int $registration_or_id
925
+	 * @return EE_Base_Class that was removed from being related
926
+	 * @throws EE_Error
927
+	 * @throws InvalidArgumentException
928
+	 * @throws InvalidDataTypeException
929
+	 * @throws InvalidInterfaceException
930
+	 * @throws ReflectionException
931
+	 */
932
+	public function remove_registration_with_id($registration_or_id)
933
+	{
934
+		return $this->_remove_relation_to($registration_or_id, 'Registration');
935
+	}
936
+
937
+
938
+	/**
939
+	 * Gets all the line items which are for ACTUAL items
940
+	 *
941
+	 * @return EE_Line_Item[]
942
+	 * @throws EE_Error
943
+	 * @throws InvalidArgumentException
944
+	 * @throws InvalidDataTypeException
945
+	 * @throws InvalidInterfaceException
946
+	 * @throws ReflectionException
947
+	 */
948
+	public function items_purchased()
949
+	{
950
+		return $this->line_items(array(array('LIN_type' => EEM_Line_Item::type_line_item)));
951
+	}
952
+
953
+
954
+	/**
955
+	 * Wrapper for _add_relation_to
956
+	 *
957
+	 * @param EE_Line_Item $line_item
958
+	 * @return EE_Base_Class the relation was added to
959
+	 * @throws EE_Error
960
+	 * @throws InvalidArgumentException
961
+	 * @throws InvalidDataTypeException
962
+	 * @throws InvalidInterfaceException
963
+	 * @throws ReflectionException
964
+	 */
965
+	public function add_line_item(EE_Line_Item $line_item)
966
+	{
967
+		return $this->_add_relation_to($line_item, 'Line_Item');
968
+	}
969
+
970
+
971
+	/**
972
+	 * Gets ALL the line items related to this transaction (unstructured)
973
+	 *
974
+	 * @param array $query_params
975
+	 * @return EE_Base_Class[]|EE_Line_Item[]
976
+	 * @throws EE_Error
977
+	 * @throws InvalidArgumentException
978
+	 * @throws InvalidDataTypeException
979
+	 * @throws InvalidInterfaceException
980
+	 * @throws ReflectionException
981
+	 */
982
+	public function line_items($query_params = array())
983
+	{
984
+		return $this->get_many_related('Line_Item', $query_params);
985
+	}
986
+
987
+
988
+	/**
989
+	 * Gets all the line items which are taxes on the total
990
+	 *
991
+	 * @return EE_Line_Item[]
992
+	 * @throws EE_Error
993
+	 * @throws InvalidArgumentException
994
+	 * @throws InvalidDataTypeException
995
+	 * @throws InvalidInterfaceException
996
+	 * @throws ReflectionException
997
+	 */
998
+	public function tax_items()
999
+	{
1000
+		return $this->line_items(array(array('LIN_type' => EEM_Line_Item::type_tax)));
1001
+	}
1002
+
1003
+
1004
+	/**
1005
+	 * Gets the total line item (which is a parent of all other related line items,
1006
+	 * meaning it takes them all into account on its total)
1007
+	 *
1008
+	 * @param bool $create_if_not_found
1009
+	 * @return \EE_Line_Item
1010
+	 * @throws EE_Error
1011
+	 * @throws InvalidArgumentException
1012
+	 * @throws InvalidDataTypeException
1013
+	 * @throws InvalidInterfaceException
1014
+	 * @throws ReflectionException
1015
+	 */
1016
+	public function total_line_item($create_if_not_found = true)
1017
+	{
1018
+		$item = $this->get_first_related('Line_Item', array(array('LIN_type' => EEM_Line_Item::type_total)));
1019
+		if (! $item && $create_if_not_found) {
1020
+			$item = EEH_Line_Item::create_total_line_item($this);
1021
+		}
1022
+		return $item;
1023
+	}
1024
+
1025
+
1026
+	/**
1027
+	 * Returns the total amount of tax on this transaction
1028
+	 * (assumes there's only one tax subtotal line item)
1029
+	 *
1030
+	 * @return float
1031
+	 * @throws EE_Error
1032
+	 * @throws InvalidArgumentException
1033
+	 * @throws InvalidDataTypeException
1034
+	 * @throws InvalidInterfaceException
1035
+	 * @throws ReflectionException
1036
+	 */
1037
+	public function tax_total()
1038
+	{
1039
+		$tax_line_item = $this->tax_total_line_item();
1040
+		if ($tax_line_item) {
1041
+			return (float) $tax_line_item->total();
1042
+		}
1043
+		return (float) 0;
1044
+	}
1045
+
1046
+
1047
+	/**
1048
+	 * Gets the tax subtotal line item (assumes there's only one)
1049
+	 *
1050
+	 * @return EE_Line_Item
1051
+	 * @throws EE_Error
1052
+	 * @throws InvalidArgumentException
1053
+	 * @throws InvalidDataTypeException
1054
+	 * @throws InvalidInterfaceException
1055
+	 * @throws ReflectionException
1056
+	 */
1057
+	public function tax_total_line_item()
1058
+	{
1059
+		return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
1060
+	}
1061
+
1062
+
1063
+	/**
1064
+	 * Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
1065
+	 *
1066
+	 * @return EE_Form_Section_Proper
1067
+	 * @throws EE_Error
1068
+	 * @throws InvalidArgumentException
1069
+	 * @throws InvalidDataTypeException
1070
+	 * @throws InvalidInterfaceException
1071
+	 * @throws ReflectionException
1072
+	 */
1073
+	public function billing_info()
1074
+	{
1075
+		$payment_method = $this->payment_method();
1076
+		if (! $payment_method) {
1077
+			EE_Error::add_error(
1078
+				__(
1079
+					'Could not find billing info for transaction because no gateway has been used for it yet',
1080
+					'event_espresso'
1081
+				),
1082
+				__FILE__,
1083
+				__FUNCTION__,
1084
+				__LINE__
1085
+			);
1086
+			return null;
1087
+		}
1088
+		$primary_reg = $this->primary_registration();
1089
+		if (! $primary_reg) {
1090
+			EE_Error::add_error(
1091
+				__(
1092
+					'Cannot get billing info for gateway %s on transaction because no primary registration exists',
1093
+					'event_espresso'
1094
+				),
1095
+				__FILE__,
1096
+				__FUNCTION__,
1097
+				__LINE__
1098
+			);
1099
+			return null;
1100
+		}
1101
+		$attendee = $primary_reg->attendee();
1102
+		if (! $attendee) {
1103
+			EE_Error::add_error(
1104
+				__(
1105
+					'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
1106
+					'event_espresso'
1107
+				),
1108
+				__FILE__,
1109
+				__FUNCTION__,
1110
+				__LINE__
1111
+			);
1112
+			return null;
1113
+		}
1114
+		return $attendee->billing_info_for_payment_method($payment_method);
1115
+	}
1116
+
1117
+
1118
+	/**
1119
+	 * Gets PMD_ID
1120
+	 *
1121
+	 * @return int
1122
+	 * @throws EE_Error
1123
+	 * @throws InvalidArgumentException
1124
+	 * @throws InvalidDataTypeException
1125
+	 * @throws InvalidInterfaceException
1126
+	 * @throws ReflectionException
1127
+	 */
1128
+	public function payment_method_ID()
1129
+	{
1130
+		return $this->get('PMD_ID');
1131
+	}
1132
+
1133
+
1134
+	/**
1135
+	 * Sets PMD_ID
1136
+	 *
1137
+	 * @param int $PMD_ID
1138
+	 * @throws EE_Error
1139
+	 * @throws InvalidArgumentException
1140
+	 * @throws InvalidDataTypeException
1141
+	 * @throws InvalidInterfaceException
1142
+	 * @throws ReflectionException
1143
+	 */
1144
+	public function set_payment_method_ID($PMD_ID)
1145
+	{
1146
+		$this->set('PMD_ID', $PMD_ID);
1147
+	}
1148
+
1149
+
1150
+	/**
1151
+	 * Gets the last-used payment method on this transaction
1152
+	 * (we COULD just use the last-made payment, but some payment methods, namely
1153
+	 * offline ones, dont' create payments)
1154
+	 *
1155
+	 * @return EE_Payment_Method
1156
+	 * @throws EE_Error
1157
+	 * @throws InvalidArgumentException
1158
+	 * @throws InvalidDataTypeException
1159
+	 * @throws InvalidInterfaceException
1160
+	 * @throws ReflectionException
1161
+	 */
1162
+	public function payment_method()
1163
+	{
1164
+		$pm = $this->get_first_related('Payment_Method');
1165
+		if ($pm instanceof EE_Payment_Method) {
1166
+			return $pm;
1167
+		}
1168
+		$last_payment = $this->last_payment();
1169
+		if ($last_payment instanceof EE_Payment && $last_payment->payment_method()) {
1170
+			return $last_payment->payment_method();
1171
+		}
1172
+		return null;
1173
+	}
1174
+
1175
+
1176
+	/**
1177
+	 * Gets the last payment made
1178
+	 *
1179
+	 * @return EE_Base_Class|EE_Payment
1180
+	 * @throws EE_Error
1181
+	 * @throws InvalidArgumentException
1182
+	 * @throws InvalidDataTypeException
1183
+	 * @throws InvalidInterfaceException
1184
+	 * @throws ReflectionException
1185
+	 */
1186
+	public function last_payment()
1187
+	{
1188
+		return $this->get_first_related('Payment', array('order_by' => array('PAY_ID' => 'desc')));
1189
+	}
1190
+
1191
+
1192
+	/**
1193
+	 * Gets all the line items which are unrelated to tickets on this transaction
1194
+	 *
1195
+	 * @return EE_Line_Item[]
1196
+	 * @throws EE_Error
1197
+	 * @throws InvalidArgumentException
1198
+	 * @throws InvalidDataTypeException
1199
+	 * @throws InvalidInterfaceException
1200
+	 * @throws ReflectionException
1201
+	 */
1202
+	public function non_ticket_line_items()
1203
+	{
1204
+		return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction($this->ID());
1205
+	}
1206
+
1207
+
1208
+	/**
1209
+	 * possibly toggles TXN status
1210
+	 *
1211
+	 * @param  boolean $update whether to save the TXN
1212
+	 * @return bool whether the TXN was saved
1213
+	 * @throws EE_Error
1214
+	 * @throws InvalidArgumentException
1215
+	 * @throws InvalidDataTypeException
1216
+	 * @throws InvalidInterfaceException
1217
+	 * @throws ReflectionException
1218
+	 * @throws RuntimeException
1219
+	 */
1220
+	public function update_status_based_on_total_paid($update = true)
1221
+	{
1222
+		// set transaction status based on comparison of TXN_paid vs TXN_total
1223
+		if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
1224
+			$new_txn_status = EEM_Transaction::overpaid_status_code;
1225
+		} elseif (EEH_Money::compare_floats($this->paid(), $this->total())) {
1226
+			$new_txn_status = EEM_Transaction::complete_status_code;
1227
+		} elseif (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
1228
+			$new_txn_status = EEM_Transaction::incomplete_status_code;
1229
+		} else {
1230
+			throw new RuntimeException(
1231
+				__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
1232
+			);
1233
+		}
1234
+		if ($new_txn_status !== $this->status_ID()) {
1235
+			$this->set_status($new_txn_status);
1236
+			if ($update) {
1237
+				return $this->save() ? true : false;
1238
+			}
1239
+		}
1240
+		return false;
1241
+	}
1242
+
1243
+
1244
+	/**
1245
+	 * Updates the transaction's status and total_paid based on all the payments
1246
+	 * that apply to it
1247
+	 *
1248
+	 * @deprecated
1249
+	 * @return array|bool
1250
+	 * @throws EE_Error
1251
+	 * @throws InvalidArgumentException
1252
+	 * @throws ReflectionException
1253
+	 * @throws InvalidDataTypeException
1254
+	 * @throws InvalidInterfaceException
1255
+	 */
1256
+	public function update_based_on_payments()
1257
+	{
1258
+		EE_Error::doing_it_wrong(
1259
+			__CLASS__ . '::' . __FUNCTION__,
1260
+			sprintf(
1261
+				__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1262
+				'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
1263
+			),
1264
+			'4.6.0'
1265
+		);
1266
+		/** @type EE_Transaction_Processor $transaction_processor */
1267
+		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1268
+		return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
1269
+	}
1270
+
1271
+
1272
+	/**
1273
+	 * @return string
1274
+	 */
1275
+	public function old_txn_status()
1276
+	{
1277
+		return $this->_old_txn_status;
1278
+	}
1279
+
1280
+
1281
+	/**
1282
+	 * @param string $old_txn_status
1283
+	 */
1284
+	public function set_old_txn_status($old_txn_status)
1285
+	{
1286
+		// only set the first time
1287
+		if ($this->_old_txn_status === null) {
1288
+			$this->_old_txn_status = $old_txn_status;
1289
+		}
1290
+	}
1291
+
1292
+
1293
+	/**
1294
+	 * reg_status_updated
1295
+	 *
1296
+	 * @return bool
1297
+	 * @throws EE_Error
1298
+	 * @throws InvalidArgumentException
1299
+	 * @throws InvalidDataTypeException
1300
+	 * @throws InvalidInterfaceException
1301
+	 * @throws ReflectionException
1302
+	 */
1303
+	public function txn_status_updated()
1304
+	{
1305
+		return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null;
1306
+	}
1307
+
1308
+
1309
+	/**
1310
+	 * _reg_steps_completed
1311
+	 * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1312
+	 * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1313
+	 * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1314
+	 *
1315
+	 * @param string $reg_step_slug
1316
+	 * @param bool   $check_all
1317
+	 * @return bool|int
1318
+	 * @throws EE_Error
1319
+	 * @throws InvalidArgumentException
1320
+	 * @throws InvalidDataTypeException
1321
+	 * @throws InvalidInterfaceException
1322
+	 * @throws ReflectionException
1323
+	 */
1324
+	private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1325
+	{
1326
+		$reg_steps = $this->reg_steps();
1327
+		if (! is_array($reg_steps) || empty($reg_steps)) {
1328
+			return false;
1329
+		}
1330
+		// loop thru reg steps array)
1331
+		foreach ($reg_steps as $slug => $reg_step_completed) {
1332
+			// if NOT checking ALL steps (only checking one step)
1333
+			if (! $check_all) {
1334
+				// and this is the one
1335
+				if ($slug === $reg_step_slug) {
1336
+					return $reg_step_completed;
1337
+				}
1338
+				// skip to next reg step in loop
1339
+				continue;
1340
+			}
1341
+			// $check_all must be true, else we would never have gotten to this point
1342
+			if ($slug === $reg_step_slug) {
1343
+				// if we reach this point, then we are testing either:
1344
+				// all_reg_steps_completed_except() or
1345
+				// all_reg_steps_completed_except_final_step(),
1346
+				// and since this is the reg step EXCEPTION being tested
1347
+				// we want to return true (yes true) if this reg step is NOT completed
1348
+				// ie: "is everything completed except the final step?"
1349
+				// "that is correct... the final step is not completed, but all others are."
1350
+				return $reg_step_completed !== true;
1351
+			}
1352
+			if ($reg_step_completed !== true) {
1353
+				// if any reg step is NOT completed, then ALL steps are not completed
1354
+				return false;
1355
+			}
1356
+		}
1357
+		return true;
1358
+	}
1359
+
1360
+
1361
+	/**
1362
+	 * all_reg_steps_completed
1363
+	 * returns:
1364
+	 *    true if ALL reg steps have been marked as completed
1365
+	 *        or false if any step is not completed
1366
+	 *
1367
+	 * @return bool
1368
+	 * @throws EE_Error
1369
+	 * @throws InvalidArgumentException
1370
+	 * @throws InvalidDataTypeException
1371
+	 * @throws InvalidInterfaceException
1372
+	 * @throws ReflectionException
1373
+	 */
1374
+	public function all_reg_steps_completed()
1375
+	{
1376
+		return $this->_reg_steps_completed();
1377
+	}
1378
+
1379
+
1380
+	/**
1381
+	 * all_reg_steps_completed_except
1382
+	 * returns:
1383
+	 *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1384
+	 *        or false if any other step is not completed
1385
+	 *        or false if ALL steps are completed including the exception you are testing !!!
1386
+	 *
1387
+	 * @param string $exception
1388
+	 * @return bool
1389
+	 * @throws EE_Error
1390
+	 * @throws InvalidArgumentException
1391
+	 * @throws InvalidDataTypeException
1392
+	 * @throws InvalidInterfaceException
1393
+	 * @throws ReflectionException
1394
+	 */
1395
+	public function all_reg_steps_completed_except($exception = '')
1396
+	{
1397
+		return $this->_reg_steps_completed($exception);
1398
+	}
1399
+
1400
+
1401
+	/**
1402
+	 * all_reg_steps_completed_except
1403
+	 * returns:
1404
+	 *        true if ALL reg steps, except the final step, have been marked as completed
1405
+	 *        or false if any step is not completed
1406
+	 *    or false if ALL steps are completed including the final step !!!
1407
+	 *
1408
+	 * @return bool
1409
+	 * @throws EE_Error
1410
+	 * @throws InvalidArgumentException
1411
+	 * @throws InvalidDataTypeException
1412
+	 * @throws InvalidInterfaceException
1413
+	 * @throws ReflectionException
1414
+	 */
1415
+	public function all_reg_steps_completed_except_final_step()
1416
+	{
1417
+		return $this->_reg_steps_completed('finalize_registration');
1418
+	}
1419
+
1420
+
1421
+	/**
1422
+	 * reg_step_completed
1423
+	 * returns:
1424
+	 *    true if a specific reg step has been marked as completed
1425
+	 *    a Unix timestamp if it has been initialized but not yet completed,
1426
+	 *    or false if it has not yet been initialized
1427
+	 *
1428
+	 * @param string $reg_step_slug
1429
+	 * @return bool|int
1430
+	 * @throws EE_Error
1431
+	 * @throws InvalidArgumentException
1432
+	 * @throws InvalidDataTypeException
1433
+	 * @throws InvalidInterfaceException
1434
+	 * @throws ReflectionException
1435
+	 */
1436
+	public function reg_step_completed($reg_step_slug)
1437
+	{
1438
+		return $this->_reg_steps_completed($reg_step_slug, false);
1439
+	}
1440
+
1441
+
1442
+	/**
1443
+	 * completed_final_reg_step
1444
+	 * returns:
1445
+	 *    true if the finalize_registration reg step has been marked as completed
1446
+	 *    a Unix timestamp if it has been initialized but not yet completed,
1447
+	 *    or false if it has not yet been initialized
1448
+	 *
1449
+	 * @return bool|int
1450
+	 * @throws EE_Error
1451
+	 * @throws InvalidArgumentException
1452
+	 * @throws InvalidDataTypeException
1453
+	 * @throws InvalidInterfaceException
1454
+	 * @throws ReflectionException
1455
+	 */
1456
+	public function final_reg_step_completed()
1457
+	{
1458
+		return $this->_reg_steps_completed('finalize_registration', false);
1459
+	}
1460
+
1461
+
1462
+	/**
1463
+	 * set_reg_step_initiated
1464
+	 * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1465
+	 *
1466
+	 * @param string $reg_step_slug
1467
+	 * @return boolean
1468
+	 * @throws EE_Error
1469
+	 * @throws InvalidArgumentException
1470
+	 * @throws InvalidDataTypeException
1471
+	 * @throws InvalidInterfaceException
1472
+	 * @throws ReflectionException
1473
+	 */
1474
+	public function set_reg_step_initiated($reg_step_slug)
1475
+	{
1476
+		return $this->_set_reg_step_completed_status($reg_step_slug, time());
1477
+	}
1478
+
1479
+
1480
+	/**
1481
+	 * set_reg_step_completed
1482
+	 * given a valid TXN_reg_step, this sets the step as completed
1483
+	 *
1484
+	 * @param string $reg_step_slug
1485
+	 * @return boolean
1486
+	 * @throws EE_Error
1487
+	 * @throws InvalidArgumentException
1488
+	 * @throws InvalidDataTypeException
1489
+	 * @throws InvalidInterfaceException
1490
+	 * @throws ReflectionException
1491
+	 */
1492
+	public function set_reg_step_completed($reg_step_slug)
1493
+	{
1494
+		return $this->_set_reg_step_completed_status($reg_step_slug, true);
1495
+	}
1496
+
1497
+
1498
+	/**
1499
+	 * set_reg_step_completed
1500
+	 * given a valid TXN_reg_step slug, this sets the step as NOT completed
1501
+	 *
1502
+	 * @param string $reg_step_slug
1503
+	 * @return boolean
1504
+	 * @throws EE_Error
1505
+	 * @throws InvalidArgumentException
1506
+	 * @throws InvalidDataTypeException
1507
+	 * @throws InvalidInterfaceException
1508
+	 * @throws ReflectionException
1509
+	 */
1510
+	public function set_reg_step_not_completed($reg_step_slug)
1511
+	{
1512
+		return $this->_set_reg_step_completed_status($reg_step_slug, false);
1513
+	}
1514
+
1515
+
1516
+	/**
1517
+	 * set_reg_step_completed
1518
+	 * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1519
+	 *
1520
+	 * @param  string      $reg_step_slug
1521
+	 * @param  boolean|int $status
1522
+	 * @return boolean
1523
+	 * @throws EE_Error
1524
+	 * @throws InvalidArgumentException
1525
+	 * @throws InvalidDataTypeException
1526
+	 * @throws InvalidInterfaceException
1527
+	 * @throws ReflectionException
1528
+	 */
1529
+	private function _set_reg_step_completed_status($reg_step_slug, $status)
1530
+	{
1531
+		// validate status
1532
+		$status = is_bool($status) || is_int($status) ? $status : false;
1533
+		// get reg steps array
1534
+		$txn_reg_steps = $this->reg_steps();
1535
+		// if reg step does NOT exist
1536
+		if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1537
+			return false;
1538
+		}
1539
+		// if  we're trying to complete a step that is already completed
1540
+		if ($txn_reg_steps[ $reg_step_slug ] === true) {
1541
+			return true;
1542
+		}
1543
+		// if  we're trying to complete a step that hasn't even started
1544
+		if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1545
+			return false;
1546
+		}
1547
+		// if current status value matches the incoming value (no change)
1548
+		// type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1549
+		if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1550
+			// this will happen in cases where multiple AJAX requests occur during the same step
1551
+			return true;
1552
+		}
1553
+		// if we're trying to set a start time, but it has already been set...
1554
+		if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1555
+			// skip the update below, but don't return FALSE so that errors won't be displayed
1556
+			return true;
1557
+		}
1558
+		// update completed status
1559
+		$txn_reg_steps[ $reg_step_slug ] = $status;
1560
+		$this->set_reg_steps($txn_reg_steps);
1561
+		$this->save();
1562
+		return true;
1563
+	}
1564
+
1565
+
1566
+	/**
1567
+	 * remove_reg_step
1568
+	 * given a valid TXN_reg_step slug, this will remove (unset)
1569
+	 * the reg step from the TXN reg step array
1570
+	 *
1571
+	 * @param string $reg_step_slug
1572
+	 * @return void
1573
+	 * @throws EE_Error
1574
+	 * @throws InvalidArgumentException
1575
+	 * @throws InvalidDataTypeException
1576
+	 * @throws InvalidInterfaceException
1577
+	 * @throws ReflectionException
1578
+	 */
1579
+	public function remove_reg_step($reg_step_slug)
1580
+	{
1581
+		// get reg steps array
1582
+		$txn_reg_steps = $this->reg_steps();
1583
+		unset($txn_reg_steps[ $reg_step_slug ]);
1584
+		$this->set_reg_steps($txn_reg_steps);
1585
+	}
1586
+
1587
+
1588
+	/**
1589
+	 * toggle_failed_transaction_status
1590
+	 * upgrades a TXNs status from failed to abandoned,
1591
+	 * meaning that contact information has been captured for at least one registrant
1592
+	 *
1593
+	 * @param bool $save
1594
+	 * @return bool
1595
+	 * @throws EE_Error
1596
+	 * @throws InvalidArgumentException
1597
+	 * @throws InvalidDataTypeException
1598
+	 * @throws InvalidInterfaceException
1599
+	 * @throws ReflectionException
1600
+	 */
1601
+	public function toggle_failed_transaction_status($save = true)
1602
+	{
1603
+		// if TXN status is still set as "failed"...
1604
+		if ($this->status_ID() === EEM_Transaction::failed_status_code) {
1605
+			$this->set_status(EEM_Transaction::abandoned_status_code);
1606
+			if ($save) {
1607
+				$this->save();
1608
+			}
1609
+			return true;
1610
+		}
1611
+		return false;
1612
+	}
1613
+
1614
+
1615
+	/**
1616
+	 * toggle_abandoned_transaction_status
1617
+	 * upgrades a TXNs status from failed or abandoned to incomplete
1618
+	 *
1619
+	 * @return bool
1620
+	 * @throws EE_Error
1621
+	 * @throws InvalidArgumentException
1622
+	 * @throws InvalidDataTypeException
1623
+	 * @throws InvalidInterfaceException
1624
+	 * @throws ReflectionException
1625
+	 */
1626
+	public function toggle_abandoned_transaction_status()
1627
+	{
1628
+		// if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1629
+		$txn_status = $this->status_ID();
1630
+		if ($txn_status === EEM_Transaction::failed_status_code
1631
+			|| $txn_status === EEM_Transaction::abandoned_status_code
1632
+		) {
1633
+			// if a contact record for the primary registrant has been created
1634
+			if ($this->primary_registration() instanceof EE_Registration
1635
+				&& $this->primary_registration()->attendee() instanceof EE_Attendee
1636
+			) {
1637
+				$this->set_status(EEM_Transaction::incomplete_status_code);
1638
+			} else {
1639
+				// no contact record? yer abandoned!
1640
+				$this->set_status(EEM_Transaction::abandoned_status_code);
1641
+			}
1642
+			return true;
1643
+		}
1644
+		return false;
1645
+	}
1646
+
1647
+
1648
+	/**
1649
+	 * checks if an Abandoned TXN has any related payments, and if so,
1650
+	 * updates the TXN status based on the amount paid
1651
+	 *
1652
+	 * @throws EE_Error
1653
+	 * @throws InvalidDataTypeException
1654
+	 * @throws InvalidInterfaceException
1655
+	 * @throws InvalidArgumentException
1656
+	 * @throws RuntimeException
1657
+	 * @throws ReflectionException
1658
+	 */
1659
+	public function verify_abandoned_transaction_status()
1660
+	{
1661
+		if ($this->status_ID() !== EEM_Transaction::abandoned_status_code) {
1662
+			return;
1663
+		}
1664
+		$payments = $this->get_many_related('Payment');
1665
+		if (! empty($payments)) {
1666
+			foreach ($payments as $payment) {
1667
+				if ($payment instanceof EE_Payment) {
1668
+					// kk this TXN should NOT be abandoned
1669
+					$this->update_status_based_on_total_paid();
1670
+					if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1671
+						EE_Error::add_attention(
1672
+							sprintf(
1673
+								esc_html__(
1674
+									'The status for Transaction #%1$d has been updated from "Abandoned" to "%2$s", because at least one payment has been made towards it. If the payment appears in the "Payment Details" table below, you may need to edit its status and/or other details as well.',
1675
+									'event_espresso'
1676
+								),
1677
+								$this->ID(),
1678
+								$this->pretty_status()
1679
+							)
1680
+						);
1681
+					}
1682
+					// get final reg step status
1683
+					$finalized = $this->final_reg_step_completed();
1684
+					// if the 'finalize_registration' step has been initiated (has a timestamp)
1685
+					// but has not yet been fully completed (TRUE)
1686
+					if (is_int($finalized) && $finalized !== false && $finalized !== true) {
1687
+						$this->set_reg_step_completed('finalize_registration');
1688
+						$this->save();
1689
+					}
1690
+				}
1691
+			}
1692
+		}
1693
+	}
1694
+
1695
+
1696
+	/**
1697
+	 * @since 4.10.4.p
1698
+	 * @throws EE_Error
1699
+	 * @throws InvalidArgumentException
1700
+	 * @throws InvalidDataTypeException
1701
+	 * @throws InvalidInterfaceException
1702
+	 * @throws ReflectionException
1703
+	 * @throws RuntimeException
1704
+	 */
1705
+	public function recalculateLineItems()
1706
+	{
1707
+		$total_line_item = $this->total_line_item(false);
1708
+		if ($total_line_item instanceof EE_Line_Item) {
1709
+			EEH_Line_Item::resetIsTaxableForTickets($total_line_item);
1710
+			return EEH_Line_Item::apply_taxes($total_line_item, true);
1711
+		}
1712
+		return false;
1713
+	}
1714 1714
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Datetime.class.php 2 patches
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -495,7 +495,7 @@  discard block
 block discarded – undo
495 495
             $date_or_time,
496 496
             $echo
497 497
         );
498
-        if (! $echo) {
498
+        if ( ! $echo) {
499 499
             return $dtt;
500 500
         }
501 501
         return '';
@@ -597,7 +597,7 @@  discard block
 block discarded – undo
597 597
             '&nbsp;',
598 598
             $this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
599 599
         );
600
-        return $start !== $end ? $start . $conjunction . $end : $start;
600
+        return $start !== $end ? $start.$conjunction.$end : $start;
601 601
     }
602 602
 
603 603
 
@@ -705,7 +705,7 @@  discard block
 block discarded – undo
705 705
             '&nbsp;',
706 706
             $this->get_i18n_datetime('DTT_EVT_end', $tm_format)
707 707
         );
708
-        return $start !== $end ? $start . $conjunction . $end : $start;
708
+        return $start !== $end ? $start.$conjunction.$end : $start;
709 709
     }
710 710
 
711 711
 
@@ -750,7 +750,7 @@  discard block
 block discarded – undo
750 750
     ) {
751 751
         $dt_format = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
752 752
         $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
753
-        $full_format = $dt_format . $separator . $tm_format;
753
+        $full_format = $dt_format.$separator.$tm_format;
754 754
         // the range output depends on various conditions
755 755
         switch (true) {
756 756
             // start date timestamp and end date timestamp are the same.
@@ -991,7 +991,7 @@  discard block
 block discarded – undo
991 991
         // tickets remaining available for purchase
992 992
         // no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
993 993
         $dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
994
-        if (! $consider_tickets) {
994
+        if ( ! $consider_tickets) {
995 995
             return $dtt_remaining;
996 996
         }
997 997
         $tickets_remaining = $this->tickets_remaining();
@@ -1015,7 +1015,7 @@  discard block
 block discarded – undo
1015 1015
     {
1016 1016
         $sum = 0;
1017 1017
         $tickets = $this->tickets($query_params);
1018
-        if (! empty($tickets)) {
1018
+        if ( ! empty($tickets)) {
1019 1019
             foreach ($tickets as $ticket) {
1020 1020
                 if ($ticket instanceof EE_Ticket) {
1021 1021
                     // get the actual amount of tickets that can be sold
@@ -1166,20 +1166,20 @@  discard block
 block discarded – undo
1166 1166
     {
1167 1167
         if ($use_dtt_name) {
1168 1168
             $dtt_name = $this->name();
1169
-            if (! empty($dtt_name)) {
1169
+            if ( ! empty($dtt_name)) {
1170 1170
                 return $dtt_name;
1171 1171
             }
1172 1172
         }
1173 1173
         // first condition is to see if the months are different
1174 1174
         if (date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1175 1175
         ) {
1176
-            $display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1176
+            $display_date = $this->start_date('M j\, Y g:i a').' - '.$this->end_date('M j\, Y g:i a');
1177 1177
             // next condition is if its the same month but different day
1178 1178
         } else {
1179 1179
             if (date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1180 1180
                 && date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1181 1181
             ) {
1182
-                $display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1182
+                $display_date = $this->start_date('M j\, g:i a').' - '.$this->end_date('M j\, g:i a Y');
1183 1183
             } else {
1184 1184
                 $display_date = $this->start_date('F j\, Y')
1185 1185
                                 . ' @ '
Please login to merge, or discard this patch.
Indentation   +1402 added lines, -1402 removed lines patch added patch discarded remove patch
@@ -13,1410 +13,1410 @@
 block discarded – undo
13 13
 class EE_Datetime extends EE_Soft_Delete_Base_Class
14 14
 {
15 15
 
16
-    /**
17
-     * constant used by get_active_status, indicates datetime has no more available spaces
18
-     */
19
-    const sold_out = 'DTS';
20
-
21
-    /**
22
-     * constant used by get_active_status, indicating datetime is still active (even is not over, can be registered-for)
23
-     */
24
-    const active = 'DTA';
25
-
26
-    /**
27
-     * constant used by get_active_status, indicating the datetime cannot be used for registrations yet, but has not
28
-     * expired
29
-     */
30
-    const upcoming = 'DTU';
31
-
32
-    /**
33
-     * Datetime is postponed
34
-     */
35
-    const postponed = 'DTP';
36
-
37
-    /**
38
-     * Datetime is cancelled
39
-     */
40
-    const cancelled = 'DTC';
41
-
42
-    /**
43
-     * constant used by get_active_status, indicates datetime has expired (event is over)
44
-     */
45
-    const expired = 'DTE';
46
-
47
-    /**
48
-     * constant used in various places indicating that an event is INACTIVE (not yet ready to be published)
49
-     */
50
-    const inactive = 'DTI';
51
-
52
-
53
-    /**
54
-     * @param array  $props_n_values    incoming values
55
-     * @param string $timezone          incoming timezone (if not set the timezone set for the website will be used.)
56
-     * @param array  $date_formats      incoming date_formats in an array where the first value is the date_format
57
-     *                                  and the second value is the time format
58
-     * @return EE_Datetime
59
-     * @throws ReflectionException
60
-     * @throws InvalidArgumentException
61
-     * @throws InvalidInterfaceException
62
-     * @throws InvalidDataTypeException
63
-     * @throws EE_Error
64
-     */
65
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
66
-    {
67
-        $has_object = parent::_check_for_object(
68
-            $props_n_values,
69
-            __CLASS__,
70
-            $timezone,
71
-            $date_formats
72
-        );
73
-        return $has_object
74
-            ? $has_object
75
-            : new self($props_n_values, false, $timezone, $date_formats);
76
-    }
77
-
78
-
79
-    /**
80
-     * @param array  $props_n_values  incoming values from the database
81
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
82
-     *                                the website will be used.
83
-     * @return EE_Datetime
84
-     * @throws ReflectionException
85
-     * @throws InvalidArgumentException
86
-     * @throws InvalidInterfaceException
87
-     * @throws InvalidDataTypeException
88
-     * @throws EE_Error
89
-     */
90
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
91
-    {
92
-        return new self($props_n_values, true, $timezone);
93
-    }
94
-
95
-
96
-    /**
97
-     * @param $name
98
-     * @throws ReflectionException
99
-     * @throws InvalidArgumentException
100
-     * @throws InvalidInterfaceException
101
-     * @throws InvalidDataTypeException
102
-     * @throws EE_Error
103
-     */
104
-    public function set_name($name)
105
-    {
106
-        $this->set('DTT_name', $name);
107
-    }
108
-
109
-
110
-    /**
111
-     * @param $description
112
-     * @throws ReflectionException
113
-     * @throws InvalidArgumentException
114
-     * @throws InvalidInterfaceException
115
-     * @throws InvalidDataTypeException
116
-     * @throws EE_Error
117
-     */
118
-    public function set_description($description)
119
-    {
120
-        $this->set('DTT_description', $description);
121
-    }
122
-
123
-
124
-    /**
125
-     * Set event start date
126
-     * set the start date for an event
127
-     *
128
-     * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
129
-     * @throws ReflectionException
130
-     * @throws InvalidArgumentException
131
-     * @throws InvalidInterfaceException
132
-     * @throws InvalidDataTypeException
133
-     * @throws EE_Error
134
-     */
135
-    public function set_start_date($date)
136
-    {
137
-        $this->_set_date_for($date, 'DTT_EVT_start');
138
-    }
139
-
140
-
141
-    /**
142
-     * Set event start time
143
-     * set the start time for an event
144
-     *
145
-     * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
146
-     * @throws ReflectionException
147
-     * @throws InvalidArgumentException
148
-     * @throws InvalidInterfaceException
149
-     * @throws InvalidDataTypeException
150
-     * @throws EE_Error
151
-     */
152
-    public function set_start_time($time)
153
-    {
154
-        $this->_set_time_for($time, 'DTT_EVT_start');
155
-    }
156
-
157
-
158
-    /**
159
-     * Set event end date
160
-     * set the end date for an event
161
-     *
162
-     * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
163
-     * @throws ReflectionException
164
-     * @throws InvalidArgumentException
165
-     * @throws InvalidInterfaceException
166
-     * @throws InvalidDataTypeException
167
-     * @throws EE_Error
168
-     */
169
-    public function set_end_date($date)
170
-    {
171
-        $this->_set_date_for($date, 'DTT_EVT_end');
172
-    }
173
-
174
-
175
-    /**
176
-     * Set event end time
177
-     * set the end time for an event
178
-     *
179
-     * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
180
-     * @throws ReflectionException
181
-     * @throws InvalidArgumentException
182
-     * @throws InvalidInterfaceException
183
-     * @throws InvalidDataTypeException
184
-     * @throws EE_Error
185
-     */
186
-    public function set_end_time($time)
187
-    {
188
-        $this->_set_time_for($time, 'DTT_EVT_end');
189
-    }
190
-
191
-
192
-    /**
193
-     * Set registration limit
194
-     * set the maximum number of attendees that can be registered for this datetime slot
195
-     *
196
-     * @param int $reg_limit
197
-     * @throws ReflectionException
198
-     * @throws InvalidArgumentException
199
-     * @throws InvalidInterfaceException
200
-     * @throws InvalidDataTypeException
201
-     * @throws EE_Error
202
-     */
203
-    public function set_reg_limit($reg_limit)
204
-    {
205
-        $this->set('DTT_reg_limit', $reg_limit);
206
-    }
207
-
208
-
209
-    /**
210
-     * get the number of tickets sold for this datetime slot
211
-     *
212
-     * @return mixed int on success, FALSE on fail
213
-     * @throws ReflectionException
214
-     * @throws InvalidArgumentException
215
-     * @throws InvalidInterfaceException
216
-     * @throws InvalidDataTypeException
217
-     * @throws EE_Error
218
-     */
219
-    public function sold()
220
-    {
221
-        return $this->get_raw('DTT_sold');
222
-    }
223
-
224
-
225
-    /**
226
-     * @param int $sold
227
-     * @throws ReflectionException
228
-     * @throws InvalidArgumentException
229
-     * @throws InvalidInterfaceException
230
-     * @throws InvalidDataTypeException
231
-     * @throws EE_Error
232
-     */
233
-    public function set_sold($sold)
234
-    {
235
-        // sold can not go below zero
236
-        $sold = max(0, $sold);
237
-        $this->set('DTT_sold', $sold);
238
-    }
239
-
240
-
241
-    /**
242
-     * Increments sold by amount passed by $qty, and persists it immediately to the database.
243
-     * Simultaneously decreases the reserved count, unless $also_decrease_reserved is false.
244
-     *
245
-     * @param int $qty
246
-     * @param boolean $also_decrease_reserved
247
-     * @return boolean indicating success
248
-     * @throws ReflectionException
249
-     * @throws InvalidArgumentException
250
-     * @throws InvalidInterfaceException
251
-     * @throws InvalidDataTypeException
252
-     * @throws EE_Error
253
-     */
254
-    public function increaseSold($qty = 1, $also_decrease_reserved = true)
255
-    {
256
-        $qty = absint($qty);
257
-        if ($also_decrease_reserved) {
258
-            $success = $this->adjustNumericFieldsInDb(
259
-                [
260
-                    'DTT_reserved' => $qty * -1,
261
-                    'DTT_sold' => $qty
262
-                ]
263
-            );
264
-        } else {
265
-            $success = $this->adjustNumericFieldsInDb(
266
-                [
267
-                    'DTT_sold' => $qty
268
-                ]
269
-            );
270
-        }
271
-
272
-        do_action(
273
-            'AHEE__EE_Datetime__increase_sold',
274
-            $this,
275
-            $qty,
276
-            $this->sold(),
277
-            $success
278
-        );
279
-        return $success;
280
-    }
281
-
282
-
283
-    /**
284
-     * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
285
-     * to save afterwards.)
286
-     *
287
-     * @param int $qty
288
-     * @return boolean indicating success
289
-     * @throws ReflectionException
290
-     * @throws InvalidArgumentException
291
-     * @throws InvalidInterfaceException
292
-     * @throws InvalidDataTypeException
293
-     * @throws EE_Error
294
-     */
295
-    public function decreaseSold($qty = 1)
296
-    {
297
-        $qty = absint($qty);
298
-        $success = $this->adjustNumericFieldsInDb(
299
-            [
300
-                'DTT_sold' => $qty * -1
301
-            ]
302
-        );
303
-        do_action(
304
-            'AHEE__EE_Datetime__decrease_sold',
305
-            $this,
306
-            $qty,
307
-            $this->sold(),
308
-            $success
309
-        );
310
-        return $success;
311
-    }
312
-
313
-
314
-    /**
315
-     * Gets qty of reserved tickets for this datetime
316
-     *
317
-     * @return int
318
-     * @throws ReflectionException
319
-     * @throws InvalidArgumentException
320
-     * @throws InvalidInterfaceException
321
-     * @throws InvalidDataTypeException
322
-     * @throws EE_Error
323
-     */
324
-    public function reserved()
325
-    {
326
-        return $this->get_raw('DTT_reserved');
327
-    }
328
-
329
-
330
-    /**
331
-     * Sets qty of reserved tickets for this datetime
332
-     *
333
-     * @param int $reserved
334
-     * @throws ReflectionException
335
-     * @throws InvalidArgumentException
336
-     * @throws InvalidInterfaceException
337
-     * @throws InvalidDataTypeException
338
-     * @throws EE_Error
339
-     */
340
-    public function set_reserved($reserved)
341
-    {
342
-        // reserved can not go below zero
343
-        $reserved = max(0, (int) $reserved);
344
-        $this->set('DTT_reserved', $reserved);
345
-    }
346
-
347
-
348
-    /**
349
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
350
-     *
351
-     * @param int $qty
352
-     * @return boolean indicating success
353
-     * @throws ReflectionException
354
-     * @throws InvalidArgumentException
355
-     * @throws InvalidInterfaceException
356
-     * @throws InvalidDataTypeException
357
-     * @throws EE_Error
358
-     */
359
-    public function increaseReserved($qty = 1)
360
-    {
361
-        $qty = absint($qty);
362
-        $success = $this->incrementFieldConditionallyInDb(
363
-            'DTT_reserved',
364
-            'DTT_sold',
365
-            'DTT_reg_limit',
366
-            $qty
367
-        );
368
-        do_action(
369
-            'AHEE__EE_Datetime__increase_reserved',
370
-            $this,
371
-            $qty,
372
-            $this->reserved(),
373
-            $success
374
-        );
375
-        return $success;
376
-    }
377
-
378
-
379
-    /**
380
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
381
-     *
382
-     * @param int $qty
383
-     * @return boolean indicating success
384
-     * @throws ReflectionException
385
-     * @throws InvalidArgumentException
386
-     * @throws InvalidInterfaceException
387
-     * @throws InvalidDataTypeException
388
-     * @throws EE_Error
389
-     */
390
-    public function decreaseReserved($qty = 1)
391
-    {
392
-        $qty = absint($qty);
393
-        $success = $this->adjustNumericFieldsInDb(
394
-            [
395
-                'DTT_reserved' => $qty * -1
396
-            ]
397
-        );
398
-        do_action(
399
-            'AHEE__EE_Datetime__decrease_reserved',
400
-            $this,
401
-            $qty,
402
-            $this->reserved(),
403
-            $success
404
-        );
405
-        return $success;
406
-    }
407
-
408
-
409
-    /**
410
-     * total sold and reserved tickets
411
-     *
412
-     * @return int
413
-     * @throws ReflectionException
414
-     * @throws InvalidArgumentException
415
-     * @throws InvalidInterfaceException
416
-     * @throws InvalidDataTypeException
417
-     * @throws EE_Error
418
-     */
419
-    public function sold_and_reserved()
420
-    {
421
-        return $this->sold() + $this->reserved();
422
-    }
423
-
424
-
425
-    /**
426
-     * returns the datetime name
427
-     *
428
-     * @return string
429
-     * @throws ReflectionException
430
-     * @throws InvalidArgumentException
431
-     * @throws InvalidInterfaceException
432
-     * @throws InvalidDataTypeException
433
-     * @throws EE_Error
434
-     */
435
-    public function name()
436
-    {
437
-        return $this->get('DTT_name');
438
-    }
439
-
440
-
441
-    /**
442
-     * returns the datetime description
443
-     *
444
-     * @return string
445
-     * @throws ReflectionException
446
-     * @throws InvalidArgumentException
447
-     * @throws InvalidInterfaceException
448
-     * @throws InvalidDataTypeException
449
-     * @throws EE_Error
450
-     */
451
-    public function description()
452
-    {
453
-        return $this->get('DTT_description');
454
-    }
455
-
456
-
457
-    /**
458
-     * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
459
-     *
460
-     * @return boolean  TRUE if is primary, FALSE if not.
461
-     * @throws ReflectionException
462
-     * @throws InvalidArgumentException
463
-     * @throws InvalidInterfaceException
464
-     * @throws InvalidDataTypeException
465
-     * @throws EE_Error
466
-     */
467
-    public function is_primary()
468
-    {
469
-        return $this->get('DTT_is_primary');
470
-    }
471
-
472
-
473
-    /**
474
-     * This helper simply returns the order for the datetime
475
-     *
476
-     * @return int  The order of the datetime for this event.
477
-     * @throws ReflectionException
478
-     * @throws InvalidArgumentException
479
-     * @throws InvalidInterfaceException
480
-     * @throws InvalidDataTypeException
481
-     * @throws EE_Error
482
-     */
483
-    public function order()
484
-    {
485
-        return $this->get('DTT_order');
486
-    }
487
-
488
-
489
-    /**
490
-     * This helper simply returns the parent id for the datetime
491
-     *
492
-     * @return int
493
-     * @throws ReflectionException
494
-     * @throws InvalidArgumentException
495
-     * @throws InvalidInterfaceException
496
-     * @throws InvalidDataTypeException
497
-     * @throws EE_Error
498
-     */
499
-    public function parent()
500
-    {
501
-        return $this->get('DTT_parent');
502
-    }
503
-
504
-
505
-    /**
506
-     * show date and/or time
507
-     *
508
-     * @param string $date_or_time    whether to display a date or time or both
509
-     * @param string $start_or_end    whether to display start or end datetimes
510
-     * @param string $dt_frmt
511
-     * @param string $tm_frmt
512
-     * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
513
-     *                                otherwise we use the standard formats)
514
-     * @return string|bool  string on success, FALSE on fail
515
-     * @throws ReflectionException
516
-     * @throws InvalidArgumentException
517
-     * @throws InvalidInterfaceException
518
-     * @throws InvalidDataTypeException
519
-     * @throws EE_Error
520
-     */
521
-    private function _show_datetime(
522
-        $date_or_time = null,
523
-        $start_or_end = 'start',
524
-        $dt_frmt = '',
525
-        $tm_frmt = '',
526
-        $echo = false
527
-    ) {
528
-        $field_name = "DTT_EVT_{$start_or_end}";
529
-        $dtt = $this->_get_datetime(
530
-            $field_name,
531
-            $dt_frmt,
532
-            $tm_frmt,
533
-            $date_or_time,
534
-            $echo
535
-        );
536
-        if (! $echo) {
537
-            return $dtt;
538
-        }
539
-        return '';
540
-    }
541
-
542
-
543
-    /**
544
-     * get event start date.  Provide either the date format, or NULL to re-use the
545
-     * last-used format, or '' to use the default date format
546
-     *
547
-     * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
548
-     * @return mixed            string on success, FALSE on fail
549
-     * @throws ReflectionException
550
-     * @throws InvalidArgumentException
551
-     * @throws InvalidInterfaceException
552
-     * @throws InvalidDataTypeException
553
-     * @throws EE_Error
554
-     */
555
-    public function start_date($dt_frmt = '')
556
-    {
557
-        return $this->_show_datetime('D', 'start', $dt_frmt);
558
-    }
559
-
560
-
561
-    /**
562
-     * Echoes start_date()
563
-     *
564
-     * @param string $dt_frmt
565
-     * @throws ReflectionException
566
-     * @throws InvalidArgumentException
567
-     * @throws InvalidInterfaceException
568
-     * @throws InvalidDataTypeException
569
-     * @throws EE_Error
570
-     */
571
-    public function e_start_date($dt_frmt = '')
572
-    {
573
-        $this->_show_datetime('D', 'start', $dt_frmt, null, true);
574
-    }
575
-
576
-
577
-    /**
578
-     * get end date. Provide either the date format, or NULL to re-use the
579
-     * last-used format, or '' to use the default date format
580
-     *
581
-     * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
582
-     * @return mixed            string on success, FALSE on fail
583
-     * @throws ReflectionException
584
-     * @throws InvalidArgumentException
585
-     * @throws InvalidInterfaceException
586
-     * @throws InvalidDataTypeException
587
-     * @throws EE_Error
588
-     */
589
-    public function end_date($dt_frmt = '')
590
-    {
591
-        return $this->_show_datetime('D', 'end', $dt_frmt);
592
-    }
593
-
594
-
595
-    /**
596
-     * Echoes the end date. See end_date()
597
-     *
598
-     * @param string $dt_frmt
599
-     * @throws ReflectionException
600
-     * @throws InvalidArgumentException
601
-     * @throws InvalidInterfaceException
602
-     * @throws InvalidDataTypeException
603
-     * @throws EE_Error
604
-     */
605
-    public function e_end_date($dt_frmt = '')
606
-    {
607
-        $this->_show_datetime('D', 'end', $dt_frmt, null, true);
608
-    }
609
-
610
-
611
-    /**
612
-     * get date_range - meaning the start AND end date
613
-     *
614
-     * @access public
615
-     * @param string $dt_frmt     string representation of date format defaults to WP settings
616
-     * @param string $conjunction conjunction junction what's your function ?
617
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
618
-     * @return mixed              string on success, FALSE on fail
619
-     * @throws ReflectionException
620
-     * @throws InvalidArgumentException
621
-     * @throws InvalidInterfaceException
622
-     * @throws InvalidDataTypeException
623
-     * @throws EE_Error
624
-     */
625
-    public function date_range($dt_frmt = '', $conjunction = ' - ')
626
-    {
627
-        $dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
628
-        $start = str_replace(
629
-            ' ',
630
-            '&nbsp;',
631
-            $this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
632
-        );
633
-        $end = str_replace(
634
-            ' ',
635
-            '&nbsp;',
636
-            $this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
637
-        );
638
-        return $start !== $end ? $start . $conjunction . $end : $start;
639
-    }
640
-
641
-
642
-    /**
643
-     * @param string $dt_frmt
644
-     * @param string $conjunction
645
-     * @throws ReflectionException
646
-     * @throws InvalidArgumentException
647
-     * @throws InvalidInterfaceException
648
-     * @throws InvalidDataTypeException
649
-     * @throws EE_Error
650
-     */
651
-    public function e_date_range($dt_frmt = '', $conjunction = ' - ')
652
-    {
653
-        echo $this->date_range($dt_frmt, $conjunction);
654
-    }
655
-
656
-
657
-    /**
658
-     * get start time
659
-     *
660
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
661
-     * @return mixed        string on success, FALSE on fail
662
-     * @throws ReflectionException
663
-     * @throws InvalidArgumentException
664
-     * @throws InvalidInterfaceException
665
-     * @throws InvalidDataTypeException
666
-     * @throws EE_Error
667
-     */
668
-    public function start_time($tm_format = '')
669
-    {
670
-        return $this->_show_datetime('T', 'start', null, $tm_format);
671
-    }
672
-
673
-
674
-    /**
675
-     * @param string $tm_format
676
-     * @throws ReflectionException
677
-     * @throws InvalidArgumentException
678
-     * @throws InvalidInterfaceException
679
-     * @throws InvalidDataTypeException
680
-     * @throws EE_Error
681
-     */
682
-    public function e_start_time($tm_format = '')
683
-    {
684
-        $this->_show_datetime('T', 'start', null, $tm_format, true);
685
-    }
686
-
687
-
688
-    /**
689
-     * get end time
690
-     *
691
-     * @param string $tm_format string representation of time format defaults to 'g:i a'
692
-     * @return mixed                string on success, FALSE on fail
693
-     * @throws ReflectionException
694
-     * @throws InvalidArgumentException
695
-     * @throws InvalidInterfaceException
696
-     * @throws InvalidDataTypeException
697
-     * @throws EE_Error
698
-     */
699
-    public function end_time($tm_format = '')
700
-    {
701
-        return $this->_show_datetime('T', 'end', null, $tm_format);
702
-    }
703
-
704
-
705
-    /**
706
-     * @param string $tm_format
707
-     * @throws ReflectionException
708
-     * @throws InvalidArgumentException
709
-     * @throws InvalidInterfaceException
710
-     * @throws InvalidDataTypeException
711
-     * @throws EE_Error
712
-     */
713
-    public function e_end_time($tm_format = '')
714
-    {
715
-        $this->_show_datetime('T', 'end', null, $tm_format, true);
716
-    }
717
-
718
-
719
-    /**
720
-     * get time_range
721
-     *
722
-     * @access public
723
-     * @param string $tm_format   string representation of time format defaults to 'g:i a'
724
-     * @param string $conjunction conjunction junction what's your function ?
725
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
726
-     * @return mixed              string on success, FALSE on fail
727
-     * @throws ReflectionException
728
-     * @throws InvalidArgumentException
729
-     * @throws InvalidInterfaceException
730
-     * @throws InvalidDataTypeException
731
-     * @throws EE_Error
732
-     */
733
-    public function time_range($tm_format = '', $conjunction = ' - ')
734
-    {
735
-        $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
736
-        $start = str_replace(
737
-            ' ',
738
-            '&nbsp;',
739
-            $this->get_i18n_datetime('DTT_EVT_start', $tm_format)
740
-        );
741
-        $end = str_replace(
742
-            ' ',
743
-            '&nbsp;',
744
-            $this->get_i18n_datetime('DTT_EVT_end', $tm_format)
745
-        );
746
-        return $start !== $end ? $start . $conjunction . $end : $start;
747
-    }
748
-
749
-
750
-    /**
751
-     * @param string $tm_format
752
-     * @param string $conjunction
753
-     * @throws ReflectionException
754
-     * @throws InvalidArgumentException
755
-     * @throws InvalidInterfaceException
756
-     * @throws InvalidDataTypeException
757
-     * @throws EE_Error
758
-     */
759
-    public function e_time_range($tm_format = '', $conjunction = ' - ')
760
-    {
761
-        echo $this->time_range($tm_format, $conjunction);
762
-    }
763
-
764
-
765
-    /**
766
-     * This returns a range representation of the date and times.
767
-     * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
768
-     * Also, the return value is localized.
769
-     *
770
-     * @param string $dt_format
771
-     * @param string $tm_format
772
-     * @param string $conjunction used between two different dates or times.
773
-     *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
774
-     * @param string $separator   used between the date and time formats.
775
-     *                            ex: Dec 1, 2016{$separator}2pm
776
-     * @return string
777
-     * @throws ReflectionException
778
-     * @throws InvalidArgumentException
779
-     * @throws InvalidInterfaceException
780
-     * @throws InvalidDataTypeException
781
-     * @throws EE_Error
782
-     */
783
-    public function date_and_time_range(
784
-        $dt_format = '',
785
-        $tm_format = '',
786
-        $conjunction = ' - ',
787
-        $separator = ' '
788
-    ) {
789
-        $dt_format = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
790
-        $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
791
-        $full_format = $dt_format . $separator . $tm_format;
792
-        // the range output depends on various conditions
793
-        switch (true) {
794
-            // start date timestamp and end date timestamp are the same.
795
-            case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')):
796
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
797
-                break;
798
-            // start and end date are the same but times are different
799
-            case ($this->start_date() === $this->end_date()):
800
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
801
-                          . $conjunction
802
-                          . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
803
-                break;
804
-            // all other conditions
805
-            default:
806
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
807
-                          . $conjunction
808
-                          . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
809
-                break;
810
-        }
811
-        return $output;
812
-    }
813
-
814
-
815
-    /**
816
-     * This echos the results of date and time range.
817
-     *
818
-     * @see date_and_time_range() for more details on purpose.
819
-     * @param string $dt_format
820
-     * @param string $tm_format
821
-     * @param string $conjunction
822
-     * @return void
823
-     * @throws ReflectionException
824
-     * @throws InvalidArgumentException
825
-     * @throws InvalidInterfaceException
826
-     * @throws InvalidDataTypeException
827
-     * @throws EE_Error
828
-     */
829
-    public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
830
-    {
831
-        echo $this->date_and_time_range($dt_format, $tm_format, $conjunction);
832
-    }
833
-
834
-
835
-    /**
836
-     * get start date and start time
837
-     *
838
-     * @param    string $dt_format - string representation of date format defaults to 'F j, Y'
839
-     * @param    string $tm_format - string representation of time format defaults to 'g:i a'
840
-     * @return    mixed    string on success, FALSE on fail
841
-     * @throws ReflectionException
842
-     * @throws InvalidArgumentException
843
-     * @throws InvalidInterfaceException
844
-     * @throws InvalidDataTypeException
845
-     * @throws EE_Error
846
-     */
847
-    public function start_date_and_time($dt_format = '', $tm_format = '')
848
-    {
849
-        return $this->_show_datetime('', 'start', $dt_format, $tm_format);
850
-    }
851
-
852
-
853
-    /**
854
-     * @param string $dt_frmt
855
-     * @param string $tm_format
856
-     * @throws ReflectionException
857
-     * @throws InvalidArgumentException
858
-     * @throws InvalidInterfaceException
859
-     * @throws InvalidDataTypeException
860
-     * @throws EE_Error
861
-     */
862
-    public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
863
-    {
864
-        $this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
865
-    }
866
-
867
-
868
-    /**
869
-     * Shows the length of the event (start to end time).
870
-     * Can be shown in 'seconds','minutes','hours', or 'days'.
871
-     * By default, rounds up. (So if you use 'days', and then event
872
-     * only occurs for 1 hour, it will return 1 day).
873
-     *
874
-     * @param string $units 'seconds','minutes','hours','days'
875
-     * @param bool   $round_up
876
-     * @return float|int|mixed
877
-     * @throws ReflectionException
878
-     * @throws InvalidArgumentException
879
-     * @throws InvalidInterfaceException
880
-     * @throws InvalidDataTypeException
881
-     * @throws EE_Error
882
-     */
883
-    public function length($units = 'seconds', $round_up = false)
884
-    {
885
-        $start = $this->get_raw('DTT_EVT_start');
886
-        $end = $this->get_raw('DTT_EVT_end');
887
-        $length_in_units = $end - $start;
888
-        switch ($units) {
889
-            // NOTE: We purposefully don't use "break;" in order to chain the divisions
890
-            /** @noinspection PhpMissingBreakStatementInspection */
891
-            // phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
892
-            case 'days':
893
-                $length_in_units /= 24;
894
-            /** @noinspection PhpMissingBreakStatementInspection */
895
-            case 'hours':
896
-                // fall through is intentional
897
-                $length_in_units /= 60;
898
-            /** @noinspection PhpMissingBreakStatementInspection */
899
-            case 'minutes':
900
-                // fall through is intentional
901
-                $length_in_units /= 60;
902
-            case 'seconds':
903
-            default:
904
-                $length_in_units = ceil($length_in_units);
905
-        }
906
-        // phpcs:enable
907
-        if ($round_up) {
908
-            $length_in_units = max($length_in_units, 1);
909
-        }
910
-        return $length_in_units;
911
-    }
912
-
913
-
914
-    /**
915
-     *        get end date and time
916
-     *
917
-     * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
918
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
919
-     * @return    mixed                string on success, FALSE on fail
920
-     * @throws ReflectionException
921
-     * @throws InvalidArgumentException
922
-     * @throws InvalidInterfaceException
923
-     * @throws InvalidDataTypeException
924
-     * @throws EE_Error
925
-     */
926
-    public function end_date_and_time($dt_frmt = '', $tm_format = '')
927
-    {
928
-        return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
929
-    }
930
-
931
-
932
-    /**
933
-     * @param string $dt_frmt
934
-     * @param string $tm_format
935
-     * @throws ReflectionException
936
-     * @throws InvalidArgumentException
937
-     * @throws InvalidInterfaceException
938
-     * @throws InvalidDataTypeException
939
-     * @throws EE_Error
940
-     */
941
-    public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
942
-    {
943
-        $this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
944
-    }
945
-
946
-
947
-    /**
948
-     *        get start timestamp
949
-     *
950
-     * @return        int
951
-     * @throws ReflectionException
952
-     * @throws InvalidArgumentException
953
-     * @throws InvalidInterfaceException
954
-     * @throws InvalidDataTypeException
955
-     * @throws EE_Error
956
-     */
957
-    public function start()
958
-    {
959
-        return $this->get_raw('DTT_EVT_start');
960
-    }
961
-
962
-
963
-    /**
964
-     *        get end timestamp
965
-     *
966
-     * @return        int
967
-     * @throws ReflectionException
968
-     * @throws InvalidArgumentException
969
-     * @throws InvalidInterfaceException
970
-     * @throws InvalidDataTypeException
971
-     * @throws EE_Error
972
-     */
973
-    public function end()
974
-    {
975
-        return $this->get_raw('DTT_EVT_end');
976
-    }
977
-
978
-
979
-    /**
980
-     *    get the registration limit for this datetime slot
981
-     *
982
-     * @return        mixed        int on success, FALSE on fail
983
-     * @throws ReflectionException
984
-     * @throws InvalidArgumentException
985
-     * @throws InvalidInterfaceException
986
-     * @throws InvalidDataTypeException
987
-     * @throws EE_Error
988
-     */
989
-    public function reg_limit()
990
-    {
991
-        return $this->get_raw('DTT_reg_limit');
992
-    }
993
-
994
-
995
-    /**
996
-     *    have the tickets sold for this datetime, met or exceed the registration limit ?
997
-     *
998
-     * @return        boolean
999
-     * @throws ReflectionException
1000
-     * @throws InvalidArgumentException
1001
-     * @throws InvalidInterfaceException
1002
-     * @throws InvalidDataTypeException
1003
-     * @throws EE_Error
1004
-     */
1005
-    public function sold_out()
1006
-    {
1007
-        return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
1008
-    }
1009
-
1010
-
1011
-    /**
1012
-     * return the total number of spaces remaining at this venue.
1013
-     * This only takes the venue's capacity into account, NOT the tickets available for sale
1014
-     *
1015
-     * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
1016
-     *                               Because if all tickets attached to this datetime have no spaces left,
1017
-     *                               then this datetime IS effectively sold out.
1018
-     *                               However, there are cases where we just want to know the spaces
1019
-     *                               remaining for this particular datetime, hence the flag.
1020
-     * @return int
1021
-     * @throws ReflectionException
1022
-     * @throws InvalidArgumentException
1023
-     * @throws InvalidInterfaceException
1024
-     * @throws InvalidDataTypeException
1025
-     * @throws EE_Error
1026
-     */
1027
-    public function spaces_remaining($consider_tickets = false)
1028
-    {
1029
-        // tickets remaining available for purchase
1030
-        // no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1031
-        $dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1032
-        if (! $consider_tickets) {
1033
-            return $dtt_remaining;
1034
-        }
1035
-        $tickets_remaining = $this->tickets_remaining();
1036
-        return min($dtt_remaining, $tickets_remaining);
1037
-    }
1038
-
1039
-
1040
-    /**
1041
-     * Counts the total tickets available
1042
-     * (from all the different types of tickets which are available for this datetime).
1043
-     *
1044
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1045
-     * @return int
1046
-     * @throws ReflectionException
1047
-     * @throws InvalidArgumentException
1048
-     * @throws InvalidInterfaceException
1049
-     * @throws InvalidDataTypeException
1050
-     * @throws EE_Error
1051
-     */
1052
-    public function tickets_remaining($query_params = array())
1053
-    {
1054
-        $sum = 0;
1055
-        $tickets = $this->tickets($query_params);
1056
-        if (! empty($tickets)) {
1057
-            foreach ($tickets as $ticket) {
1058
-                if ($ticket instanceof EE_Ticket) {
1059
-                    // get the actual amount of tickets that can be sold
1060
-                    $qty = $ticket->qty('saleable');
1061
-                    if ($qty === EE_INF) {
1062
-                        return EE_INF;
1063
-                    }
1064
-                    // no negative ticket quantities plz
1065
-                    if ($qty > 0) {
1066
-                        $sum += $qty;
1067
-                    }
1068
-                }
1069
-            }
1070
-        }
1071
-        return $sum;
1072
-    }
1073
-
1074
-
1075
-    /**
1076
-     * Gets the count of all the tickets available at this datetime (not ticket types)
1077
-     * before any were sold
1078
-     *
1079
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1080
-     * @return int
1081
-     * @throws ReflectionException
1082
-     * @throws InvalidArgumentException
1083
-     * @throws InvalidInterfaceException
1084
-     * @throws InvalidDataTypeException
1085
-     * @throws EE_Error
1086
-     */
1087
-    public function sum_tickets_initially_available($query_params = array())
1088
-    {
1089
-        return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1090
-    }
1091
-
1092
-
1093
-    /**
1094
-     * Returns the lesser-of-the two: spaces remaining at this datetime, or
1095
-     * the total tickets remaining (a sum of the tickets remaining for each ticket type
1096
-     * that is available for this datetime).
1097
-     *
1098
-     * @return int
1099
-     * @throws ReflectionException
1100
-     * @throws InvalidArgumentException
1101
-     * @throws InvalidInterfaceException
1102
-     * @throws InvalidDataTypeException
1103
-     * @throws EE_Error
1104
-     */
1105
-    public function total_tickets_available_at_this_datetime()
1106
-    {
1107
-        return $this->spaces_remaining(true);
1108
-    }
1109
-
1110
-
1111
-    /**
1112
-     * This simply compares the internal dtt for the given string with NOW
1113
-     * and determines if the date is upcoming or not.
1114
-     *
1115
-     * @access public
1116
-     * @return boolean
1117
-     * @throws ReflectionException
1118
-     * @throws InvalidArgumentException
1119
-     * @throws InvalidInterfaceException
1120
-     * @throws InvalidDataTypeException
1121
-     * @throws EE_Error
1122
-     */
1123
-    public function is_upcoming()
1124
-    {
1125
-        return ($this->get_raw('DTT_EVT_start') > time());
1126
-    }
1127
-
1128
-
1129
-    /**
1130
-     * This simply compares the internal datetime for the given string with NOW
1131
-     * and returns if the date is active (i.e. start and end time)
1132
-     *
1133
-     * @return boolean
1134
-     * @throws ReflectionException
1135
-     * @throws InvalidArgumentException
1136
-     * @throws InvalidInterfaceException
1137
-     * @throws InvalidDataTypeException
1138
-     * @throws EE_Error
1139
-     */
1140
-    public function is_active()
1141
-    {
1142
-        return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1143
-    }
1144
-
1145
-
1146
-    /**
1147
-     * This simply compares the internal dtt for the given string with NOW
1148
-     * and determines if the date is expired or not.
1149
-     *
1150
-     * @return boolean
1151
-     * @throws ReflectionException
1152
-     * @throws InvalidArgumentException
1153
-     * @throws InvalidInterfaceException
1154
-     * @throws InvalidDataTypeException
1155
-     * @throws EE_Error
1156
-     */
1157
-    public function is_expired()
1158
-    {
1159
-        return ($this->get_raw('DTT_EVT_end') < time());
1160
-    }
1161
-
1162
-
1163
-    /**
1164
-     * This returns the active status for whether an event is active, upcoming, or expired
1165
-     *
1166
-     * @return int return value will be one of the EE_Datetime status constants.
1167
-     * @throws ReflectionException
1168
-     * @throws InvalidArgumentException
1169
-     * @throws InvalidInterfaceException
1170
-     * @throws InvalidDataTypeException
1171
-     * @throws EE_Error
1172
-     */
1173
-    public function get_active_status()
1174
-    {
1175
-        $total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1176
-        if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1177
-            return EE_Datetime::sold_out;
1178
-        }
1179
-        if ($this->is_expired()) {
1180
-            return EE_Datetime::expired;
1181
-        }
1182
-        if ($this->is_upcoming()) {
1183
-            return EE_Datetime::upcoming;
1184
-        }
1185
-        if ($this->is_active()) {
1186
-            return EE_Datetime::active;
1187
-        }
1188
-        return null;
1189
-    }
1190
-
1191
-
1192
-    /**
1193
-     * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1194
-     *
1195
-     * @param  boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1196
-     * @return string
1197
-     * @throws ReflectionException
1198
-     * @throws InvalidArgumentException
1199
-     * @throws InvalidInterfaceException
1200
-     * @throws InvalidDataTypeException
1201
-     * @throws EE_Error
1202
-     */
1203
-    public function get_dtt_display_name($use_dtt_name = false)
1204
-    {
1205
-        if ($use_dtt_name) {
1206
-            $dtt_name = $this->name();
1207
-            if (! empty($dtt_name)) {
1208
-                return $dtt_name;
1209
-            }
1210
-        }
1211
-        // first condition is to see if the months are different
1212
-        if (date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1213
-        ) {
1214
-            $display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1215
-            // next condition is if its the same month but different day
1216
-        } else {
1217
-            if (date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1218
-                && date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1219
-            ) {
1220
-                $display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1221
-            } else {
1222
-                $display_date = $this->start_date('F j\, Y')
1223
-                                . ' @ '
1224
-                                . $this->start_date('g:i a')
1225
-                                . ' - '
1226
-                                . $this->end_date('g:i a');
1227
-            }
1228
-        }
1229
-        return $display_date;
1230
-    }
1231
-
1232
-
1233
-    /**
1234
-     * Gets all the tickets for this datetime
1235
-     *
1236
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1237
-     * @return EE_Base_Class[]|EE_Ticket[]
1238
-     * @throws ReflectionException
1239
-     * @throws InvalidArgumentException
1240
-     * @throws InvalidInterfaceException
1241
-     * @throws InvalidDataTypeException
1242
-     * @throws EE_Error
1243
-     */
1244
-    public function tickets($query_params = array())
1245
-    {
1246
-        return $this->get_many_related('Ticket', $query_params);
1247
-    }
1248
-
1249
-
1250
-    /**
1251
-     * Gets all the ticket types currently available for purchase
1252
-     *
1253
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1254
-     * @return EE_Ticket[]
1255
-     * @throws ReflectionException
1256
-     * @throws InvalidArgumentException
1257
-     * @throws InvalidInterfaceException
1258
-     * @throws InvalidDataTypeException
1259
-     * @throws EE_Error
1260
-     */
1261
-    public function ticket_types_available_for_purchase($query_params = array())
1262
-    {
1263
-        // first check if datetime is valid
1264
-        if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1265
-            return array();
1266
-        }
1267
-        if (empty($query_params)) {
1268
-            $query_params = array(
1269
-                array(
1270
-                    'TKT_start_date' => array('<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')),
1271
-                    'TKT_end_date'   => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
1272
-                    'TKT_deleted'    => false,
1273
-                ),
1274
-            );
1275
-        }
1276
-        return $this->tickets($query_params);
1277
-    }
1278
-
1279
-
1280
-    /**
1281
-     * @return EE_Base_Class|EE_Event
1282
-     * @throws ReflectionException
1283
-     * @throws InvalidArgumentException
1284
-     * @throws InvalidInterfaceException
1285
-     * @throws InvalidDataTypeException
1286
-     * @throws EE_Error
1287
-     */
1288
-    public function event()
1289
-    {
1290
-        return $this->get_first_related('Event');
1291
-    }
1292
-
1293
-
1294
-    /**
1295
-     * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1296
-     * (via the tickets).
1297
-     *
1298
-     * @return int
1299
-     * @throws ReflectionException
1300
-     * @throws InvalidArgumentException
1301
-     * @throws InvalidInterfaceException
1302
-     * @throws InvalidDataTypeException
1303
-     * @throws EE_Error
1304
-     */
1305
-    public function update_sold()
1306
-    {
1307
-        $count_regs_for_this_datetime = EEM_Registration::instance()->count(
1308
-            array(
1309
-                array(
1310
-                    'STS_ID'                 => EEM_Registration::status_id_approved,
1311
-                    'REG_deleted'            => 0,
1312
-                    'Ticket.Datetime.DTT_ID' => $this->ID(),
1313
-                ),
1314
-            )
1315
-        );
1316
-        $this->set_sold($count_regs_for_this_datetime);
1317
-        $this->save();
1318
-        return $count_regs_for_this_datetime;
1319
-    }
1320
-
1321
-
1322
-    /*******************************************************************
16
+	/**
17
+	 * constant used by get_active_status, indicates datetime has no more available spaces
18
+	 */
19
+	const sold_out = 'DTS';
20
+
21
+	/**
22
+	 * constant used by get_active_status, indicating datetime is still active (even is not over, can be registered-for)
23
+	 */
24
+	const active = 'DTA';
25
+
26
+	/**
27
+	 * constant used by get_active_status, indicating the datetime cannot be used for registrations yet, but has not
28
+	 * expired
29
+	 */
30
+	const upcoming = 'DTU';
31
+
32
+	/**
33
+	 * Datetime is postponed
34
+	 */
35
+	const postponed = 'DTP';
36
+
37
+	/**
38
+	 * Datetime is cancelled
39
+	 */
40
+	const cancelled = 'DTC';
41
+
42
+	/**
43
+	 * constant used by get_active_status, indicates datetime has expired (event is over)
44
+	 */
45
+	const expired = 'DTE';
46
+
47
+	/**
48
+	 * constant used in various places indicating that an event is INACTIVE (not yet ready to be published)
49
+	 */
50
+	const inactive = 'DTI';
51
+
52
+
53
+	/**
54
+	 * @param array  $props_n_values    incoming values
55
+	 * @param string $timezone          incoming timezone (if not set the timezone set for the website will be used.)
56
+	 * @param array  $date_formats      incoming date_formats in an array where the first value is the date_format
57
+	 *                                  and the second value is the time format
58
+	 * @return EE_Datetime
59
+	 * @throws ReflectionException
60
+	 * @throws InvalidArgumentException
61
+	 * @throws InvalidInterfaceException
62
+	 * @throws InvalidDataTypeException
63
+	 * @throws EE_Error
64
+	 */
65
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
66
+	{
67
+		$has_object = parent::_check_for_object(
68
+			$props_n_values,
69
+			__CLASS__,
70
+			$timezone,
71
+			$date_formats
72
+		);
73
+		return $has_object
74
+			? $has_object
75
+			: new self($props_n_values, false, $timezone, $date_formats);
76
+	}
77
+
78
+
79
+	/**
80
+	 * @param array  $props_n_values  incoming values from the database
81
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
82
+	 *                                the website will be used.
83
+	 * @return EE_Datetime
84
+	 * @throws ReflectionException
85
+	 * @throws InvalidArgumentException
86
+	 * @throws InvalidInterfaceException
87
+	 * @throws InvalidDataTypeException
88
+	 * @throws EE_Error
89
+	 */
90
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
91
+	{
92
+		return new self($props_n_values, true, $timezone);
93
+	}
94
+
95
+
96
+	/**
97
+	 * @param $name
98
+	 * @throws ReflectionException
99
+	 * @throws InvalidArgumentException
100
+	 * @throws InvalidInterfaceException
101
+	 * @throws InvalidDataTypeException
102
+	 * @throws EE_Error
103
+	 */
104
+	public function set_name($name)
105
+	{
106
+		$this->set('DTT_name', $name);
107
+	}
108
+
109
+
110
+	/**
111
+	 * @param $description
112
+	 * @throws ReflectionException
113
+	 * @throws InvalidArgumentException
114
+	 * @throws InvalidInterfaceException
115
+	 * @throws InvalidDataTypeException
116
+	 * @throws EE_Error
117
+	 */
118
+	public function set_description($description)
119
+	{
120
+		$this->set('DTT_description', $description);
121
+	}
122
+
123
+
124
+	/**
125
+	 * Set event start date
126
+	 * set the start date for an event
127
+	 *
128
+	 * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
129
+	 * @throws ReflectionException
130
+	 * @throws InvalidArgumentException
131
+	 * @throws InvalidInterfaceException
132
+	 * @throws InvalidDataTypeException
133
+	 * @throws EE_Error
134
+	 */
135
+	public function set_start_date($date)
136
+	{
137
+		$this->_set_date_for($date, 'DTT_EVT_start');
138
+	}
139
+
140
+
141
+	/**
142
+	 * Set event start time
143
+	 * set the start time for an event
144
+	 *
145
+	 * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
146
+	 * @throws ReflectionException
147
+	 * @throws InvalidArgumentException
148
+	 * @throws InvalidInterfaceException
149
+	 * @throws InvalidDataTypeException
150
+	 * @throws EE_Error
151
+	 */
152
+	public function set_start_time($time)
153
+	{
154
+		$this->_set_time_for($time, 'DTT_EVT_start');
155
+	}
156
+
157
+
158
+	/**
159
+	 * Set event end date
160
+	 * set the end date for an event
161
+	 *
162
+	 * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
163
+	 * @throws ReflectionException
164
+	 * @throws InvalidArgumentException
165
+	 * @throws InvalidInterfaceException
166
+	 * @throws InvalidDataTypeException
167
+	 * @throws EE_Error
168
+	 */
169
+	public function set_end_date($date)
170
+	{
171
+		$this->_set_date_for($date, 'DTT_EVT_end');
172
+	}
173
+
174
+
175
+	/**
176
+	 * Set event end time
177
+	 * set the end time for an event
178
+	 *
179
+	 * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
180
+	 * @throws ReflectionException
181
+	 * @throws InvalidArgumentException
182
+	 * @throws InvalidInterfaceException
183
+	 * @throws InvalidDataTypeException
184
+	 * @throws EE_Error
185
+	 */
186
+	public function set_end_time($time)
187
+	{
188
+		$this->_set_time_for($time, 'DTT_EVT_end');
189
+	}
190
+
191
+
192
+	/**
193
+	 * Set registration limit
194
+	 * set the maximum number of attendees that can be registered for this datetime slot
195
+	 *
196
+	 * @param int $reg_limit
197
+	 * @throws ReflectionException
198
+	 * @throws InvalidArgumentException
199
+	 * @throws InvalidInterfaceException
200
+	 * @throws InvalidDataTypeException
201
+	 * @throws EE_Error
202
+	 */
203
+	public function set_reg_limit($reg_limit)
204
+	{
205
+		$this->set('DTT_reg_limit', $reg_limit);
206
+	}
207
+
208
+
209
+	/**
210
+	 * get the number of tickets sold for this datetime slot
211
+	 *
212
+	 * @return mixed int on success, FALSE on fail
213
+	 * @throws ReflectionException
214
+	 * @throws InvalidArgumentException
215
+	 * @throws InvalidInterfaceException
216
+	 * @throws InvalidDataTypeException
217
+	 * @throws EE_Error
218
+	 */
219
+	public function sold()
220
+	{
221
+		return $this->get_raw('DTT_sold');
222
+	}
223
+
224
+
225
+	/**
226
+	 * @param int $sold
227
+	 * @throws ReflectionException
228
+	 * @throws InvalidArgumentException
229
+	 * @throws InvalidInterfaceException
230
+	 * @throws InvalidDataTypeException
231
+	 * @throws EE_Error
232
+	 */
233
+	public function set_sold($sold)
234
+	{
235
+		// sold can not go below zero
236
+		$sold = max(0, $sold);
237
+		$this->set('DTT_sold', $sold);
238
+	}
239
+
240
+
241
+	/**
242
+	 * Increments sold by amount passed by $qty, and persists it immediately to the database.
243
+	 * Simultaneously decreases the reserved count, unless $also_decrease_reserved is false.
244
+	 *
245
+	 * @param int $qty
246
+	 * @param boolean $also_decrease_reserved
247
+	 * @return boolean indicating success
248
+	 * @throws ReflectionException
249
+	 * @throws InvalidArgumentException
250
+	 * @throws InvalidInterfaceException
251
+	 * @throws InvalidDataTypeException
252
+	 * @throws EE_Error
253
+	 */
254
+	public function increaseSold($qty = 1, $also_decrease_reserved = true)
255
+	{
256
+		$qty = absint($qty);
257
+		if ($also_decrease_reserved) {
258
+			$success = $this->adjustNumericFieldsInDb(
259
+				[
260
+					'DTT_reserved' => $qty * -1,
261
+					'DTT_sold' => $qty
262
+				]
263
+			);
264
+		} else {
265
+			$success = $this->adjustNumericFieldsInDb(
266
+				[
267
+					'DTT_sold' => $qty
268
+				]
269
+			);
270
+		}
271
+
272
+		do_action(
273
+			'AHEE__EE_Datetime__increase_sold',
274
+			$this,
275
+			$qty,
276
+			$this->sold(),
277
+			$success
278
+		);
279
+		return $success;
280
+	}
281
+
282
+
283
+	/**
284
+	 * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
285
+	 * to save afterwards.)
286
+	 *
287
+	 * @param int $qty
288
+	 * @return boolean indicating success
289
+	 * @throws ReflectionException
290
+	 * @throws InvalidArgumentException
291
+	 * @throws InvalidInterfaceException
292
+	 * @throws InvalidDataTypeException
293
+	 * @throws EE_Error
294
+	 */
295
+	public function decreaseSold($qty = 1)
296
+	{
297
+		$qty = absint($qty);
298
+		$success = $this->adjustNumericFieldsInDb(
299
+			[
300
+				'DTT_sold' => $qty * -1
301
+			]
302
+		);
303
+		do_action(
304
+			'AHEE__EE_Datetime__decrease_sold',
305
+			$this,
306
+			$qty,
307
+			$this->sold(),
308
+			$success
309
+		);
310
+		return $success;
311
+	}
312
+
313
+
314
+	/**
315
+	 * Gets qty of reserved tickets for this datetime
316
+	 *
317
+	 * @return int
318
+	 * @throws ReflectionException
319
+	 * @throws InvalidArgumentException
320
+	 * @throws InvalidInterfaceException
321
+	 * @throws InvalidDataTypeException
322
+	 * @throws EE_Error
323
+	 */
324
+	public function reserved()
325
+	{
326
+		return $this->get_raw('DTT_reserved');
327
+	}
328
+
329
+
330
+	/**
331
+	 * Sets qty of reserved tickets for this datetime
332
+	 *
333
+	 * @param int $reserved
334
+	 * @throws ReflectionException
335
+	 * @throws InvalidArgumentException
336
+	 * @throws InvalidInterfaceException
337
+	 * @throws InvalidDataTypeException
338
+	 * @throws EE_Error
339
+	 */
340
+	public function set_reserved($reserved)
341
+	{
342
+		// reserved can not go below zero
343
+		$reserved = max(0, (int) $reserved);
344
+		$this->set('DTT_reserved', $reserved);
345
+	}
346
+
347
+
348
+	/**
349
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
350
+	 *
351
+	 * @param int $qty
352
+	 * @return boolean indicating success
353
+	 * @throws ReflectionException
354
+	 * @throws InvalidArgumentException
355
+	 * @throws InvalidInterfaceException
356
+	 * @throws InvalidDataTypeException
357
+	 * @throws EE_Error
358
+	 */
359
+	public function increaseReserved($qty = 1)
360
+	{
361
+		$qty = absint($qty);
362
+		$success = $this->incrementFieldConditionallyInDb(
363
+			'DTT_reserved',
364
+			'DTT_sold',
365
+			'DTT_reg_limit',
366
+			$qty
367
+		);
368
+		do_action(
369
+			'AHEE__EE_Datetime__increase_reserved',
370
+			$this,
371
+			$qty,
372
+			$this->reserved(),
373
+			$success
374
+		);
375
+		return $success;
376
+	}
377
+
378
+
379
+	/**
380
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
381
+	 *
382
+	 * @param int $qty
383
+	 * @return boolean indicating success
384
+	 * @throws ReflectionException
385
+	 * @throws InvalidArgumentException
386
+	 * @throws InvalidInterfaceException
387
+	 * @throws InvalidDataTypeException
388
+	 * @throws EE_Error
389
+	 */
390
+	public function decreaseReserved($qty = 1)
391
+	{
392
+		$qty = absint($qty);
393
+		$success = $this->adjustNumericFieldsInDb(
394
+			[
395
+				'DTT_reserved' => $qty * -1
396
+			]
397
+		);
398
+		do_action(
399
+			'AHEE__EE_Datetime__decrease_reserved',
400
+			$this,
401
+			$qty,
402
+			$this->reserved(),
403
+			$success
404
+		);
405
+		return $success;
406
+	}
407
+
408
+
409
+	/**
410
+	 * total sold and reserved tickets
411
+	 *
412
+	 * @return int
413
+	 * @throws ReflectionException
414
+	 * @throws InvalidArgumentException
415
+	 * @throws InvalidInterfaceException
416
+	 * @throws InvalidDataTypeException
417
+	 * @throws EE_Error
418
+	 */
419
+	public function sold_and_reserved()
420
+	{
421
+		return $this->sold() + $this->reserved();
422
+	}
423
+
424
+
425
+	/**
426
+	 * returns the datetime name
427
+	 *
428
+	 * @return string
429
+	 * @throws ReflectionException
430
+	 * @throws InvalidArgumentException
431
+	 * @throws InvalidInterfaceException
432
+	 * @throws InvalidDataTypeException
433
+	 * @throws EE_Error
434
+	 */
435
+	public function name()
436
+	{
437
+		return $this->get('DTT_name');
438
+	}
439
+
440
+
441
+	/**
442
+	 * returns the datetime description
443
+	 *
444
+	 * @return string
445
+	 * @throws ReflectionException
446
+	 * @throws InvalidArgumentException
447
+	 * @throws InvalidInterfaceException
448
+	 * @throws InvalidDataTypeException
449
+	 * @throws EE_Error
450
+	 */
451
+	public function description()
452
+	{
453
+		return $this->get('DTT_description');
454
+	}
455
+
456
+
457
+	/**
458
+	 * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
459
+	 *
460
+	 * @return boolean  TRUE if is primary, FALSE if not.
461
+	 * @throws ReflectionException
462
+	 * @throws InvalidArgumentException
463
+	 * @throws InvalidInterfaceException
464
+	 * @throws InvalidDataTypeException
465
+	 * @throws EE_Error
466
+	 */
467
+	public function is_primary()
468
+	{
469
+		return $this->get('DTT_is_primary');
470
+	}
471
+
472
+
473
+	/**
474
+	 * This helper simply returns the order for the datetime
475
+	 *
476
+	 * @return int  The order of the datetime for this event.
477
+	 * @throws ReflectionException
478
+	 * @throws InvalidArgumentException
479
+	 * @throws InvalidInterfaceException
480
+	 * @throws InvalidDataTypeException
481
+	 * @throws EE_Error
482
+	 */
483
+	public function order()
484
+	{
485
+		return $this->get('DTT_order');
486
+	}
487
+
488
+
489
+	/**
490
+	 * This helper simply returns the parent id for the datetime
491
+	 *
492
+	 * @return int
493
+	 * @throws ReflectionException
494
+	 * @throws InvalidArgumentException
495
+	 * @throws InvalidInterfaceException
496
+	 * @throws InvalidDataTypeException
497
+	 * @throws EE_Error
498
+	 */
499
+	public function parent()
500
+	{
501
+		return $this->get('DTT_parent');
502
+	}
503
+
504
+
505
+	/**
506
+	 * show date and/or time
507
+	 *
508
+	 * @param string $date_or_time    whether to display a date or time or both
509
+	 * @param string $start_or_end    whether to display start or end datetimes
510
+	 * @param string $dt_frmt
511
+	 * @param string $tm_frmt
512
+	 * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
513
+	 *                                otherwise we use the standard formats)
514
+	 * @return string|bool  string on success, FALSE on fail
515
+	 * @throws ReflectionException
516
+	 * @throws InvalidArgumentException
517
+	 * @throws InvalidInterfaceException
518
+	 * @throws InvalidDataTypeException
519
+	 * @throws EE_Error
520
+	 */
521
+	private function _show_datetime(
522
+		$date_or_time = null,
523
+		$start_or_end = 'start',
524
+		$dt_frmt = '',
525
+		$tm_frmt = '',
526
+		$echo = false
527
+	) {
528
+		$field_name = "DTT_EVT_{$start_or_end}";
529
+		$dtt = $this->_get_datetime(
530
+			$field_name,
531
+			$dt_frmt,
532
+			$tm_frmt,
533
+			$date_or_time,
534
+			$echo
535
+		);
536
+		if (! $echo) {
537
+			return $dtt;
538
+		}
539
+		return '';
540
+	}
541
+
542
+
543
+	/**
544
+	 * get event start date.  Provide either the date format, or NULL to re-use the
545
+	 * last-used format, or '' to use the default date format
546
+	 *
547
+	 * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
548
+	 * @return mixed            string on success, FALSE on fail
549
+	 * @throws ReflectionException
550
+	 * @throws InvalidArgumentException
551
+	 * @throws InvalidInterfaceException
552
+	 * @throws InvalidDataTypeException
553
+	 * @throws EE_Error
554
+	 */
555
+	public function start_date($dt_frmt = '')
556
+	{
557
+		return $this->_show_datetime('D', 'start', $dt_frmt);
558
+	}
559
+
560
+
561
+	/**
562
+	 * Echoes start_date()
563
+	 *
564
+	 * @param string $dt_frmt
565
+	 * @throws ReflectionException
566
+	 * @throws InvalidArgumentException
567
+	 * @throws InvalidInterfaceException
568
+	 * @throws InvalidDataTypeException
569
+	 * @throws EE_Error
570
+	 */
571
+	public function e_start_date($dt_frmt = '')
572
+	{
573
+		$this->_show_datetime('D', 'start', $dt_frmt, null, true);
574
+	}
575
+
576
+
577
+	/**
578
+	 * get end date. Provide either the date format, or NULL to re-use the
579
+	 * last-used format, or '' to use the default date format
580
+	 *
581
+	 * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
582
+	 * @return mixed            string on success, FALSE on fail
583
+	 * @throws ReflectionException
584
+	 * @throws InvalidArgumentException
585
+	 * @throws InvalidInterfaceException
586
+	 * @throws InvalidDataTypeException
587
+	 * @throws EE_Error
588
+	 */
589
+	public function end_date($dt_frmt = '')
590
+	{
591
+		return $this->_show_datetime('D', 'end', $dt_frmt);
592
+	}
593
+
594
+
595
+	/**
596
+	 * Echoes the end date. See end_date()
597
+	 *
598
+	 * @param string $dt_frmt
599
+	 * @throws ReflectionException
600
+	 * @throws InvalidArgumentException
601
+	 * @throws InvalidInterfaceException
602
+	 * @throws InvalidDataTypeException
603
+	 * @throws EE_Error
604
+	 */
605
+	public function e_end_date($dt_frmt = '')
606
+	{
607
+		$this->_show_datetime('D', 'end', $dt_frmt, null, true);
608
+	}
609
+
610
+
611
+	/**
612
+	 * get date_range - meaning the start AND end date
613
+	 *
614
+	 * @access public
615
+	 * @param string $dt_frmt     string representation of date format defaults to WP settings
616
+	 * @param string $conjunction conjunction junction what's your function ?
617
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
618
+	 * @return mixed              string on success, FALSE on fail
619
+	 * @throws ReflectionException
620
+	 * @throws InvalidArgumentException
621
+	 * @throws InvalidInterfaceException
622
+	 * @throws InvalidDataTypeException
623
+	 * @throws EE_Error
624
+	 */
625
+	public function date_range($dt_frmt = '', $conjunction = ' - ')
626
+	{
627
+		$dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
628
+		$start = str_replace(
629
+			' ',
630
+			'&nbsp;',
631
+			$this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
632
+		);
633
+		$end = str_replace(
634
+			' ',
635
+			'&nbsp;',
636
+			$this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
637
+		);
638
+		return $start !== $end ? $start . $conjunction . $end : $start;
639
+	}
640
+
641
+
642
+	/**
643
+	 * @param string $dt_frmt
644
+	 * @param string $conjunction
645
+	 * @throws ReflectionException
646
+	 * @throws InvalidArgumentException
647
+	 * @throws InvalidInterfaceException
648
+	 * @throws InvalidDataTypeException
649
+	 * @throws EE_Error
650
+	 */
651
+	public function e_date_range($dt_frmt = '', $conjunction = ' - ')
652
+	{
653
+		echo $this->date_range($dt_frmt, $conjunction);
654
+	}
655
+
656
+
657
+	/**
658
+	 * get start time
659
+	 *
660
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
661
+	 * @return mixed        string on success, FALSE on fail
662
+	 * @throws ReflectionException
663
+	 * @throws InvalidArgumentException
664
+	 * @throws InvalidInterfaceException
665
+	 * @throws InvalidDataTypeException
666
+	 * @throws EE_Error
667
+	 */
668
+	public function start_time($tm_format = '')
669
+	{
670
+		return $this->_show_datetime('T', 'start', null, $tm_format);
671
+	}
672
+
673
+
674
+	/**
675
+	 * @param string $tm_format
676
+	 * @throws ReflectionException
677
+	 * @throws InvalidArgumentException
678
+	 * @throws InvalidInterfaceException
679
+	 * @throws InvalidDataTypeException
680
+	 * @throws EE_Error
681
+	 */
682
+	public function e_start_time($tm_format = '')
683
+	{
684
+		$this->_show_datetime('T', 'start', null, $tm_format, true);
685
+	}
686
+
687
+
688
+	/**
689
+	 * get end time
690
+	 *
691
+	 * @param string $tm_format string representation of time format defaults to 'g:i a'
692
+	 * @return mixed                string on success, FALSE on fail
693
+	 * @throws ReflectionException
694
+	 * @throws InvalidArgumentException
695
+	 * @throws InvalidInterfaceException
696
+	 * @throws InvalidDataTypeException
697
+	 * @throws EE_Error
698
+	 */
699
+	public function end_time($tm_format = '')
700
+	{
701
+		return $this->_show_datetime('T', 'end', null, $tm_format);
702
+	}
703
+
704
+
705
+	/**
706
+	 * @param string $tm_format
707
+	 * @throws ReflectionException
708
+	 * @throws InvalidArgumentException
709
+	 * @throws InvalidInterfaceException
710
+	 * @throws InvalidDataTypeException
711
+	 * @throws EE_Error
712
+	 */
713
+	public function e_end_time($tm_format = '')
714
+	{
715
+		$this->_show_datetime('T', 'end', null, $tm_format, true);
716
+	}
717
+
718
+
719
+	/**
720
+	 * get time_range
721
+	 *
722
+	 * @access public
723
+	 * @param string $tm_format   string representation of time format defaults to 'g:i a'
724
+	 * @param string $conjunction conjunction junction what's your function ?
725
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
726
+	 * @return mixed              string on success, FALSE on fail
727
+	 * @throws ReflectionException
728
+	 * @throws InvalidArgumentException
729
+	 * @throws InvalidInterfaceException
730
+	 * @throws InvalidDataTypeException
731
+	 * @throws EE_Error
732
+	 */
733
+	public function time_range($tm_format = '', $conjunction = ' - ')
734
+	{
735
+		$tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
736
+		$start = str_replace(
737
+			' ',
738
+			'&nbsp;',
739
+			$this->get_i18n_datetime('DTT_EVT_start', $tm_format)
740
+		);
741
+		$end = str_replace(
742
+			' ',
743
+			'&nbsp;',
744
+			$this->get_i18n_datetime('DTT_EVT_end', $tm_format)
745
+		);
746
+		return $start !== $end ? $start . $conjunction . $end : $start;
747
+	}
748
+
749
+
750
+	/**
751
+	 * @param string $tm_format
752
+	 * @param string $conjunction
753
+	 * @throws ReflectionException
754
+	 * @throws InvalidArgumentException
755
+	 * @throws InvalidInterfaceException
756
+	 * @throws InvalidDataTypeException
757
+	 * @throws EE_Error
758
+	 */
759
+	public function e_time_range($tm_format = '', $conjunction = ' - ')
760
+	{
761
+		echo $this->time_range($tm_format, $conjunction);
762
+	}
763
+
764
+
765
+	/**
766
+	 * This returns a range representation of the date and times.
767
+	 * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
768
+	 * Also, the return value is localized.
769
+	 *
770
+	 * @param string $dt_format
771
+	 * @param string $tm_format
772
+	 * @param string $conjunction used between two different dates or times.
773
+	 *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
774
+	 * @param string $separator   used between the date and time formats.
775
+	 *                            ex: Dec 1, 2016{$separator}2pm
776
+	 * @return string
777
+	 * @throws ReflectionException
778
+	 * @throws InvalidArgumentException
779
+	 * @throws InvalidInterfaceException
780
+	 * @throws InvalidDataTypeException
781
+	 * @throws EE_Error
782
+	 */
783
+	public function date_and_time_range(
784
+		$dt_format = '',
785
+		$tm_format = '',
786
+		$conjunction = ' - ',
787
+		$separator = ' '
788
+	) {
789
+		$dt_format = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
790
+		$tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
791
+		$full_format = $dt_format . $separator . $tm_format;
792
+		// the range output depends on various conditions
793
+		switch (true) {
794
+			// start date timestamp and end date timestamp are the same.
795
+			case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')):
796
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
797
+				break;
798
+			// start and end date are the same but times are different
799
+			case ($this->start_date() === $this->end_date()):
800
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
801
+						  . $conjunction
802
+						  . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
803
+				break;
804
+			// all other conditions
805
+			default:
806
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
807
+						  . $conjunction
808
+						  . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
809
+				break;
810
+		}
811
+		return $output;
812
+	}
813
+
814
+
815
+	/**
816
+	 * This echos the results of date and time range.
817
+	 *
818
+	 * @see date_and_time_range() for more details on purpose.
819
+	 * @param string $dt_format
820
+	 * @param string $tm_format
821
+	 * @param string $conjunction
822
+	 * @return void
823
+	 * @throws ReflectionException
824
+	 * @throws InvalidArgumentException
825
+	 * @throws InvalidInterfaceException
826
+	 * @throws InvalidDataTypeException
827
+	 * @throws EE_Error
828
+	 */
829
+	public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
830
+	{
831
+		echo $this->date_and_time_range($dt_format, $tm_format, $conjunction);
832
+	}
833
+
834
+
835
+	/**
836
+	 * get start date and start time
837
+	 *
838
+	 * @param    string $dt_format - string representation of date format defaults to 'F j, Y'
839
+	 * @param    string $tm_format - string representation of time format defaults to 'g:i a'
840
+	 * @return    mixed    string on success, FALSE on fail
841
+	 * @throws ReflectionException
842
+	 * @throws InvalidArgumentException
843
+	 * @throws InvalidInterfaceException
844
+	 * @throws InvalidDataTypeException
845
+	 * @throws EE_Error
846
+	 */
847
+	public function start_date_and_time($dt_format = '', $tm_format = '')
848
+	{
849
+		return $this->_show_datetime('', 'start', $dt_format, $tm_format);
850
+	}
851
+
852
+
853
+	/**
854
+	 * @param string $dt_frmt
855
+	 * @param string $tm_format
856
+	 * @throws ReflectionException
857
+	 * @throws InvalidArgumentException
858
+	 * @throws InvalidInterfaceException
859
+	 * @throws InvalidDataTypeException
860
+	 * @throws EE_Error
861
+	 */
862
+	public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
863
+	{
864
+		$this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
865
+	}
866
+
867
+
868
+	/**
869
+	 * Shows the length of the event (start to end time).
870
+	 * Can be shown in 'seconds','minutes','hours', or 'days'.
871
+	 * By default, rounds up. (So if you use 'days', and then event
872
+	 * only occurs for 1 hour, it will return 1 day).
873
+	 *
874
+	 * @param string $units 'seconds','minutes','hours','days'
875
+	 * @param bool   $round_up
876
+	 * @return float|int|mixed
877
+	 * @throws ReflectionException
878
+	 * @throws InvalidArgumentException
879
+	 * @throws InvalidInterfaceException
880
+	 * @throws InvalidDataTypeException
881
+	 * @throws EE_Error
882
+	 */
883
+	public function length($units = 'seconds', $round_up = false)
884
+	{
885
+		$start = $this->get_raw('DTT_EVT_start');
886
+		$end = $this->get_raw('DTT_EVT_end');
887
+		$length_in_units = $end - $start;
888
+		switch ($units) {
889
+			// NOTE: We purposefully don't use "break;" in order to chain the divisions
890
+			/** @noinspection PhpMissingBreakStatementInspection */
891
+			// phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
892
+			case 'days':
893
+				$length_in_units /= 24;
894
+			/** @noinspection PhpMissingBreakStatementInspection */
895
+			case 'hours':
896
+				// fall through is intentional
897
+				$length_in_units /= 60;
898
+			/** @noinspection PhpMissingBreakStatementInspection */
899
+			case 'minutes':
900
+				// fall through is intentional
901
+				$length_in_units /= 60;
902
+			case 'seconds':
903
+			default:
904
+				$length_in_units = ceil($length_in_units);
905
+		}
906
+		// phpcs:enable
907
+		if ($round_up) {
908
+			$length_in_units = max($length_in_units, 1);
909
+		}
910
+		return $length_in_units;
911
+	}
912
+
913
+
914
+	/**
915
+	 *        get end date and time
916
+	 *
917
+	 * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
918
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
919
+	 * @return    mixed                string on success, FALSE on fail
920
+	 * @throws ReflectionException
921
+	 * @throws InvalidArgumentException
922
+	 * @throws InvalidInterfaceException
923
+	 * @throws InvalidDataTypeException
924
+	 * @throws EE_Error
925
+	 */
926
+	public function end_date_and_time($dt_frmt = '', $tm_format = '')
927
+	{
928
+		return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
929
+	}
930
+
931
+
932
+	/**
933
+	 * @param string $dt_frmt
934
+	 * @param string $tm_format
935
+	 * @throws ReflectionException
936
+	 * @throws InvalidArgumentException
937
+	 * @throws InvalidInterfaceException
938
+	 * @throws InvalidDataTypeException
939
+	 * @throws EE_Error
940
+	 */
941
+	public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
942
+	{
943
+		$this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
944
+	}
945
+
946
+
947
+	/**
948
+	 *        get start timestamp
949
+	 *
950
+	 * @return        int
951
+	 * @throws ReflectionException
952
+	 * @throws InvalidArgumentException
953
+	 * @throws InvalidInterfaceException
954
+	 * @throws InvalidDataTypeException
955
+	 * @throws EE_Error
956
+	 */
957
+	public function start()
958
+	{
959
+		return $this->get_raw('DTT_EVT_start');
960
+	}
961
+
962
+
963
+	/**
964
+	 *        get end timestamp
965
+	 *
966
+	 * @return        int
967
+	 * @throws ReflectionException
968
+	 * @throws InvalidArgumentException
969
+	 * @throws InvalidInterfaceException
970
+	 * @throws InvalidDataTypeException
971
+	 * @throws EE_Error
972
+	 */
973
+	public function end()
974
+	{
975
+		return $this->get_raw('DTT_EVT_end');
976
+	}
977
+
978
+
979
+	/**
980
+	 *    get the registration limit for this datetime slot
981
+	 *
982
+	 * @return        mixed        int on success, FALSE on fail
983
+	 * @throws ReflectionException
984
+	 * @throws InvalidArgumentException
985
+	 * @throws InvalidInterfaceException
986
+	 * @throws InvalidDataTypeException
987
+	 * @throws EE_Error
988
+	 */
989
+	public function reg_limit()
990
+	{
991
+		return $this->get_raw('DTT_reg_limit');
992
+	}
993
+
994
+
995
+	/**
996
+	 *    have the tickets sold for this datetime, met or exceed the registration limit ?
997
+	 *
998
+	 * @return        boolean
999
+	 * @throws ReflectionException
1000
+	 * @throws InvalidArgumentException
1001
+	 * @throws InvalidInterfaceException
1002
+	 * @throws InvalidDataTypeException
1003
+	 * @throws EE_Error
1004
+	 */
1005
+	public function sold_out()
1006
+	{
1007
+		return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
1008
+	}
1009
+
1010
+
1011
+	/**
1012
+	 * return the total number of spaces remaining at this venue.
1013
+	 * This only takes the venue's capacity into account, NOT the tickets available for sale
1014
+	 *
1015
+	 * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
1016
+	 *                               Because if all tickets attached to this datetime have no spaces left,
1017
+	 *                               then this datetime IS effectively sold out.
1018
+	 *                               However, there are cases where we just want to know the spaces
1019
+	 *                               remaining for this particular datetime, hence the flag.
1020
+	 * @return int
1021
+	 * @throws ReflectionException
1022
+	 * @throws InvalidArgumentException
1023
+	 * @throws InvalidInterfaceException
1024
+	 * @throws InvalidDataTypeException
1025
+	 * @throws EE_Error
1026
+	 */
1027
+	public function spaces_remaining($consider_tickets = false)
1028
+	{
1029
+		// tickets remaining available for purchase
1030
+		// no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1031
+		$dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1032
+		if (! $consider_tickets) {
1033
+			return $dtt_remaining;
1034
+		}
1035
+		$tickets_remaining = $this->tickets_remaining();
1036
+		return min($dtt_remaining, $tickets_remaining);
1037
+	}
1038
+
1039
+
1040
+	/**
1041
+	 * Counts the total tickets available
1042
+	 * (from all the different types of tickets which are available for this datetime).
1043
+	 *
1044
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1045
+	 * @return int
1046
+	 * @throws ReflectionException
1047
+	 * @throws InvalidArgumentException
1048
+	 * @throws InvalidInterfaceException
1049
+	 * @throws InvalidDataTypeException
1050
+	 * @throws EE_Error
1051
+	 */
1052
+	public function tickets_remaining($query_params = array())
1053
+	{
1054
+		$sum = 0;
1055
+		$tickets = $this->tickets($query_params);
1056
+		if (! empty($tickets)) {
1057
+			foreach ($tickets as $ticket) {
1058
+				if ($ticket instanceof EE_Ticket) {
1059
+					// get the actual amount of tickets that can be sold
1060
+					$qty = $ticket->qty('saleable');
1061
+					if ($qty === EE_INF) {
1062
+						return EE_INF;
1063
+					}
1064
+					// no negative ticket quantities plz
1065
+					if ($qty > 0) {
1066
+						$sum += $qty;
1067
+					}
1068
+				}
1069
+			}
1070
+		}
1071
+		return $sum;
1072
+	}
1073
+
1074
+
1075
+	/**
1076
+	 * Gets the count of all the tickets available at this datetime (not ticket types)
1077
+	 * before any were sold
1078
+	 *
1079
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1080
+	 * @return int
1081
+	 * @throws ReflectionException
1082
+	 * @throws InvalidArgumentException
1083
+	 * @throws InvalidInterfaceException
1084
+	 * @throws InvalidDataTypeException
1085
+	 * @throws EE_Error
1086
+	 */
1087
+	public function sum_tickets_initially_available($query_params = array())
1088
+	{
1089
+		return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1090
+	}
1091
+
1092
+
1093
+	/**
1094
+	 * Returns the lesser-of-the two: spaces remaining at this datetime, or
1095
+	 * the total tickets remaining (a sum of the tickets remaining for each ticket type
1096
+	 * that is available for this datetime).
1097
+	 *
1098
+	 * @return int
1099
+	 * @throws ReflectionException
1100
+	 * @throws InvalidArgumentException
1101
+	 * @throws InvalidInterfaceException
1102
+	 * @throws InvalidDataTypeException
1103
+	 * @throws EE_Error
1104
+	 */
1105
+	public function total_tickets_available_at_this_datetime()
1106
+	{
1107
+		return $this->spaces_remaining(true);
1108
+	}
1109
+
1110
+
1111
+	/**
1112
+	 * This simply compares the internal dtt for the given string with NOW
1113
+	 * and determines if the date is upcoming or not.
1114
+	 *
1115
+	 * @access public
1116
+	 * @return boolean
1117
+	 * @throws ReflectionException
1118
+	 * @throws InvalidArgumentException
1119
+	 * @throws InvalidInterfaceException
1120
+	 * @throws InvalidDataTypeException
1121
+	 * @throws EE_Error
1122
+	 */
1123
+	public function is_upcoming()
1124
+	{
1125
+		return ($this->get_raw('DTT_EVT_start') > time());
1126
+	}
1127
+
1128
+
1129
+	/**
1130
+	 * This simply compares the internal datetime for the given string with NOW
1131
+	 * and returns if the date is active (i.e. start and end time)
1132
+	 *
1133
+	 * @return boolean
1134
+	 * @throws ReflectionException
1135
+	 * @throws InvalidArgumentException
1136
+	 * @throws InvalidInterfaceException
1137
+	 * @throws InvalidDataTypeException
1138
+	 * @throws EE_Error
1139
+	 */
1140
+	public function is_active()
1141
+	{
1142
+		return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1143
+	}
1144
+
1145
+
1146
+	/**
1147
+	 * This simply compares the internal dtt for the given string with NOW
1148
+	 * and determines if the date is expired or not.
1149
+	 *
1150
+	 * @return boolean
1151
+	 * @throws ReflectionException
1152
+	 * @throws InvalidArgumentException
1153
+	 * @throws InvalidInterfaceException
1154
+	 * @throws InvalidDataTypeException
1155
+	 * @throws EE_Error
1156
+	 */
1157
+	public function is_expired()
1158
+	{
1159
+		return ($this->get_raw('DTT_EVT_end') < time());
1160
+	}
1161
+
1162
+
1163
+	/**
1164
+	 * This returns the active status for whether an event is active, upcoming, or expired
1165
+	 *
1166
+	 * @return int return value will be one of the EE_Datetime status constants.
1167
+	 * @throws ReflectionException
1168
+	 * @throws InvalidArgumentException
1169
+	 * @throws InvalidInterfaceException
1170
+	 * @throws InvalidDataTypeException
1171
+	 * @throws EE_Error
1172
+	 */
1173
+	public function get_active_status()
1174
+	{
1175
+		$total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1176
+		if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1177
+			return EE_Datetime::sold_out;
1178
+		}
1179
+		if ($this->is_expired()) {
1180
+			return EE_Datetime::expired;
1181
+		}
1182
+		if ($this->is_upcoming()) {
1183
+			return EE_Datetime::upcoming;
1184
+		}
1185
+		if ($this->is_active()) {
1186
+			return EE_Datetime::active;
1187
+		}
1188
+		return null;
1189
+	}
1190
+
1191
+
1192
+	/**
1193
+	 * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1194
+	 *
1195
+	 * @param  boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1196
+	 * @return string
1197
+	 * @throws ReflectionException
1198
+	 * @throws InvalidArgumentException
1199
+	 * @throws InvalidInterfaceException
1200
+	 * @throws InvalidDataTypeException
1201
+	 * @throws EE_Error
1202
+	 */
1203
+	public function get_dtt_display_name($use_dtt_name = false)
1204
+	{
1205
+		if ($use_dtt_name) {
1206
+			$dtt_name = $this->name();
1207
+			if (! empty($dtt_name)) {
1208
+				return $dtt_name;
1209
+			}
1210
+		}
1211
+		// first condition is to see if the months are different
1212
+		if (date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1213
+		) {
1214
+			$display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1215
+			// next condition is if its the same month but different day
1216
+		} else {
1217
+			if (date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1218
+				&& date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1219
+			) {
1220
+				$display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1221
+			} else {
1222
+				$display_date = $this->start_date('F j\, Y')
1223
+								. ' @ '
1224
+								. $this->start_date('g:i a')
1225
+								. ' - '
1226
+								. $this->end_date('g:i a');
1227
+			}
1228
+		}
1229
+		return $display_date;
1230
+	}
1231
+
1232
+
1233
+	/**
1234
+	 * Gets all the tickets for this datetime
1235
+	 *
1236
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1237
+	 * @return EE_Base_Class[]|EE_Ticket[]
1238
+	 * @throws ReflectionException
1239
+	 * @throws InvalidArgumentException
1240
+	 * @throws InvalidInterfaceException
1241
+	 * @throws InvalidDataTypeException
1242
+	 * @throws EE_Error
1243
+	 */
1244
+	public function tickets($query_params = array())
1245
+	{
1246
+		return $this->get_many_related('Ticket', $query_params);
1247
+	}
1248
+
1249
+
1250
+	/**
1251
+	 * Gets all the ticket types currently available for purchase
1252
+	 *
1253
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1254
+	 * @return EE_Ticket[]
1255
+	 * @throws ReflectionException
1256
+	 * @throws InvalidArgumentException
1257
+	 * @throws InvalidInterfaceException
1258
+	 * @throws InvalidDataTypeException
1259
+	 * @throws EE_Error
1260
+	 */
1261
+	public function ticket_types_available_for_purchase($query_params = array())
1262
+	{
1263
+		// first check if datetime is valid
1264
+		if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1265
+			return array();
1266
+		}
1267
+		if (empty($query_params)) {
1268
+			$query_params = array(
1269
+				array(
1270
+					'TKT_start_date' => array('<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')),
1271
+					'TKT_end_date'   => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
1272
+					'TKT_deleted'    => false,
1273
+				),
1274
+			);
1275
+		}
1276
+		return $this->tickets($query_params);
1277
+	}
1278
+
1279
+
1280
+	/**
1281
+	 * @return EE_Base_Class|EE_Event
1282
+	 * @throws ReflectionException
1283
+	 * @throws InvalidArgumentException
1284
+	 * @throws InvalidInterfaceException
1285
+	 * @throws InvalidDataTypeException
1286
+	 * @throws EE_Error
1287
+	 */
1288
+	public function event()
1289
+	{
1290
+		return $this->get_first_related('Event');
1291
+	}
1292
+
1293
+
1294
+	/**
1295
+	 * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1296
+	 * (via the tickets).
1297
+	 *
1298
+	 * @return int
1299
+	 * @throws ReflectionException
1300
+	 * @throws InvalidArgumentException
1301
+	 * @throws InvalidInterfaceException
1302
+	 * @throws InvalidDataTypeException
1303
+	 * @throws EE_Error
1304
+	 */
1305
+	public function update_sold()
1306
+	{
1307
+		$count_regs_for_this_datetime = EEM_Registration::instance()->count(
1308
+			array(
1309
+				array(
1310
+					'STS_ID'                 => EEM_Registration::status_id_approved,
1311
+					'REG_deleted'            => 0,
1312
+					'Ticket.Datetime.DTT_ID' => $this->ID(),
1313
+				),
1314
+			)
1315
+		);
1316
+		$this->set_sold($count_regs_for_this_datetime);
1317
+		$this->save();
1318
+		return $count_regs_for_this_datetime;
1319
+	}
1320
+
1321
+
1322
+	/*******************************************************************
1323 1323
      ***********************  DEPRECATED METHODS  **********************
1324 1324
      *******************************************************************/
1325 1325
 
1326 1326
 
1327
-    /**
1328
-     * Increments sold by amount passed by $qty, and persists it immediately to the database.
1329
-     *
1330
-     * @deprecated 4.9.80.p
1331
-     * @param int $qty
1332
-     * @return boolean
1333
-     * @throws ReflectionException
1334
-     * @throws InvalidArgumentException
1335
-     * @throws InvalidInterfaceException
1336
-     * @throws InvalidDataTypeException
1337
-     * @throws EE_Error
1338
-     */
1339
-    public function increase_sold($qty = 1)
1340
-    {
1341
-        EE_Error::doing_it_wrong(
1342
-            __FUNCTION__,
1343
-            esc_html__('Please use EE_Datetime::increaseSold() instead', 'event_espresso'),
1344
-            '4.9.80.p',
1345
-            '5.0.0.p'
1346
-        );
1347
-        return $this->increaseSold($qty);
1348
-    }
1349
-
1350
-
1351
-    /**
1352
-     * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
1353
-     * to save afterwards.)
1354
-     *
1355
-     * @deprecated 4.9.80.p
1356
-     * @param int $qty
1357
-     * @return boolean
1358
-     * @throws ReflectionException
1359
-     * @throws InvalidArgumentException
1360
-     * @throws InvalidInterfaceException
1361
-     * @throws InvalidDataTypeException
1362
-     * @throws EE_Error
1363
-     */
1364
-    public function decrease_sold($qty = 1)
1365
-    {
1366
-        EE_Error::doing_it_wrong(
1367
-            __FUNCTION__,
1368
-            esc_html__('Please use EE_Datetime::decreaseSold() instead', 'event_espresso'),
1369
-            '4.9.80.p',
1370
-            '5.0.0.p'
1371
-        );
1372
-        return $this->decreaseSold($qty);
1373
-    }
1374
-
1375
-
1376
-    /**
1377
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1378
-     *
1379
-     * @deprecated 4.9.80.p
1380
-     * @param int $qty
1381
-     * @return boolean indicating success
1382
-     * @throws ReflectionException
1383
-     * @throws InvalidArgumentException
1384
-     * @throws InvalidInterfaceException
1385
-     * @throws InvalidDataTypeException
1386
-     * @throws EE_Error
1387
-     */
1388
-    public function increase_reserved($qty = 1)
1389
-    {
1390
-        EE_Error::doing_it_wrong(
1391
-            __FUNCTION__,
1392
-            esc_html__('Please use EE_Datetime::increaseReserved() instead', 'event_espresso'),
1393
-            '4.9.80.p',
1394
-            '5.0.0.p'
1395
-        );
1396
-        return $this->increaseReserved($qty);
1397
-    }
1398
-
1399
-
1400
-    /**
1401
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1402
-     *
1403
-     * @deprecated 4.9.80.p
1404
-     * @param int $qty
1405
-     * @return boolean
1406
-     * @throws ReflectionException
1407
-     * @throws InvalidArgumentException
1408
-     * @throws InvalidInterfaceException
1409
-     * @throws InvalidDataTypeException
1410
-     * @throws EE_Error
1411
-     */
1412
-    public function decrease_reserved($qty = 1)
1413
-    {
1414
-        EE_Error::doing_it_wrong(
1415
-            __FUNCTION__,
1416
-            esc_html__('Please use EE_Datetime::decreaseReserved() instead', 'event_espresso'),
1417
-            '4.9.80.p',
1418
-            '5.0.0.p'
1419
-        );
1420
-        return $this->decreaseReserved($qty);
1421
-    }
1327
+	/**
1328
+	 * Increments sold by amount passed by $qty, and persists it immediately to the database.
1329
+	 *
1330
+	 * @deprecated 4.9.80.p
1331
+	 * @param int $qty
1332
+	 * @return boolean
1333
+	 * @throws ReflectionException
1334
+	 * @throws InvalidArgumentException
1335
+	 * @throws InvalidInterfaceException
1336
+	 * @throws InvalidDataTypeException
1337
+	 * @throws EE_Error
1338
+	 */
1339
+	public function increase_sold($qty = 1)
1340
+	{
1341
+		EE_Error::doing_it_wrong(
1342
+			__FUNCTION__,
1343
+			esc_html__('Please use EE_Datetime::increaseSold() instead', 'event_espresso'),
1344
+			'4.9.80.p',
1345
+			'5.0.0.p'
1346
+		);
1347
+		return $this->increaseSold($qty);
1348
+	}
1349
+
1350
+
1351
+	/**
1352
+	 * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
1353
+	 * to save afterwards.)
1354
+	 *
1355
+	 * @deprecated 4.9.80.p
1356
+	 * @param int $qty
1357
+	 * @return boolean
1358
+	 * @throws ReflectionException
1359
+	 * @throws InvalidArgumentException
1360
+	 * @throws InvalidInterfaceException
1361
+	 * @throws InvalidDataTypeException
1362
+	 * @throws EE_Error
1363
+	 */
1364
+	public function decrease_sold($qty = 1)
1365
+	{
1366
+		EE_Error::doing_it_wrong(
1367
+			__FUNCTION__,
1368
+			esc_html__('Please use EE_Datetime::decreaseSold() instead', 'event_espresso'),
1369
+			'4.9.80.p',
1370
+			'5.0.0.p'
1371
+		);
1372
+		return $this->decreaseSold($qty);
1373
+	}
1374
+
1375
+
1376
+	/**
1377
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1378
+	 *
1379
+	 * @deprecated 4.9.80.p
1380
+	 * @param int $qty
1381
+	 * @return boolean indicating success
1382
+	 * @throws ReflectionException
1383
+	 * @throws InvalidArgumentException
1384
+	 * @throws InvalidInterfaceException
1385
+	 * @throws InvalidDataTypeException
1386
+	 * @throws EE_Error
1387
+	 */
1388
+	public function increase_reserved($qty = 1)
1389
+	{
1390
+		EE_Error::doing_it_wrong(
1391
+			__FUNCTION__,
1392
+			esc_html__('Please use EE_Datetime::increaseReserved() instead', 'event_espresso'),
1393
+			'4.9.80.p',
1394
+			'5.0.0.p'
1395
+		);
1396
+		return $this->increaseReserved($qty);
1397
+	}
1398
+
1399
+
1400
+	/**
1401
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1402
+	 *
1403
+	 * @deprecated 4.9.80.p
1404
+	 * @param int $qty
1405
+	 * @return boolean
1406
+	 * @throws ReflectionException
1407
+	 * @throws InvalidArgumentException
1408
+	 * @throws InvalidInterfaceException
1409
+	 * @throws InvalidDataTypeException
1410
+	 * @throws EE_Error
1411
+	 */
1412
+	public function decrease_reserved($qty = 1)
1413
+	{
1414
+		EE_Error::doing_it_wrong(
1415
+			__FUNCTION__,
1416
+			esc_html__('Please use EE_Datetime::decreaseReserved() instead', 'event_espresso'),
1417
+			'4.9.80.p',
1418
+			'5.0.0.p'
1419
+		);
1420
+		return $this->decreaseReserved($qty);
1421
+	}
1422 1422
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Question_Group.class.php 1 patch
Indentation   +276 added lines, -276 removed lines patch added patch discarded remove patch
@@ -12,280 +12,280 @@
 block discarded – undo
12 12
 class EE_Question_Group extends EE_Soft_Delete_Base_Class
13 13
 {
14 14
 
15
-    /**
16
-     * @param array $props_n_values
17
-     * @return EE_Question_Group|mixed
18
-     */
19
-    public static function new_instance($props_n_values = array())
20
-    {
21
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__);
22
-        return $has_object ? $has_object : new self($props_n_values);
23
-    }
24
-
25
-
26
-    /**
27
-     * @param array $props_n_values
28
-     * @return EE_Question_Group
29
-     */
30
-    public static function new_instance_from_db($props_n_values = array())
31
-    {
32
-        return new self($props_n_values, true);
33
-    }
34
-
35
-
36
-    /**
37
-     * gets the question group's name
38
-     *
39
-     * @access public
40
-     * @param bool $pretty
41
-     * @return string
42
-     */
43
-    public function name($pretty = false)
44
-    {
45
-        return $pretty ? $this->get_pretty('QSG_name') : $this->get('QSG_name');
46
-    }
47
-
48
-
49
-    /**
50
-     * Gets the question group's internal name
51
-     *
52
-     * @access public
53
-     * @return string
54
-     */
55
-    public function identifier()
56
-    {
57
-        return $this->get('QSG_identifier');
58
-    }
59
-
60
-
61
-    /**
62
-     * Gets the question group's description
63
-     *
64
-     * @access public
65
-     * @param bool $pretty
66
-     * @return string
67
-     */
68
-    public function desc($pretty = false)
69
-    {
70
-        return $pretty ? $this->get_pretty('QSG_desc') : $this->get('QSG_desc');
71
-    }
72
-
73
-
74
-    /**
75
-     * Gets the question group's order number in a sequence
76
-     * of other question groups
77
-     *
78
-     * @access public
79
-     * @return int
80
-     */
81
-    public function order()
82
-    {
83
-        return $this->get('QSG_order');
84
-    }
85
-
86
-
87
-    /**
88
-     * Returns whether to show the group's name on the frontend
89
-     *
90
-     * @access public
91
-     * @return boolean
92
-     */
93
-    public function show_group_name()
94
-    {
95
-        return $this->get('QSG_show_group_name');
96
-    }
97
-
98
-
99
-    /**
100
-     * Returns whether to show the group's description
101
-     * on the frontend
102
-     *
103
-     * @access public
104
-     * @return boolean
105
-     */
106
-    public function show_group_desc()
107
-    {
108
-        return $this->get('QSG_show_group_desc');
109
-    }
110
-
111
-
112
-    /**
113
-     * Returns whether this is a 'system group' (meaning
114
-     * a question group integral to the system, whose questions
115
-     * relate to the attendee table)
116
-     *
117
-     * @access public
118
-     * @return int
119
-     */
120
-    public function system_group()
121
-    {
122
-        return $this->get('QSG_system');
123
-    }
124
-
125
-
126
-    /**
127
-     * get the author of the question group.
128
-     *
129
-     * @since 4.5.0
130
-     *
131
-     * @return int
132
-     */
133
-    public function wp_user()
134
-    {
135
-        return $this->get('QSG_wp_user');
136
-    }
137
-
138
-
139
-    /**
140
-     * Returns whether this question group has
141
-     * been deleted
142
-     *
143
-     * @access public
144
-     * @return boolean
145
-     */
146
-    public function deleted()
147
-    {
148
-        return $this->get('QST_deleted');
149
-    }
150
-
151
-
152
-    /**
153
-     * Gets an array of questions with questions IN the group at the start of the array and questions NOT in the group
154
-     * at the end of the array.  Questions in the group are ordered by Question_Group_Question.QGQ_order and questions
155
-     * NOT in the group are ordered by Question.QGQ_order
156
-     *
157
-     * @return EE_Question[]
158
-     */
159
-    public function questions_in_and_not_in_group()
160
-    {
161
-        $questions_in_group = $this->questions();
162
-        $exclude_question_ids = ! empty($questions_in_group) ? array_keys($questions_in_group) : array();
163
-        $questions_not_in_group = $this->questions_not_in_group($exclude_question_ids);
164
-        return $questions_in_group + $questions_not_in_group;
165
-    }
166
-
167
-
168
-    /**
169
-     * Gets all the questions which are part of this question group (ordered Question_Group_Question.QGQ_order)
170
-     *
171
-     * @param array $query_params
172
-     * @return EE_Question[]
173
-     */
174
-    public function questions($query_params = array())
175
-    {
176
-        $query_params = ! empty($query_params) ? $query_params
177
-            : array('order_by' => array('Question_Group_Question.QGQ_order' => 'ASC'));
178
-        return $this->ID() ? $this->get_many_related('Question', $query_params) : array();
179
-    }
180
-
181
-
182
-    /**
183
-     * Gets all the questions which are NOT part of this question group.
184
-     *
185
-     * @param  mixed $question_IDS_in_group if empty array then all questions returned.  if FALSE then we first get
186
-     *                                      questions in this group and exclude them from questions get all. IF empty
187
-     *                                      array then we just return all questions.
188
-     * @return EE_Question[]
189
-     */
190
-    public function questions_not_in_group($question_IDS_in_group = false)
191
-    {
192
-        if ($question_IDS_in_group === false) {
193
-            $questions = $this->questions();
194
-            $question_IDS_in_group = ! empty($questions) ? array_keys($questions) : array();
195
-        }
196
-        $_where = ! empty($question_IDS_in_group) ? array('QST_ID' => array('not_in', $question_IDS_in_group))
197
-            : array();
198
-
199
-        return EEM_Question::instance()->get_all(array($_where, 'order_by' => array('QST_ID' => 'ASC')));
200
-    }
201
-
202
-
203
-    /**
204
-     * Gets all events which are related to this question group
205
-     *
206
-     * @return EE_Event[]
207
-     */
208
-    public function events()
209
-    {
210
-        return $this->get_many_related('Event');
211
-    }
212
-
213
-
214
-    /**
215
-     * Adds the question to this question group
216
-     *
217
-     * @param EE_Question || int $question object or ID
218
-     * @return boolean if successful
219
-     */
220
-    public function add_question($questionObjectOrID)
221
-    {
222
-        return $this->_add_relation_to($questionObjectOrID, 'Question');
223
-    }
224
-
225
-
226
-    /**
227
-     * Removes the question from this question group
228
-     *
229
-     * @param EE_Question || int $question object or ID
230
-     * @return boolean of success
231
-     */
232
-    public function remove_question($questionObjectOrID)
233
-    {
234
-        return $this->_remove_relation_to($questionObjectOrID, 'Question');
235
-    }
236
-
237
-
238
-    /**
239
-     * @param $questionObjectOrID
240
-     * @param $qst_order
241
-     * @return int
242
-     */
243
-    public function update_question_order($questionObjectOrID, $qst_order)
244
-    {
245
-        $qst_ID = $questionObjectOrID instanceof EE_Question ? $questionObjectOrID->ID() : (int) $questionObjectOrID;
246
-        return EEM_Question_Group_Question::instance()->update(
247
-            array('QGQ_order' => $qst_order),
248
-            array(
249
-                array(
250
-                    'QST_ID' => $qst_ID,
251
-                    'QSG_ID' => $this->ID(),
252
-                ),
253
-            )
254
-        );
255
-    }
256
-
257
-
258
-    /**
259
-     * Basically this is method just returns whether the question group has any questions with answers.  This is used
260
-     * by the admin currently to determine whether we should display the ui for deleting permanently or not b/c
261
-     * question groups with questions that have answers should not be possible to delete permanently
262
-     *
263
-     * @return boolean true if has questions with answers, false if not.
264
-     */
265
-    public function has_questions_with_answers()
266
-    {
267
-        $has_answers = false;
268
-        $questions = $this->get_many_related('Question');
269
-        foreach ($questions as $question) {
270
-            if ($question->count_related('Answer') > 0) {
271
-                $has_answers = true;
272
-            }
273
-        }
274
-        return $has_answers;
275
-    }
276
-
277
-
278
-    /**
279
-     * The purpose of this method is set the question group order for this question group to be the max out of all
280
-     * question groups
281
-     *
282
-     * @access public
283
-     * @return void
284
-     */
285
-    public function set_order_to_latest()
286
-    {
287
-        $latest_order = $this->get_model()->get_latest_question_group_order();
288
-        $latest_order++;
289
-        $this->set('QSG_order', $latest_order);
290
-    }
15
+	/**
16
+	 * @param array $props_n_values
17
+	 * @return EE_Question_Group|mixed
18
+	 */
19
+	public static function new_instance($props_n_values = array())
20
+	{
21
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__);
22
+		return $has_object ? $has_object : new self($props_n_values);
23
+	}
24
+
25
+
26
+	/**
27
+	 * @param array $props_n_values
28
+	 * @return EE_Question_Group
29
+	 */
30
+	public static function new_instance_from_db($props_n_values = array())
31
+	{
32
+		return new self($props_n_values, true);
33
+	}
34
+
35
+
36
+	/**
37
+	 * gets the question group's name
38
+	 *
39
+	 * @access public
40
+	 * @param bool $pretty
41
+	 * @return string
42
+	 */
43
+	public function name($pretty = false)
44
+	{
45
+		return $pretty ? $this->get_pretty('QSG_name') : $this->get('QSG_name');
46
+	}
47
+
48
+
49
+	/**
50
+	 * Gets the question group's internal name
51
+	 *
52
+	 * @access public
53
+	 * @return string
54
+	 */
55
+	public function identifier()
56
+	{
57
+		return $this->get('QSG_identifier');
58
+	}
59
+
60
+
61
+	/**
62
+	 * Gets the question group's description
63
+	 *
64
+	 * @access public
65
+	 * @param bool $pretty
66
+	 * @return string
67
+	 */
68
+	public function desc($pretty = false)
69
+	{
70
+		return $pretty ? $this->get_pretty('QSG_desc') : $this->get('QSG_desc');
71
+	}
72
+
73
+
74
+	/**
75
+	 * Gets the question group's order number in a sequence
76
+	 * of other question groups
77
+	 *
78
+	 * @access public
79
+	 * @return int
80
+	 */
81
+	public function order()
82
+	{
83
+		return $this->get('QSG_order');
84
+	}
85
+
86
+
87
+	/**
88
+	 * Returns whether to show the group's name on the frontend
89
+	 *
90
+	 * @access public
91
+	 * @return boolean
92
+	 */
93
+	public function show_group_name()
94
+	{
95
+		return $this->get('QSG_show_group_name');
96
+	}
97
+
98
+
99
+	/**
100
+	 * Returns whether to show the group's description
101
+	 * on the frontend
102
+	 *
103
+	 * @access public
104
+	 * @return boolean
105
+	 */
106
+	public function show_group_desc()
107
+	{
108
+		return $this->get('QSG_show_group_desc');
109
+	}
110
+
111
+
112
+	/**
113
+	 * Returns whether this is a 'system group' (meaning
114
+	 * a question group integral to the system, whose questions
115
+	 * relate to the attendee table)
116
+	 *
117
+	 * @access public
118
+	 * @return int
119
+	 */
120
+	public function system_group()
121
+	{
122
+		return $this->get('QSG_system');
123
+	}
124
+
125
+
126
+	/**
127
+	 * get the author of the question group.
128
+	 *
129
+	 * @since 4.5.0
130
+	 *
131
+	 * @return int
132
+	 */
133
+	public function wp_user()
134
+	{
135
+		return $this->get('QSG_wp_user');
136
+	}
137
+
138
+
139
+	/**
140
+	 * Returns whether this question group has
141
+	 * been deleted
142
+	 *
143
+	 * @access public
144
+	 * @return boolean
145
+	 */
146
+	public function deleted()
147
+	{
148
+		return $this->get('QST_deleted');
149
+	}
150
+
151
+
152
+	/**
153
+	 * Gets an array of questions with questions IN the group at the start of the array and questions NOT in the group
154
+	 * at the end of the array.  Questions in the group are ordered by Question_Group_Question.QGQ_order and questions
155
+	 * NOT in the group are ordered by Question.QGQ_order
156
+	 *
157
+	 * @return EE_Question[]
158
+	 */
159
+	public function questions_in_and_not_in_group()
160
+	{
161
+		$questions_in_group = $this->questions();
162
+		$exclude_question_ids = ! empty($questions_in_group) ? array_keys($questions_in_group) : array();
163
+		$questions_not_in_group = $this->questions_not_in_group($exclude_question_ids);
164
+		return $questions_in_group + $questions_not_in_group;
165
+	}
166
+
167
+
168
+	/**
169
+	 * Gets all the questions which are part of this question group (ordered Question_Group_Question.QGQ_order)
170
+	 *
171
+	 * @param array $query_params
172
+	 * @return EE_Question[]
173
+	 */
174
+	public function questions($query_params = array())
175
+	{
176
+		$query_params = ! empty($query_params) ? $query_params
177
+			: array('order_by' => array('Question_Group_Question.QGQ_order' => 'ASC'));
178
+		return $this->ID() ? $this->get_many_related('Question', $query_params) : array();
179
+	}
180
+
181
+
182
+	/**
183
+	 * Gets all the questions which are NOT part of this question group.
184
+	 *
185
+	 * @param  mixed $question_IDS_in_group if empty array then all questions returned.  if FALSE then we first get
186
+	 *                                      questions in this group and exclude them from questions get all. IF empty
187
+	 *                                      array then we just return all questions.
188
+	 * @return EE_Question[]
189
+	 */
190
+	public function questions_not_in_group($question_IDS_in_group = false)
191
+	{
192
+		if ($question_IDS_in_group === false) {
193
+			$questions = $this->questions();
194
+			$question_IDS_in_group = ! empty($questions) ? array_keys($questions) : array();
195
+		}
196
+		$_where = ! empty($question_IDS_in_group) ? array('QST_ID' => array('not_in', $question_IDS_in_group))
197
+			: array();
198
+
199
+		return EEM_Question::instance()->get_all(array($_where, 'order_by' => array('QST_ID' => 'ASC')));
200
+	}
201
+
202
+
203
+	/**
204
+	 * Gets all events which are related to this question group
205
+	 *
206
+	 * @return EE_Event[]
207
+	 */
208
+	public function events()
209
+	{
210
+		return $this->get_many_related('Event');
211
+	}
212
+
213
+
214
+	/**
215
+	 * Adds the question to this question group
216
+	 *
217
+	 * @param EE_Question || int $question object or ID
218
+	 * @return boolean if successful
219
+	 */
220
+	public function add_question($questionObjectOrID)
221
+	{
222
+		return $this->_add_relation_to($questionObjectOrID, 'Question');
223
+	}
224
+
225
+
226
+	/**
227
+	 * Removes the question from this question group
228
+	 *
229
+	 * @param EE_Question || int $question object or ID
230
+	 * @return boolean of success
231
+	 */
232
+	public function remove_question($questionObjectOrID)
233
+	{
234
+		return $this->_remove_relation_to($questionObjectOrID, 'Question');
235
+	}
236
+
237
+
238
+	/**
239
+	 * @param $questionObjectOrID
240
+	 * @param $qst_order
241
+	 * @return int
242
+	 */
243
+	public function update_question_order($questionObjectOrID, $qst_order)
244
+	{
245
+		$qst_ID = $questionObjectOrID instanceof EE_Question ? $questionObjectOrID->ID() : (int) $questionObjectOrID;
246
+		return EEM_Question_Group_Question::instance()->update(
247
+			array('QGQ_order' => $qst_order),
248
+			array(
249
+				array(
250
+					'QST_ID' => $qst_ID,
251
+					'QSG_ID' => $this->ID(),
252
+				),
253
+			)
254
+		);
255
+	}
256
+
257
+
258
+	/**
259
+	 * Basically this is method just returns whether the question group has any questions with answers.  This is used
260
+	 * by the admin currently to determine whether we should display the ui for deleting permanently or not b/c
261
+	 * question groups with questions that have answers should not be possible to delete permanently
262
+	 *
263
+	 * @return boolean true if has questions with answers, false if not.
264
+	 */
265
+	public function has_questions_with_answers()
266
+	{
267
+		$has_answers = false;
268
+		$questions = $this->get_many_related('Question');
269
+		foreach ($questions as $question) {
270
+			if ($question->count_related('Answer') > 0) {
271
+				$has_answers = true;
272
+			}
273
+		}
274
+		return $has_answers;
275
+	}
276
+
277
+
278
+	/**
279
+	 * The purpose of this method is set the question group order for this question group to be the max out of all
280
+	 * question groups
281
+	 *
282
+	 * @access public
283
+	 * @return void
284
+	 */
285
+	public function set_order_to_latest()
286
+	{
287
+		$latest_order = $this->get_model()->get_latest_question_group_order();
288
+		$latest_order++;
289
+		$this->set('QSG_order', $latest_order);
290
+	}
291 291
 }
Please login to merge, or discard this patch.