Completed
Branch Gutenberg/event-attendees-bloc... (dcb43c)
by
unknown
21:24 queued 19:24
created
core/db_classes/EE_Ticket_Price.class.php 1 patch
Indentation   +19 added lines, -19 removed lines patch added patch discarded remove patch
@@ -11,25 +11,25 @@
 block discarded – undo
11 11
 class EE_Ticket_Price extends EE_Base_Class
12 12
 {
13 13
 
14
-    /**
15
-     * @param array  $props_n_values
16
-     * @param string $timezone
17
-     * @return EE_Ticket_Price|mixed
18
-     */
19
-    public static function new_instance($props_n_values = array(), $timezone = '', $date_formats = array())
20
-    {
21
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
22
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
23
-    }
14
+	/**
15
+	 * @param array  $props_n_values
16
+	 * @param string $timezone
17
+	 * @return EE_Ticket_Price|mixed
18
+	 */
19
+	public static function new_instance($props_n_values = array(), $timezone = '', $date_formats = array())
20
+	{
21
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
22
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
23
+	}
24 24
 
25 25
 
26
-    /**
27
-     * @param array  $props_n_values
28
-     * @param string $timezone
29
-     * @return EE_Ticket_Price
30
-     */
31
-    public static function new_instance_from_db($props_n_values = array(), $timezone = '')
32
-    {
33
-        return new self($props_n_values, true, $timezone);
34
-    }
26
+	/**
27
+	 * @param array  $props_n_values
28
+	 * @param string $timezone
29
+	 * @return EE_Ticket_Price
30
+	 */
31
+	public static function new_instance_from_db($props_n_values = array(), $timezone = '')
32
+	{
33
+		return new self($props_n_values, true, $timezone);
34
+	}
35 35
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Ticket_Template.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_Ticket_Template extends EE_Base_Class
13 13
 {
14 14
 
15
-    /**
16
-     * @param array  $props_n_values
17
-     * @param string $timezone
18
-     * @return EE_Ticket_Template|mixed
19
-     */
20
-    public static function new_instance($props_n_values = array(), $timezone = '')
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 string $timezone
18
+	 * @return EE_Ticket_Template|mixed
19
+	 */
20
+	public static function new_instance($props_n_values = array(), $timezone = '')
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 string $timezone
30
-     * @return EE_Ticket_Template
31
-     */
32
-    public static function new_instance_from_db($props_n_values = array(), $timezone = '')
33
-    {
34
-        return new self($props_n_values, true, $timezone);
35
-    }
27
+	/**
28
+	 * @param array  $props_n_values
29
+	 * @param string $timezone
30
+	 * @return EE_Ticket_Template
31
+	 */
32
+	public static function new_instance_from_db($props_n_values = array(), $timezone = '')
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_Extra_Meta.class.php 1 patch
Indentation   +111 added lines, -111 removed lines patch added patch discarded remove patch
@@ -10,115 +10,115 @@
 block discarded – undo
10 10
 class EE_Extra_Meta extends EE_Base_Class
11 11
 {
12 12
 
13
-    /**
14
-     * @param array $props_n_values
15
-     * @return EE_Extra_Meta|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_Extra_Meta
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 FK_ID
36
-     *
37
-     * @return int
38
-     */
39
-    public function FK_ID()
40
-    {
41
-        return $this->get('FK_ID');
42
-    }
43
-
44
-
45
-    /**
46
-     * Sets FK_ID
47
-     *
48
-     * @param int $FK_ID
49
-     * @return boolean
50
-     */
51
-    public function set_FK_ID($FK_ID)
52
-    {
53
-        $this->set('FK_ID', $FK_ID);
54
-    }
55
-
56
-
57
-    /**
58
-     * Gets model
59
-     *
60
-     * @return string
61
-     */
62
-    public function model()
63
-    {
64
-        return $this->get('EXM_model');
65
-    }
66
-
67
-
68
-    /**
69
-     * Sets model
70
-     *
71
-     * @param string $model
72
-     * @return boolean
73
-     */
74
-    public function set_model($model)
75
-    {
76
-        $this->set('EXM_model', $model);
77
-    }
78
-
79
-
80
-    /**
81
-     * Gets key
82
-     *
83
-     * @return string
84
-     */
85
-    public function key()
86
-    {
87
-        return $this->get('EXM_key');
88
-    }
89
-
90
-
91
-    /**
92
-     * Sets key
93
-     *
94
-     * @param string $key
95
-     * @return boolean
96
-     */
97
-    public function set_key($key)
98
-    {
99
-        $this->set('EXM_key', $key);
100
-    }
101
-
102
-
103
-    /**
104
-     * Gets value
105
-     *
106
-     * @return string
107
-     */
108
-    public function value()
109
-    {
110
-        return $this->get('EXM_value');
111
-    }
112
-
113
-
114
-    /**
115
-     * Sets value
116
-     *
117
-     * @param string $value
118
-     * @return boolean
119
-     */
120
-    public function set_value($value)
121
-    {
122
-        $this->set('EXM_value', $value);
123
-    }
13
+	/**
14
+	 * @param array $props_n_values
15
+	 * @return EE_Extra_Meta|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_Extra_Meta
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 FK_ID
36
+	 *
37
+	 * @return int
38
+	 */
39
+	public function FK_ID()
40
+	{
41
+		return $this->get('FK_ID');
42
+	}
43
+
44
+
45
+	/**
46
+	 * Sets FK_ID
47
+	 *
48
+	 * @param int $FK_ID
49
+	 * @return boolean
50
+	 */
51
+	public function set_FK_ID($FK_ID)
52
+	{
53
+		$this->set('FK_ID', $FK_ID);
54
+	}
55
+
56
+
57
+	/**
58
+	 * Gets model
59
+	 *
60
+	 * @return string
61
+	 */
62
+	public function model()
63
+	{
64
+		return $this->get('EXM_model');
65
+	}
66
+
67
+
68
+	/**
69
+	 * Sets model
70
+	 *
71
+	 * @param string $model
72
+	 * @return boolean
73
+	 */
74
+	public function set_model($model)
75
+	{
76
+		$this->set('EXM_model', $model);
77
+	}
78
+
79
+
80
+	/**
81
+	 * Gets key
82
+	 *
83
+	 * @return string
84
+	 */
85
+	public function key()
86
+	{
87
+		return $this->get('EXM_key');
88
+	}
89
+
90
+
91
+	/**
92
+	 * Sets key
93
+	 *
94
+	 * @param string $key
95
+	 * @return boolean
96
+	 */
97
+	public function set_key($key)
98
+	{
99
+		$this->set('EXM_key', $key);
100
+	}
101
+
102
+
103
+	/**
104
+	 * Gets value
105
+	 *
106
+	 * @return string
107
+	 */
108
+	public function value()
109
+	{
110
+		return $this->get('EXM_value');
111
+	}
112
+
113
+
114
+	/**
115
+	 * Sets value
116
+	 *
117
+	 * @param string $value
118
+	 * @return boolean
119
+	 */
120
+	public function set_value($value)
121
+	{
122
+		$this->set('EXM_value', $value);
123
+	}
124 124
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Post_Meta.class.php 1 patch
Indentation   +108 added lines, -108 removed lines patch added patch discarded remove patch
@@ -10,112 +10,112 @@
 block discarded – undo
10 10
 class EE_Post_Meta extends EE_Base_Class
11 11
 {
12 12
 
13
-    /**
14
-     * @param array $props_n_values
15
-     * @return EE_Post_Meta|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_Post_Meta
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 meta_id
36
-     *
37
-     * @return int
38
-     */
39
-    public function meta_id()
40
-    {
41
-        return $this->get('meta_id');
42
-    }
43
-
44
-
45
-    /**
46
-     * Sets meta_id
47
-     *
48
-     * @param int $meta_id
49
-     * @return boolean
50
-     */
51
-    public function set_meta_id($meta_id)
52
-    {
53
-        return $this->set('meta_id', $meta_id);
54
-    }
55
-
56
-    /**
57
-     * Gets post_id
58
-     *
59
-     * @return int
60
-     */
61
-    public function post_id()
62
-    {
63
-        return $this->get('post_id');
64
-    }
65
-
66
-
67
-    /**
68
-     * Sets post_id
69
-     *
70
-     * @param int $post_id
71
-     * @return boolean
72
-     */
73
-    public function set_post_id($post_id)
74
-    {
75
-        return $this->set('post_id', $post_id);
76
-    }
77
-
78
-    /**
79
-     * Gets meta_key
80
-     *
81
-     * @return string
82
-     */
83
-    public function meta_key()
84
-    {
85
-        return $this->get('meta_key');
86
-    }
87
-
88
-
89
-    /**
90
-     * Sets meta_key
91
-     *
92
-     * @param string $meta_key
93
-     * @return boolean
94
-     */
95
-    public function set_meta_key($meta_key)
96
-    {
97
-        return $this->set('meta_key', $meta_key);
98
-    }
99
-
100
-    /**
101
-     * Gets meta_value
102
-     *
103
-     * @return mixed
104
-     */
105
-    public function meta_value()
106
-    {
107
-        return $this->get('meta_value');
108
-    }
109
-
110
-
111
-    /**
112
-     * Sets meta_value
113
-     *
114
-     * @param mixed $meta_value
115
-     * @return boolean
116
-     */
117
-    public function set_meta_value($meta_value)
118
-    {
119
-        return $this->set('meta_value', $meta_value);
120
-    }
13
+	/**
14
+	 * @param array $props_n_values
15
+	 * @return EE_Post_Meta|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_Post_Meta
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 meta_id
36
+	 *
37
+	 * @return int
38
+	 */
39
+	public function meta_id()
40
+	{
41
+		return $this->get('meta_id');
42
+	}
43
+
44
+
45
+	/**
46
+	 * Sets meta_id
47
+	 *
48
+	 * @param int $meta_id
49
+	 * @return boolean
50
+	 */
51
+	public function set_meta_id($meta_id)
52
+	{
53
+		return $this->set('meta_id', $meta_id);
54
+	}
55
+
56
+	/**
57
+	 * Gets post_id
58
+	 *
59
+	 * @return int
60
+	 */
61
+	public function post_id()
62
+	{
63
+		return $this->get('post_id');
64
+	}
65
+
66
+
67
+	/**
68
+	 * Sets post_id
69
+	 *
70
+	 * @param int $post_id
71
+	 * @return boolean
72
+	 */
73
+	public function set_post_id($post_id)
74
+	{
75
+		return $this->set('post_id', $post_id);
76
+	}
77
+
78
+	/**
79
+	 * Gets meta_key
80
+	 *
81
+	 * @return string
82
+	 */
83
+	public function meta_key()
84
+	{
85
+		return $this->get('meta_key');
86
+	}
87
+
88
+
89
+	/**
90
+	 * Sets meta_key
91
+	 *
92
+	 * @param string $meta_key
93
+	 * @return boolean
94
+	 */
95
+	public function set_meta_key($meta_key)
96
+	{
97
+		return $this->set('meta_key', $meta_key);
98
+	}
99
+
100
+	/**
101
+	 * Gets meta_value
102
+	 *
103
+	 * @return mixed
104
+	 */
105
+	public function meta_value()
106
+	{
107
+		return $this->get('meta_value');
108
+	}
109
+
110
+
111
+	/**
112
+	 * Sets meta_value
113
+	 *
114
+	 * @param mixed $meta_value
115
+	 * @return boolean
116
+	 */
117
+	public function set_meta_value($meta_value)
118
+	{
119
+		return $this->set('meta_value', $meta_value);
120
+	}
121 121
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Question.class.php 2 patches
Indentation   +657 added lines, -657 removed lines patch added patch discarded remove patch
@@ -12,661 +12,661 @@
 block discarded – undo
12 12
 class EE_Question extends EE_Soft_Delete_Base_Class implements EEI_Duplicatable
13 13
 {
14 14
 
15
-    /**
16
-     *
17
-     * @param array  $props_n_values          incoming values
18
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
19
-     *                                        used.)
20
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
21
-     *                                        date_format and the second value is the time format
22
-     * @return EE_Question
23
-     */
24
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
25
-    {
26
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
27
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
28
-    }
29
-
30
-
31
-    /**
32
-     * @param array  $props_n_values  incoming values from the database
33
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
34
-     *                                the website will be used.
35
-     * @return EE_Question
36
-     */
37
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
38
-    {
39
-        return new self($props_n_values, true, $timezone);
40
-    }
41
-
42
-
43
-    /**
44
-     *        Set    Question display text
45
-     *
46
-     * @access        public
47
-     * @param string $QST_display_text
48
-     */
49
-    public function set_display_text($QST_display_text = '')
50
-    {
51
-        $this->set('QST_display_text', $QST_display_text);
52
-    }
53
-
54
-
55
-    /**
56
-     *        Set    Question admin text
57
-     *
58
-     * @access        public
59
-     * @param        string $QST_admin_label
60
-     */
61
-    public function set_admin_label($QST_admin_label = '')
62
-    {
63
-        $this->set('QST_admin_label', $QST_admin_label);
64
-    }
65
-
66
-
67
-    /**
68
-     *        Set    system name
69
-     *
70
-     * @access        public
71
-     * @param        mixed $QST_system
72
-     */
73
-    public function set_system_ID($QST_system = '')
74
-    {
75
-        $this->set('QST_system', $QST_system);
76
-    }
77
-
78
-
79
-    /**
80
-     *        Set    question's type
81
-     *
82
-     * @access        public
83
-     * @param        string $QST_type
84
-     */
85
-    public function set_question_type($QST_type = '')
86
-    {
87
-        $this->set('QST_type', $QST_type);
88
-    }
89
-
90
-
91
-    /**
92
-     *        Sets whether this question must be answered when presented in a form
93
-     *
94
-     * @access        public
95
-     * @param        bool $QST_required
96
-     */
97
-    public function set_required($QST_required = false)
98
-    {
99
-        $this->set('QST_required', $QST_required);
100
-    }
101
-
102
-
103
-    /**
104
-     *        Set    Question display text
105
-     *
106
-     * @access        public
107
-     * @param        string $QST_required_text
108
-     */
109
-    public function set_required_text($QST_required_text = '')
110
-    {
111
-        $this->set('QST_required_text', $QST_required_text);
112
-    }
113
-
114
-
115
-    /**
116
-     *        Sets the order of this question when placed in a sequence of questions
117
-     *
118
-     * @access        public
119
-     * @param        int $QST_order
120
-     */
121
-    public function set_order($QST_order = 0)
122
-    {
123
-        $this->set('QST_order', $QST_order);
124
-    }
125
-
126
-
127
-    /**
128
-     *        Sets whether the question is admin-only
129
-     *
130
-     * @access        public
131
-     * @param        bool $QST_admin_only
132
-     */
133
-    public function set_admin_only($QST_admin_only = false)
134
-    {
135
-        $this->set('QST_admin_only', $QST_admin_only);
136
-    }
137
-
138
-
139
-    /**
140
-     *        Sets the wordpress user ID on the question
141
-     *
142
-     * @access        public
143
-     * @param        int $QST_wp_user
144
-     */
145
-    public function set_wp_user($QST_wp_user = 1)
146
-    {
147
-        $this->set('QST_wp_user', $QST_wp_user);
148
-    }
149
-
150
-
151
-    /**
152
-     *        Sets whether the question has been deleted
153
-     *        (we use this boolean instead of actually
154
-     *        deleting it because when users delete this question
155
-     *        they really want to remove the question from future
156
-     *        forms, BUT keep their old answers which depend
157
-     *        on this record actually existing.
158
-     *
159
-     * @access        public
160
-     * @param    bool $QST_deleted
161
-     */
162
-    public function set_deleted($QST_deleted = false)
163
-    {
164
-        $this->set('QST_deleted', $QST_deleted);
165
-    }
166
-
167
-
168
-    /**
169
-     * returns the text for displaying the question to users
170
-     *
171
-     * @access public
172
-     * @return string
173
-     */
174
-    public function display_text()
175
-    {
176
-        return $this->get('QST_display_text');
177
-    }
178
-
179
-
180
-    /**
181
-     * returns the text for the administrative label
182
-     *
183
-     * @access public
184
-     * @return string
185
-     */
186
-    public function admin_label()
187
-    {
188
-        return $this->get('QST_admin_label');
189
-    }
190
-
191
-
192
-    /**
193
-     * returns the attendee column name for this question
194
-     *
195
-     * @access public
196
-     * @return string
197
-     */
198
-    public function system_ID()
199
-    {
200
-        return $this->get('QST_system');
201
-    }
202
-
203
-
204
-    /**
205
-     * returns either a string of 'text', 'textfield', etc.
206
-     *
207
-     * @access public
208
-     * @return boolean
209
-     */
210
-    public function required()
211
-    {
212
-        return $this->get('QST_required');
213
-    }
214
-
215
-
216
-    /**
217
-     * returns the text which should be displayed when a user
218
-     * doesn't answer this question in a form
219
-     *
220
-     * @access public
221
-     * @return string
222
-     */
223
-    public function required_text()
224
-    {
225
-        return $this->get('QST_required_text');
226
-    }
227
-
228
-
229
-    /**
230
-     * returns the type of this question
231
-     *
232
-     * @access public
233
-     * @return string
234
-     */
235
-    public function type()
236
-    {
237
-        return $this->get('QST_type');
238
-    }
239
-
240
-
241
-    /**
242
-     * returns an integer showing where this question should
243
-     * be placed in a sequence of questions
244
-     *
245
-     * @access public
246
-     * @return int
247
-     */
248
-    public function order()
249
-    {
250
-        return $this->get('QST_order');
251
-    }
252
-
253
-
254
-    /**
255
-     * returns whether this question should only appears to admins,
256
-     * or to everyone
257
-     *
258
-     * @access public
259
-     * @return boolean
260
-     */
261
-    public function admin_only()
262
-    {
263
-        return $this->get('QST_admin_only');
264
-    }
265
-
266
-
267
-    /**
268
-     * returns the id the wordpress user who created this question
269
-     *
270
-     * @access public
271
-     * @return int
272
-     */
273
-    public function wp_user()
274
-    {
275
-        return $this->get('QST_wp_user');
276
-    }
277
-
278
-
279
-    /**
280
-     * returns whether this question has been marked as 'deleted'
281
-     *
282
-     * @access public
283
-     * @return boolean
284
-     */
285
-    public function deleted()
286
-    {
287
-        return $this->get('QST_deleted');
288
-    }
289
-
290
-
291
-    /**
292
-     * Gets an array of related EE_Answer  to this EE_Question
293
-     *
294
-     * @return EE_Answer[]
295
-     */
296
-    public function answers()
297
-    {
298
-        return $this->get_many_related('Answer');
299
-    }
300
-
301
-
302
-    /**
303
-     * Boolean check for if there are answers on this question in th db
304
-     *
305
-     * @return boolean true = has answers, false = no answers.
306
-     */
307
-    public function has_answers()
308
-    {
309
-        return $this->count_related('Answer') > 0 ? true : false;
310
-    }
311
-
312
-
313
-    /**
314
-     * gets an array of EE_Question_Group which relate to this question
315
-     *
316
-     * @return EE_Question_Group[]
317
-     */
318
-    public function question_groups()
319
-    {
320
-        return $this->get_many_related('Question_Group');
321
-    }
322
-
323
-
324
-    /**
325
-     * Returns all the options for this question. By default, it returns only the not-yet-deleted ones.
326
-     *
327
-     * @param boolean      $notDeletedOptionsOnly            1
328
-     *                                                       whether to return ALL options, or only the ones which have
329
-     *                                                       not yet been deleleted
330
-     * @param string|array $selected_value_to_always_include , when retrieving options to an ANSWERED question,
331
-     *                                                       we want to usually only show non-deleted options AND the
332
-     *                                                       value that was selected for the answer, whether it was
333
-     *                                                       trashed or not.
334
-     * @return EE_Question_Option[]
335
-     */
336
-    public function options($notDeletedOptionsOnly = true, $selected_value_to_always_include = null)
337
-    {
338
-        if (! $this->ID()) {
339
-            return array();
340
-        }
341
-        $query_params = array();
342
-        if ($selected_value_to_always_include) {
343
-            if (is_array($selected_value_to_always_include)) {
344
-                $query_params[0]['OR*options-query']['QSO_value'] = array('IN', $selected_value_to_always_include);
345
-            } else {
346
-                $query_params[0]['OR*options-query']['QSO_value'] = $selected_value_to_always_include;
347
-            }
348
-        }
349
-        if ($notDeletedOptionsOnly) {
350
-            $query_params[0]['OR*options-query']['QSO_deleted'] = false;
351
-        }
352
-        // order by QSO_order
353
-        $query_params['order_by'] = array('QSO_order' => 'ASC');
354
-        return $this->get_many_related('Question_Option', $query_params);
355
-    }
356
-
357
-
358
-    /**
359
-     * returns an array of EE_Question_Options which relate to this question
360
-     *
361
-     * @return \EE_Question_Option[]
362
-     */
363
-    public function temp_options()
364
-    {
365
-        return $this->_model_relations['Question_Option'];
366
-    }
367
-
368
-
369
-    /**
370
-     * Adds an option for this question. Note: if the option were previously associated with a different
371
-     * Question, that relationship will be overwritten.
372
-     *
373
-     * @param EE_Question_Option $option
374
-     * @return boolean success
375
-     */
376
-    public function add_option(EE_Question_Option $option)
377
-    {
378
-        return $this->_add_relation_to($option, 'Question_Option');
379
-    }
380
-
381
-
382
-    /**
383
-     * Adds an option directly to this question without saving to the db
384
-     *
385
-     * @param EE_Question_Option $option
386
-     * @return boolean success
387
-     */
388
-    public function add_temp_option(EE_Question_Option $option)
389
-    {
390
-        $this->_model_relations['Question_Option'][] = $option;
391
-        return true;
392
-    }
393
-
394
-
395
-    /**
396
-     * Marks the option as deleted.
397
-     *
398
-     * @param EE_Question_Option $option
399
-     * @return boolean success
400
-     */
401
-    public function remove_option(EE_Question_Option $option)
402
-    {
403
-        return $this->_remove_relation_to($option, 'Question_Option');
404
-    }
405
-
406
-
407
-    /**
408
-     * @return bool
409
-     */
410
-    public function is_system_question()
411
-    {
412
-        $system_ID = $this->get('QST_system');
413
-        return ! empty($system_ID) ? true : false;
414
-    }
415
-
416
-
417
-    /**
418
-     * The purpose of this method is set the question order this question order to be the max out of all questions
419
-     *
420
-     * @access public
421
-     * @return void
422
-     */
423
-    public function set_order_to_latest()
424
-    {
425
-        $latest_order = $this->get_model()->get_latest_question_order();
426
-        $latest_order++;
427
-        $this->set('QST_order', $latest_order);
428
-    }
429
-
430
-
431
-    /**
432
-     * Retrieves the list of allowed question types from the model.
433
-     *
434
-     * @return string[]
435
-     */
436
-    private function _allowed_question_types()
437
-    {
438
-        $questionModel = $this->get_model();
439
-        /* @var $questionModel EEM_Question */
440
-        return $questionModel->allowed_question_types();
441
-    }
442
-
443
-    /**
444
-     * Duplicates this question and its question options
445
-     *
446
-     * @return \EE_Question
447
-     */
448
-    public function duplicate($options = array())
449
-    {
450
-        $new_question = clone $this;
451
-        $new_question->set('QST_ID', null);
452
-        $new_question->set_display_text(sprintf(__('%s **Duplicate**', 'event_espresso'), $this->display_text()));
453
-        $new_question->set_admin_label(sprintf(__('%s **Duplicate**', 'event_espresso'), $this->admin_label()));
454
-        $new_question->set_system_ID(null);
455
-        $new_question->set_wp_user(get_current_user_id());
456
-        // if we're duplicating a trashed question, assume we don't want the new one to be trashed
457
-        $new_question->set_deleted(false);
458
-        $success = $new_question->save();
459
-        if ($success) {
460
-            // we don't totally want to duplicate the question options, because we want them to be for the NEW question
461
-            foreach ($this->options() as $question_option) {
462
-                $question_option->duplicate(array('QST_ID' => $new_question->ID()));
463
-            }
464
-            return $new_question;
465
-        } else {
466
-            return null;
467
-        }
468
-    }
469
-
470
-    /**
471
-     * Returns the question's maximum allowed response size
472
-     *
473
-     * @return int|float
474
-     */
475
-    public function max()
476
-    {
477
-        return $this->get('QST_max');
478
-    }
479
-
480
-    /**
481
-     * Sets the question's maximum allowed response size
482
-     *
483
-     * @param int|float $new_max
484
-     * @return void
485
-     */
486
-    public function set_max($new_max)
487
-    {
488
-        $this->set('QST_max', $new_max);
489
-    }
490
-
491
-
492
-    /**
493
-     * Creates a form input from this question which can be used in HTML forms
494
-     *
495
-     * @param EE_Registration $registration
496
-     * @param EE_Answer       $answer
497
-     * @param array           $input_constructor_args
498
-     * @return EE_Form_Input_Base
499
-     */
500
-    public function generate_form_input($registration = null, $answer = null, $input_constructor_args = array())
501
-    {
502
-        $identifier = $this->is_system_question() ? $this->system_ID() : $this->ID();
503
-
504
-        $input_constructor_args = array_merge(
505
-            array(
506
-                'required'                          => $this->required() ? true : false,
507
-                'html_label_text'                   => $this->display_text(),
508
-                'required_validation_error_message' => $this->required_text(),
509
-            ),
510
-            $input_constructor_args
511
-        );
512
-        if (! $answer instanceof EE_Answer && $registration instanceof EE_Registration) {
513
-            $answer = EEM_Answer::instance()->get_registration_question_answer_object($registration, $this->ID());
514
-        }
515
-        // has this question been answered ?
516
-        if ($answer instanceof EE_Answer
517
-            && $answer->value() !== ''
518
-        ) {
519
-            // answer gets htmlspecialchars called on it, undo that please
520
-            // because the form input's display strategy may call esc_attr too
521
-            // which also does html special characters
522
-            $values_with_html_special_chars = $answer->value();
523
-            if (is_array($values_with_html_special_chars)) {
524
-                $default_value = array_map('htmlspecialchars_decode', $values_with_html_special_chars);
525
-            } else {
526
-                $default_value = htmlspecialchars_decode($values_with_html_special_chars);
527
-            }
528
-            $input_constructor_args['default'] = $default_value;
529
-        }
530
-        $max_max_for_question = EEM_Question::instance()->absolute_max_for_system_question($this->system_ID());
531
-        if (in_array(
532
-            $this->type(),
533
-            EEM_Question::instance()->questionTypesWithMaxLength(),
534
-            true
535
-        )) {
536
-            $input_constructor_args['validation_strategies'][] = new EE_Max_Length_Validation_Strategy(
537
-                null,
538
-                min($max_max_for_question, $this->max())
539
-            );
540
-        }
541
-        $input_constructor_args = apply_filters(
542
-            'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__input_constructor_args',
543
-            $input_constructor_args,
544
-            $registration,
545
-            $this,
546
-            $answer
547
-        );
548
-
549
-        $result = null;
550
-        switch ($this->type()) {
551
-            // Text
552
-            case EEM_Question::QST_type_text:
553
-                $result = new EE_Text_Input($input_constructor_args);
554
-                break;
555
-            // Textarea
556
-            case EEM_Question::QST_type_textarea:
557
-                $result = new EE_Text_Area_Input($input_constructor_args);
558
-                break;
559
-            // Radio Buttons
560
-            case EEM_Question::QST_type_radio:
561
-                $result = new EE_Radio_Button_Input($this->options(), $input_constructor_args);
562
-                break;
563
-            // Dropdown
564
-            case EEM_Question::QST_type_dropdown:
565
-                $result = new EE_Select_Input($this->options(), $input_constructor_args);
566
-                break;
567
-            // State Dropdown
568
-            case EEM_Question::QST_type_state:
569
-                $state_options = apply_filters(
570
-                    'FHEE__EE_Question__generate_form_input__state_options',
571
-                    null,
572
-                    $this,
573
-                    $registration,
574
-                    $answer
575
-                );
576
-                $result = new EE_State_Select_Input($state_options, $input_constructor_args);
577
-                break;
578
-            // Country Dropdown
579
-            case EEM_Question::QST_type_country:
580
-                $country_options = apply_filters(
581
-                    'FHEE__EE_Question__generate_form_input__country_options',
582
-                    null,
583
-                    $this,
584
-                    $registration,
585
-                    $answer
586
-                );
587
-                $result = new EE_Country_Select_Input($country_options, $input_constructor_args);
588
-                break;
589
-            // Checkboxes
590
-            case EEM_Question::QST_type_checkbox:
591
-                $result = new EE_Checkbox_Multi_Input($this->options(), $input_constructor_args);
592
-                break;
593
-            // Date
594
-            case EEM_Question::QST_type_date:
595
-                $result = new EE_Datepicker_Input($input_constructor_args);
596
-                break;
597
-            case EEM_Question::QST_type_html_textarea:
598
-                $input_constructor_args['validation_strategies'][] = new EE_Simple_HTML_Validation_Strategy();
599
-                $result = new EE_Text_Area_Input($input_constructor_args);
600
-                $result->remove_validation_strategy('EE_Plaintext_Validation_Strategy');
601
-                break;
602
-            case EEM_Question::QST_type_email:
603
-                $result = new EE_Email_Input($input_constructor_args);
604
-                break;
605
-            case EEM_Question::QST_type_us_phone:
606
-                $result = new EE_Phone_Input($input_constructor_args);
607
-                break;
608
-            case EEM_Question::QST_type_int:
609
-                $result = new EE_Integer_Input($input_constructor_args);
610
-                break;
611
-            case EEM_Question::QST_type_decimal:
612
-                $result = new EE_Float_Input($input_constructor_args);
613
-                break;
614
-            case EEM_Question::QST_type_url:
615
-                $input_constructor_args['validation_strategies'][] = new EE_URL_Validation_Strategy();
616
-                $result = new EE_Text_Input($input_constructor_args);
617
-                break;
618
-            case EEM_Question::QST_type_year:
619
-                $result = new EE_Year_Input(
620
-                    $input_constructor_args,
621
-                    apply_filters(
622
-                        'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__four_digit',
623
-                        true,
624
-                        $this
625
-                    ),
626
-                    apply_filters(
627
-                        'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__early_range',
628
-                        100,
629
-                        $this
630
-                    ),
631
-                    apply_filters(
632
-                        'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__end_range',
633
-                        100,
634
-                        $this
635
-                    )
636
-                );
637
-                break;
638
-            case EEM_Question::QST_type_multi_select:
639
-                $result = new EE_Select_Multiple_Input($this->options(), $input_constructor_args);
640
-                break;
641
-            // fallback
642
-            default:
643
-                $default_input = apply_filters(
644
-                    'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__default',
645
-                    null,
646
-                    $this->type(),
647
-                    $this,
648
-                    $input_constructor_args
649
-                );
650
-                if (! $default_input) {
651
-                    $default_input = new EE_Text_Input($input_constructor_args);
652
-                }
653
-                $result = $default_input;
654
-        }
655
-        return apply_filters('FHEE__EE_Question__generate_form_input__return', $result, $registration, $this, $answer);
656
-    }
657
-
658
-
659
-    /**
660
-     * Returns whether or not this question type should have question option entries
661
-     *
662
-     * @return bool
663
-     */
664
-    public function should_have_question_options()
665
-    {
666
-        return in_array(
667
-            $this->type(),
668
-            $this->_model->question_types_with_options(),
669
-            true
670
-        );
671
-    }
15
+	/**
16
+	 *
17
+	 * @param array  $props_n_values          incoming values
18
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
19
+	 *                                        used.)
20
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
21
+	 *                                        date_format and the second value is the time format
22
+	 * @return EE_Question
23
+	 */
24
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
25
+	{
26
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
27
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
28
+	}
29
+
30
+
31
+	/**
32
+	 * @param array  $props_n_values  incoming values from the database
33
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
34
+	 *                                the website will be used.
35
+	 * @return EE_Question
36
+	 */
37
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
38
+	{
39
+		return new self($props_n_values, true, $timezone);
40
+	}
41
+
42
+
43
+	/**
44
+	 *        Set    Question display text
45
+	 *
46
+	 * @access        public
47
+	 * @param string $QST_display_text
48
+	 */
49
+	public function set_display_text($QST_display_text = '')
50
+	{
51
+		$this->set('QST_display_text', $QST_display_text);
52
+	}
53
+
54
+
55
+	/**
56
+	 *        Set    Question admin text
57
+	 *
58
+	 * @access        public
59
+	 * @param        string $QST_admin_label
60
+	 */
61
+	public function set_admin_label($QST_admin_label = '')
62
+	{
63
+		$this->set('QST_admin_label', $QST_admin_label);
64
+	}
65
+
66
+
67
+	/**
68
+	 *        Set    system name
69
+	 *
70
+	 * @access        public
71
+	 * @param        mixed $QST_system
72
+	 */
73
+	public function set_system_ID($QST_system = '')
74
+	{
75
+		$this->set('QST_system', $QST_system);
76
+	}
77
+
78
+
79
+	/**
80
+	 *        Set    question's type
81
+	 *
82
+	 * @access        public
83
+	 * @param        string $QST_type
84
+	 */
85
+	public function set_question_type($QST_type = '')
86
+	{
87
+		$this->set('QST_type', $QST_type);
88
+	}
89
+
90
+
91
+	/**
92
+	 *        Sets whether this question must be answered when presented in a form
93
+	 *
94
+	 * @access        public
95
+	 * @param        bool $QST_required
96
+	 */
97
+	public function set_required($QST_required = false)
98
+	{
99
+		$this->set('QST_required', $QST_required);
100
+	}
101
+
102
+
103
+	/**
104
+	 *        Set    Question display text
105
+	 *
106
+	 * @access        public
107
+	 * @param        string $QST_required_text
108
+	 */
109
+	public function set_required_text($QST_required_text = '')
110
+	{
111
+		$this->set('QST_required_text', $QST_required_text);
112
+	}
113
+
114
+
115
+	/**
116
+	 *        Sets the order of this question when placed in a sequence of questions
117
+	 *
118
+	 * @access        public
119
+	 * @param        int $QST_order
120
+	 */
121
+	public function set_order($QST_order = 0)
122
+	{
123
+		$this->set('QST_order', $QST_order);
124
+	}
125
+
126
+
127
+	/**
128
+	 *        Sets whether the question is admin-only
129
+	 *
130
+	 * @access        public
131
+	 * @param        bool $QST_admin_only
132
+	 */
133
+	public function set_admin_only($QST_admin_only = false)
134
+	{
135
+		$this->set('QST_admin_only', $QST_admin_only);
136
+	}
137
+
138
+
139
+	/**
140
+	 *        Sets the wordpress user ID on the question
141
+	 *
142
+	 * @access        public
143
+	 * @param        int $QST_wp_user
144
+	 */
145
+	public function set_wp_user($QST_wp_user = 1)
146
+	{
147
+		$this->set('QST_wp_user', $QST_wp_user);
148
+	}
149
+
150
+
151
+	/**
152
+	 *        Sets whether the question has been deleted
153
+	 *        (we use this boolean instead of actually
154
+	 *        deleting it because when users delete this question
155
+	 *        they really want to remove the question from future
156
+	 *        forms, BUT keep their old answers which depend
157
+	 *        on this record actually existing.
158
+	 *
159
+	 * @access        public
160
+	 * @param    bool $QST_deleted
161
+	 */
162
+	public function set_deleted($QST_deleted = false)
163
+	{
164
+		$this->set('QST_deleted', $QST_deleted);
165
+	}
166
+
167
+
168
+	/**
169
+	 * returns the text for displaying the question to users
170
+	 *
171
+	 * @access public
172
+	 * @return string
173
+	 */
174
+	public function display_text()
175
+	{
176
+		return $this->get('QST_display_text');
177
+	}
178
+
179
+
180
+	/**
181
+	 * returns the text for the administrative label
182
+	 *
183
+	 * @access public
184
+	 * @return string
185
+	 */
186
+	public function admin_label()
187
+	{
188
+		return $this->get('QST_admin_label');
189
+	}
190
+
191
+
192
+	/**
193
+	 * returns the attendee column name for this question
194
+	 *
195
+	 * @access public
196
+	 * @return string
197
+	 */
198
+	public function system_ID()
199
+	{
200
+		return $this->get('QST_system');
201
+	}
202
+
203
+
204
+	/**
205
+	 * returns either a string of 'text', 'textfield', etc.
206
+	 *
207
+	 * @access public
208
+	 * @return boolean
209
+	 */
210
+	public function required()
211
+	{
212
+		return $this->get('QST_required');
213
+	}
214
+
215
+
216
+	/**
217
+	 * returns the text which should be displayed when a user
218
+	 * doesn't answer this question in a form
219
+	 *
220
+	 * @access public
221
+	 * @return string
222
+	 */
223
+	public function required_text()
224
+	{
225
+		return $this->get('QST_required_text');
226
+	}
227
+
228
+
229
+	/**
230
+	 * returns the type of this question
231
+	 *
232
+	 * @access public
233
+	 * @return string
234
+	 */
235
+	public function type()
236
+	{
237
+		return $this->get('QST_type');
238
+	}
239
+
240
+
241
+	/**
242
+	 * returns an integer showing where this question should
243
+	 * be placed in a sequence of questions
244
+	 *
245
+	 * @access public
246
+	 * @return int
247
+	 */
248
+	public function order()
249
+	{
250
+		return $this->get('QST_order');
251
+	}
252
+
253
+
254
+	/**
255
+	 * returns whether this question should only appears to admins,
256
+	 * or to everyone
257
+	 *
258
+	 * @access public
259
+	 * @return boolean
260
+	 */
261
+	public function admin_only()
262
+	{
263
+		return $this->get('QST_admin_only');
264
+	}
265
+
266
+
267
+	/**
268
+	 * returns the id the wordpress user who created this question
269
+	 *
270
+	 * @access public
271
+	 * @return int
272
+	 */
273
+	public function wp_user()
274
+	{
275
+		return $this->get('QST_wp_user');
276
+	}
277
+
278
+
279
+	/**
280
+	 * returns whether this question has been marked as 'deleted'
281
+	 *
282
+	 * @access public
283
+	 * @return boolean
284
+	 */
285
+	public function deleted()
286
+	{
287
+		return $this->get('QST_deleted');
288
+	}
289
+
290
+
291
+	/**
292
+	 * Gets an array of related EE_Answer  to this EE_Question
293
+	 *
294
+	 * @return EE_Answer[]
295
+	 */
296
+	public function answers()
297
+	{
298
+		return $this->get_many_related('Answer');
299
+	}
300
+
301
+
302
+	/**
303
+	 * Boolean check for if there are answers on this question in th db
304
+	 *
305
+	 * @return boolean true = has answers, false = no answers.
306
+	 */
307
+	public function has_answers()
308
+	{
309
+		return $this->count_related('Answer') > 0 ? true : false;
310
+	}
311
+
312
+
313
+	/**
314
+	 * gets an array of EE_Question_Group which relate to this question
315
+	 *
316
+	 * @return EE_Question_Group[]
317
+	 */
318
+	public function question_groups()
319
+	{
320
+		return $this->get_many_related('Question_Group');
321
+	}
322
+
323
+
324
+	/**
325
+	 * Returns all the options for this question. By default, it returns only the not-yet-deleted ones.
326
+	 *
327
+	 * @param boolean      $notDeletedOptionsOnly            1
328
+	 *                                                       whether to return ALL options, or only the ones which have
329
+	 *                                                       not yet been deleleted
330
+	 * @param string|array $selected_value_to_always_include , when retrieving options to an ANSWERED question,
331
+	 *                                                       we want to usually only show non-deleted options AND the
332
+	 *                                                       value that was selected for the answer, whether it was
333
+	 *                                                       trashed or not.
334
+	 * @return EE_Question_Option[]
335
+	 */
336
+	public function options($notDeletedOptionsOnly = true, $selected_value_to_always_include = null)
337
+	{
338
+		if (! $this->ID()) {
339
+			return array();
340
+		}
341
+		$query_params = array();
342
+		if ($selected_value_to_always_include) {
343
+			if (is_array($selected_value_to_always_include)) {
344
+				$query_params[0]['OR*options-query']['QSO_value'] = array('IN', $selected_value_to_always_include);
345
+			} else {
346
+				$query_params[0]['OR*options-query']['QSO_value'] = $selected_value_to_always_include;
347
+			}
348
+		}
349
+		if ($notDeletedOptionsOnly) {
350
+			$query_params[0]['OR*options-query']['QSO_deleted'] = false;
351
+		}
352
+		// order by QSO_order
353
+		$query_params['order_by'] = array('QSO_order' => 'ASC');
354
+		return $this->get_many_related('Question_Option', $query_params);
355
+	}
356
+
357
+
358
+	/**
359
+	 * returns an array of EE_Question_Options which relate to this question
360
+	 *
361
+	 * @return \EE_Question_Option[]
362
+	 */
363
+	public function temp_options()
364
+	{
365
+		return $this->_model_relations['Question_Option'];
366
+	}
367
+
368
+
369
+	/**
370
+	 * Adds an option for this question. Note: if the option were previously associated with a different
371
+	 * Question, that relationship will be overwritten.
372
+	 *
373
+	 * @param EE_Question_Option $option
374
+	 * @return boolean success
375
+	 */
376
+	public function add_option(EE_Question_Option $option)
377
+	{
378
+		return $this->_add_relation_to($option, 'Question_Option');
379
+	}
380
+
381
+
382
+	/**
383
+	 * Adds an option directly to this question without saving to the db
384
+	 *
385
+	 * @param EE_Question_Option $option
386
+	 * @return boolean success
387
+	 */
388
+	public function add_temp_option(EE_Question_Option $option)
389
+	{
390
+		$this->_model_relations['Question_Option'][] = $option;
391
+		return true;
392
+	}
393
+
394
+
395
+	/**
396
+	 * Marks the option as deleted.
397
+	 *
398
+	 * @param EE_Question_Option $option
399
+	 * @return boolean success
400
+	 */
401
+	public function remove_option(EE_Question_Option $option)
402
+	{
403
+		return $this->_remove_relation_to($option, 'Question_Option');
404
+	}
405
+
406
+
407
+	/**
408
+	 * @return bool
409
+	 */
410
+	public function is_system_question()
411
+	{
412
+		$system_ID = $this->get('QST_system');
413
+		return ! empty($system_ID) ? true : false;
414
+	}
415
+
416
+
417
+	/**
418
+	 * The purpose of this method is set the question order this question order to be the max out of all questions
419
+	 *
420
+	 * @access public
421
+	 * @return void
422
+	 */
423
+	public function set_order_to_latest()
424
+	{
425
+		$latest_order = $this->get_model()->get_latest_question_order();
426
+		$latest_order++;
427
+		$this->set('QST_order', $latest_order);
428
+	}
429
+
430
+
431
+	/**
432
+	 * Retrieves the list of allowed question types from the model.
433
+	 *
434
+	 * @return string[]
435
+	 */
436
+	private function _allowed_question_types()
437
+	{
438
+		$questionModel = $this->get_model();
439
+		/* @var $questionModel EEM_Question */
440
+		return $questionModel->allowed_question_types();
441
+	}
442
+
443
+	/**
444
+	 * Duplicates this question and its question options
445
+	 *
446
+	 * @return \EE_Question
447
+	 */
448
+	public function duplicate($options = array())
449
+	{
450
+		$new_question = clone $this;
451
+		$new_question->set('QST_ID', null);
452
+		$new_question->set_display_text(sprintf(__('%s **Duplicate**', 'event_espresso'), $this->display_text()));
453
+		$new_question->set_admin_label(sprintf(__('%s **Duplicate**', 'event_espresso'), $this->admin_label()));
454
+		$new_question->set_system_ID(null);
455
+		$new_question->set_wp_user(get_current_user_id());
456
+		// if we're duplicating a trashed question, assume we don't want the new one to be trashed
457
+		$new_question->set_deleted(false);
458
+		$success = $new_question->save();
459
+		if ($success) {
460
+			// we don't totally want to duplicate the question options, because we want them to be for the NEW question
461
+			foreach ($this->options() as $question_option) {
462
+				$question_option->duplicate(array('QST_ID' => $new_question->ID()));
463
+			}
464
+			return $new_question;
465
+		} else {
466
+			return null;
467
+		}
468
+	}
469
+
470
+	/**
471
+	 * Returns the question's maximum allowed response size
472
+	 *
473
+	 * @return int|float
474
+	 */
475
+	public function max()
476
+	{
477
+		return $this->get('QST_max');
478
+	}
479
+
480
+	/**
481
+	 * Sets the question's maximum allowed response size
482
+	 *
483
+	 * @param int|float $new_max
484
+	 * @return void
485
+	 */
486
+	public function set_max($new_max)
487
+	{
488
+		$this->set('QST_max', $new_max);
489
+	}
490
+
491
+
492
+	/**
493
+	 * Creates a form input from this question which can be used in HTML forms
494
+	 *
495
+	 * @param EE_Registration $registration
496
+	 * @param EE_Answer       $answer
497
+	 * @param array           $input_constructor_args
498
+	 * @return EE_Form_Input_Base
499
+	 */
500
+	public function generate_form_input($registration = null, $answer = null, $input_constructor_args = array())
501
+	{
502
+		$identifier = $this->is_system_question() ? $this->system_ID() : $this->ID();
503
+
504
+		$input_constructor_args = array_merge(
505
+			array(
506
+				'required'                          => $this->required() ? true : false,
507
+				'html_label_text'                   => $this->display_text(),
508
+				'required_validation_error_message' => $this->required_text(),
509
+			),
510
+			$input_constructor_args
511
+		);
512
+		if (! $answer instanceof EE_Answer && $registration instanceof EE_Registration) {
513
+			$answer = EEM_Answer::instance()->get_registration_question_answer_object($registration, $this->ID());
514
+		}
515
+		// has this question been answered ?
516
+		if ($answer instanceof EE_Answer
517
+			&& $answer->value() !== ''
518
+		) {
519
+			// answer gets htmlspecialchars called on it, undo that please
520
+			// because the form input's display strategy may call esc_attr too
521
+			// which also does html special characters
522
+			$values_with_html_special_chars = $answer->value();
523
+			if (is_array($values_with_html_special_chars)) {
524
+				$default_value = array_map('htmlspecialchars_decode', $values_with_html_special_chars);
525
+			} else {
526
+				$default_value = htmlspecialchars_decode($values_with_html_special_chars);
527
+			}
528
+			$input_constructor_args['default'] = $default_value;
529
+		}
530
+		$max_max_for_question = EEM_Question::instance()->absolute_max_for_system_question($this->system_ID());
531
+		if (in_array(
532
+			$this->type(),
533
+			EEM_Question::instance()->questionTypesWithMaxLength(),
534
+			true
535
+		)) {
536
+			$input_constructor_args['validation_strategies'][] = new EE_Max_Length_Validation_Strategy(
537
+				null,
538
+				min($max_max_for_question, $this->max())
539
+			);
540
+		}
541
+		$input_constructor_args = apply_filters(
542
+			'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__input_constructor_args',
543
+			$input_constructor_args,
544
+			$registration,
545
+			$this,
546
+			$answer
547
+		);
548
+
549
+		$result = null;
550
+		switch ($this->type()) {
551
+			// Text
552
+			case EEM_Question::QST_type_text:
553
+				$result = new EE_Text_Input($input_constructor_args);
554
+				break;
555
+			// Textarea
556
+			case EEM_Question::QST_type_textarea:
557
+				$result = new EE_Text_Area_Input($input_constructor_args);
558
+				break;
559
+			// Radio Buttons
560
+			case EEM_Question::QST_type_radio:
561
+				$result = new EE_Radio_Button_Input($this->options(), $input_constructor_args);
562
+				break;
563
+			// Dropdown
564
+			case EEM_Question::QST_type_dropdown:
565
+				$result = new EE_Select_Input($this->options(), $input_constructor_args);
566
+				break;
567
+			// State Dropdown
568
+			case EEM_Question::QST_type_state:
569
+				$state_options = apply_filters(
570
+					'FHEE__EE_Question__generate_form_input__state_options',
571
+					null,
572
+					$this,
573
+					$registration,
574
+					$answer
575
+				);
576
+				$result = new EE_State_Select_Input($state_options, $input_constructor_args);
577
+				break;
578
+			// Country Dropdown
579
+			case EEM_Question::QST_type_country:
580
+				$country_options = apply_filters(
581
+					'FHEE__EE_Question__generate_form_input__country_options',
582
+					null,
583
+					$this,
584
+					$registration,
585
+					$answer
586
+				);
587
+				$result = new EE_Country_Select_Input($country_options, $input_constructor_args);
588
+				break;
589
+			// Checkboxes
590
+			case EEM_Question::QST_type_checkbox:
591
+				$result = new EE_Checkbox_Multi_Input($this->options(), $input_constructor_args);
592
+				break;
593
+			// Date
594
+			case EEM_Question::QST_type_date:
595
+				$result = new EE_Datepicker_Input($input_constructor_args);
596
+				break;
597
+			case EEM_Question::QST_type_html_textarea:
598
+				$input_constructor_args['validation_strategies'][] = new EE_Simple_HTML_Validation_Strategy();
599
+				$result = new EE_Text_Area_Input($input_constructor_args);
600
+				$result->remove_validation_strategy('EE_Plaintext_Validation_Strategy');
601
+				break;
602
+			case EEM_Question::QST_type_email:
603
+				$result = new EE_Email_Input($input_constructor_args);
604
+				break;
605
+			case EEM_Question::QST_type_us_phone:
606
+				$result = new EE_Phone_Input($input_constructor_args);
607
+				break;
608
+			case EEM_Question::QST_type_int:
609
+				$result = new EE_Integer_Input($input_constructor_args);
610
+				break;
611
+			case EEM_Question::QST_type_decimal:
612
+				$result = new EE_Float_Input($input_constructor_args);
613
+				break;
614
+			case EEM_Question::QST_type_url:
615
+				$input_constructor_args['validation_strategies'][] = new EE_URL_Validation_Strategy();
616
+				$result = new EE_Text_Input($input_constructor_args);
617
+				break;
618
+			case EEM_Question::QST_type_year:
619
+				$result = new EE_Year_Input(
620
+					$input_constructor_args,
621
+					apply_filters(
622
+						'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__four_digit',
623
+						true,
624
+						$this
625
+					),
626
+					apply_filters(
627
+						'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__early_range',
628
+						100,
629
+						$this
630
+					),
631
+					apply_filters(
632
+						'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__end_range',
633
+						100,
634
+						$this
635
+					)
636
+				);
637
+				break;
638
+			case EEM_Question::QST_type_multi_select:
639
+				$result = new EE_Select_Multiple_Input($this->options(), $input_constructor_args);
640
+				break;
641
+			// fallback
642
+			default:
643
+				$default_input = apply_filters(
644
+					'FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__default',
645
+					null,
646
+					$this->type(),
647
+					$this,
648
+					$input_constructor_args
649
+				);
650
+				if (! $default_input) {
651
+					$default_input = new EE_Text_Input($input_constructor_args);
652
+				}
653
+				$result = $default_input;
654
+		}
655
+		return apply_filters('FHEE__EE_Question__generate_form_input__return', $result, $registration, $this, $answer);
656
+	}
657
+
658
+
659
+	/**
660
+	 * Returns whether or not this question type should have question option entries
661
+	 *
662
+	 * @return bool
663
+	 */
664
+	public function should_have_question_options()
665
+	{
666
+		return in_array(
667
+			$this->type(),
668
+			$this->_model->question_types_with_options(),
669
+			true
670
+		);
671
+	}
672 672
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -335,7 +335,7 @@  discard block
 block discarded – undo
335 335
      */
336 336
     public function options($notDeletedOptionsOnly = true, $selected_value_to_always_include = null)
337 337
     {
338
-        if (! $this->ID()) {
338
+        if ( ! $this->ID()) {
339 339
             return array();
340 340
         }
341 341
         $query_params = array();
@@ -509,7 +509,7 @@  discard block
 block discarded – undo
509 509
             ),
510 510
             $input_constructor_args
511 511
         );
512
-        if (! $answer instanceof EE_Answer && $registration instanceof EE_Registration) {
512
+        if ( ! $answer instanceof EE_Answer && $registration instanceof EE_Registration) {
513 513
             $answer = EEM_Answer::instance()->get_registration_question_answer_object($registration, $this->ID());
514 514
         }
515 515
         // has this question been answered ?
@@ -647,7 +647,7 @@  discard block
 block discarded – undo
647 647
                     $this,
648 648
                     $input_constructor_args
649 649
                 );
650
-                if (! $default_input) {
650
+                if ( ! $default_input) {
651 651
                     $default_input = new EE_Text_Input($input_constructor_args);
652 652
                 }
653 653
                 $result = $default_input;
Please login to merge, or discard this patch.
core/db_classes/EE_Price.class.php 2 patches
Indentation   +286 added lines, -286 removed lines patch added patch discarded remove patch
@@ -10,290 +10,290 @@
 block discarded – undo
10 10
 class EE_Price extends EE_Soft_Delete_Base_Class
11 11
 {
12 12
 
13
-    /**
14
-     *
15
-     * @param array  $props_n_values          incoming values
16
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
17
-     *                                        used.)
18
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
19
-     *                                        date_format and the second value is the time format
20
-     * @return EE_Attendee
21
-     */
22
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
23
-    {
24
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
25
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
26
-    }
27
-
28
-
29
-    /**
30
-     * @param array  $props_n_values  incoming values from the database
31
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
32
-     *                                the website will be used.
33
-     * @return EE_Attendee
34
-     */
35
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
36
-    {
37
-        return new self($props_n_values, true, $timezone);
38
-    }
39
-
40
-
41
-    /**
42
-     *        Set Price type ID
43
-     *
44
-     * @access        public
45
-     * @param        int $PRT_ID
46
-     */
47
-    public function set_type($PRT_ID = 0)
48
-    {
49
-        $this->set('PRT_ID', $PRT_ID);
50
-    }
51
-
52
-
53
-    /**
54
-     *        Set Price Amount
55
-     *
56
-     * @access        public
57
-     * @param        float $PRC_amount
58
-     */
59
-    public function set_amount($PRC_amount = 0.00)
60
-    {
61
-        $this->set('PRC_amount', $PRC_amount);
62
-    }
63
-
64
-
65
-    /**
66
-     *        Set Price Name
67
-     *
68
-     * @access        public
69
-     * @param        string $PRC_name
70
-     */
71
-    public function set_name($PRC_name = '')
72
-    {
73
-        $this->set('PRC_name', $PRC_name);
74
-    }
75
-
76
-
77
-    /**
78
-     *        Set Price Description
79
-     *
80
-     * @access        public
81
-     * @param        string $PRC_desc
82
-     */
83
-    public function set_description($PRC_desc = '')
84
-    {
85
-        $this->Set('PRC_desc', $PRC_desc);
86
-    }
87
-
88
-
89
-    /**
90
-     *        set is_default
91
-     *
92
-     * @access        public
93
-     * @param        bool $PRC_is_default
94
-     */
95
-    public function set_is_default($PRC_is_default = false)
96
-    {
97
-        $this->set('PRC_is_default', $PRC_is_default);
98
-    }
99
-
100
-
101
-    /**
102
-     *        set deleted
103
-     *
104
-     * @access        public
105
-     * @param        bool $PRC_deleted
106
-     */
107
-    public function set_deleted($PRC_deleted = null)
108
-    {
109
-        $this->set('PRC_deleted', $PRC_deleted);
110
-    }
111
-
112
-
113
-    /**
114
-     *    get Price type
115
-     *
116
-     * @access        public
117
-     * @return        int
118
-     */
119
-    public function type()
120
-    {
121
-        return $this->get('PRT_ID');
122
-    }
123
-
124
-
125
-    /**
126
-     *    get Price Amount
127
-     *
128
-     * @access        public
129
-     * @return        float
130
-     */
131
-    public function amount()
132
-    {
133
-        return $this->get('PRC_amount');
134
-    }
135
-
136
-
137
-    /**
138
-     *    get Price Name
139
-     *
140
-     * @access        public
141
-     * @return        string
142
-     */
143
-    public function name()
144
-    {
145
-        return $this->get('PRC_name');
146
-    }
147
-
148
-
149
-    /**
150
-     *    get Price description
151
-     *
152
-     * @access        public
153
-     * @return        string
154
-     */
155
-    public function desc()
156
-    {
157
-        return $this->get('PRC_desc');
158
-    }
159
-
160
-
161
-    /**
162
-     *    get overrides
163
-     *
164
-     * @access        public
165
-     * @return        int
166
-     */
167
-    public function overrides()
168
-    {
169
-        return $this->get('PRC_overrides');
170
-    }
171
-
172
-
173
-    /**
174
-     *    get order
175
-     *
176
-     * @access        public
177
-     * @return        int
178
-     */
179
-    public function order()
180
-    {
181
-        return $this->get('PRC_order');
182
-    }
183
-
184
-
185
-    /**
186
-     * get the author of the price
187
-     *
188
-     * @since 4.5.0
189
-     *
190
-     * @return int
191
-     */
192
-    public function wp_user()
193
-    {
194
-        return $this->get('PRC_wp_user');
195
-    }
196
-
197
-
198
-    /**
199
-     *    get is_default
200
-     *
201
-     * @access        public
202
-     * @return        bool
203
-     */
204
-    public function is_default()
205
-    {
206
-        return $this->get('PRC_is_default');
207
-    }
208
-
209
-
210
-    /**
211
-     *    get deleted
212
-     *
213
-     * @access        public
214
-     * @return        bool
215
-     */
216
-    public function deleted()
217
-    {
218
-        return $this->get('PRC_deleted');
219
-    }
220
-
221
-
222
-    /**
223
-     * @return bool
224
-     */
225
-    public function parent()
226
-    {
227
-        return $this->get('PRC_parent');
228
-    }
229
-
230
-
231
-    // some helper methods for getting info on the price_type for this price
232
-
233
-    /**
234
-     * return whether the price is a base price or not
235
-     *
236
-     * @return boolean
237
-     */
238
-    public function is_base_price()
239
-    {
240
-        $price_type = $this->type_obj();
241
-        return $price_type->base_type() === 1;
242
-    }
243
-
244
-
245
-    /**
246
-     *
247
-     * @return EE_Price_Type
248
-     */
249
-    public function type_obj()
250
-    {
251
-        return $this->get_first_related('Price_Type');
252
-    }
253
-
254
-
255
-    /**
256
-     * Simply indicates whether this price increases or decreases the total
257
-     *
258
-     * @return boolean true = discount, otherwise adds to the total
259
-     */
260
-    public function is_discount()
261
-    {
262
-        $price_type = $this->type_obj();
263
-        return $price_type->is_discount();
264
-    }
265
-
266
-
267
-    /**
268
-     * whether the price is a percentage or not
269
-     *
270
-     * @return boolean
271
-     */
272
-    public function is_percent()
273
-    {
274
-        $price_type = $this->type_obj();
275
-        return $price_type->get('PRT_is_percent');
276
-    }
277
-
278
-
279
-    /**
280
-     * return pretty price dependant on whether its a dollar or percent.
281
-     *
282
-     * @since 4.4.0
283
-     *
284
-     * @return string
285
-     */
286
-    public function pretty_price()
287
-    {
288
-        return ! $this->is_percent() ? $this->get_pretty('PRC_amount') : $this->get('PRC_amount') . '%';
289
-    }
290
-
291
-
292
-    /**
293
-     * @return mixed
294
-     */
295
-    public function get_price_without_currency_symbol()
296
-    {
297
-        return str_replace(EE_Registry::instance()->CFG->currency->sign, '', $this->get_pretty('PRC_amount'));
298
-    }
13
+	/**
14
+	 *
15
+	 * @param array  $props_n_values          incoming values
16
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
17
+	 *                                        used.)
18
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
19
+	 *                                        date_format and the second value is the time format
20
+	 * @return EE_Attendee
21
+	 */
22
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
23
+	{
24
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
25
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
26
+	}
27
+
28
+
29
+	/**
30
+	 * @param array  $props_n_values  incoming values from the database
31
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
32
+	 *                                the website will be used.
33
+	 * @return EE_Attendee
34
+	 */
35
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
36
+	{
37
+		return new self($props_n_values, true, $timezone);
38
+	}
39
+
40
+
41
+	/**
42
+	 *        Set Price type ID
43
+	 *
44
+	 * @access        public
45
+	 * @param        int $PRT_ID
46
+	 */
47
+	public function set_type($PRT_ID = 0)
48
+	{
49
+		$this->set('PRT_ID', $PRT_ID);
50
+	}
51
+
52
+
53
+	/**
54
+	 *        Set Price Amount
55
+	 *
56
+	 * @access        public
57
+	 * @param        float $PRC_amount
58
+	 */
59
+	public function set_amount($PRC_amount = 0.00)
60
+	{
61
+		$this->set('PRC_amount', $PRC_amount);
62
+	}
63
+
64
+
65
+	/**
66
+	 *        Set Price Name
67
+	 *
68
+	 * @access        public
69
+	 * @param        string $PRC_name
70
+	 */
71
+	public function set_name($PRC_name = '')
72
+	{
73
+		$this->set('PRC_name', $PRC_name);
74
+	}
75
+
76
+
77
+	/**
78
+	 *        Set Price Description
79
+	 *
80
+	 * @access        public
81
+	 * @param        string $PRC_desc
82
+	 */
83
+	public function set_description($PRC_desc = '')
84
+	{
85
+		$this->Set('PRC_desc', $PRC_desc);
86
+	}
87
+
88
+
89
+	/**
90
+	 *        set is_default
91
+	 *
92
+	 * @access        public
93
+	 * @param        bool $PRC_is_default
94
+	 */
95
+	public function set_is_default($PRC_is_default = false)
96
+	{
97
+		$this->set('PRC_is_default', $PRC_is_default);
98
+	}
99
+
100
+
101
+	/**
102
+	 *        set deleted
103
+	 *
104
+	 * @access        public
105
+	 * @param        bool $PRC_deleted
106
+	 */
107
+	public function set_deleted($PRC_deleted = null)
108
+	{
109
+		$this->set('PRC_deleted', $PRC_deleted);
110
+	}
111
+
112
+
113
+	/**
114
+	 *    get Price type
115
+	 *
116
+	 * @access        public
117
+	 * @return        int
118
+	 */
119
+	public function type()
120
+	{
121
+		return $this->get('PRT_ID');
122
+	}
123
+
124
+
125
+	/**
126
+	 *    get Price Amount
127
+	 *
128
+	 * @access        public
129
+	 * @return        float
130
+	 */
131
+	public function amount()
132
+	{
133
+		return $this->get('PRC_amount');
134
+	}
135
+
136
+
137
+	/**
138
+	 *    get Price Name
139
+	 *
140
+	 * @access        public
141
+	 * @return        string
142
+	 */
143
+	public function name()
144
+	{
145
+		return $this->get('PRC_name');
146
+	}
147
+
148
+
149
+	/**
150
+	 *    get Price description
151
+	 *
152
+	 * @access        public
153
+	 * @return        string
154
+	 */
155
+	public function desc()
156
+	{
157
+		return $this->get('PRC_desc');
158
+	}
159
+
160
+
161
+	/**
162
+	 *    get overrides
163
+	 *
164
+	 * @access        public
165
+	 * @return        int
166
+	 */
167
+	public function overrides()
168
+	{
169
+		return $this->get('PRC_overrides');
170
+	}
171
+
172
+
173
+	/**
174
+	 *    get order
175
+	 *
176
+	 * @access        public
177
+	 * @return        int
178
+	 */
179
+	public function order()
180
+	{
181
+		return $this->get('PRC_order');
182
+	}
183
+
184
+
185
+	/**
186
+	 * get the author of the price
187
+	 *
188
+	 * @since 4.5.0
189
+	 *
190
+	 * @return int
191
+	 */
192
+	public function wp_user()
193
+	{
194
+		return $this->get('PRC_wp_user');
195
+	}
196
+
197
+
198
+	/**
199
+	 *    get is_default
200
+	 *
201
+	 * @access        public
202
+	 * @return        bool
203
+	 */
204
+	public function is_default()
205
+	{
206
+		return $this->get('PRC_is_default');
207
+	}
208
+
209
+
210
+	/**
211
+	 *    get deleted
212
+	 *
213
+	 * @access        public
214
+	 * @return        bool
215
+	 */
216
+	public function deleted()
217
+	{
218
+		return $this->get('PRC_deleted');
219
+	}
220
+
221
+
222
+	/**
223
+	 * @return bool
224
+	 */
225
+	public function parent()
226
+	{
227
+		return $this->get('PRC_parent');
228
+	}
229
+
230
+
231
+	// some helper methods for getting info on the price_type for this price
232
+
233
+	/**
234
+	 * return whether the price is a base price or not
235
+	 *
236
+	 * @return boolean
237
+	 */
238
+	public function is_base_price()
239
+	{
240
+		$price_type = $this->type_obj();
241
+		return $price_type->base_type() === 1;
242
+	}
243
+
244
+
245
+	/**
246
+	 *
247
+	 * @return EE_Price_Type
248
+	 */
249
+	public function type_obj()
250
+	{
251
+		return $this->get_first_related('Price_Type');
252
+	}
253
+
254
+
255
+	/**
256
+	 * Simply indicates whether this price increases or decreases the total
257
+	 *
258
+	 * @return boolean true = discount, otherwise adds to the total
259
+	 */
260
+	public function is_discount()
261
+	{
262
+		$price_type = $this->type_obj();
263
+		return $price_type->is_discount();
264
+	}
265
+
266
+
267
+	/**
268
+	 * whether the price is a percentage or not
269
+	 *
270
+	 * @return boolean
271
+	 */
272
+	public function is_percent()
273
+	{
274
+		$price_type = $this->type_obj();
275
+		return $price_type->get('PRT_is_percent');
276
+	}
277
+
278
+
279
+	/**
280
+	 * return pretty price dependant on whether its a dollar or percent.
281
+	 *
282
+	 * @since 4.4.0
283
+	 *
284
+	 * @return string
285
+	 */
286
+	public function pretty_price()
287
+	{
288
+		return ! $this->is_percent() ? $this->get_pretty('PRC_amount') : $this->get('PRC_amount') . '%';
289
+	}
290
+
291
+
292
+	/**
293
+	 * @return mixed
294
+	 */
295
+	public function get_price_without_currency_symbol()
296
+	{
297
+		return str_replace(EE_Registry::instance()->CFG->currency->sign, '', $this->get_pretty('PRC_amount'));
298
+	}
299 299
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -285,7 +285,7 @@
 block discarded – undo
285 285
      */
286 286
     public function pretty_price()
287 287
     {
288
-        return ! $this->is_percent() ? $this->get_pretty('PRC_amount') : $this->get('PRC_amount') . '%';
288
+        return ! $this->is_percent() ? $this->get_pretty('PRC_amount') : $this->get('PRC_amount').'%';
289 289
     }
290 290
 
291 291
 
Please login to merge, or discard this patch.
core/db_classes/EE_Import.class.php 2 patches
Indentation   +950 added lines, -950 removed lines patch added patch discarded remove patch
@@ -14,91 +14,91 @@  discard block
 block discarded – undo
14 14
 class EE_Import implements ResettableInterface
15 15
 {
16 16
 
17
-    const do_insert = 'insert';
18
-    const do_update = 'update';
19
-    const do_nothing = 'nothing';
20
-
21
-
22
-    // instance of the EE_Import object
23
-    private static $_instance = null;
24
-
25
-    private static $_csv_array = array();
26
-
27
-    /**
28
-     *
29
-     * @var array of model names
30
-     */
31
-    private static $_model_list = array();
32
-
33
-    private static $_columns_to_save = array();
34
-
35
-    protected $_total_inserts = 0;
36
-    protected $_total_updates = 0;
37
-    protected $_total_insert_errors = 0;
38
-    protected $_total_update_errors = 0;
39
-
40
-
41
-    /**
42
-     *        private constructor to prevent direct creation
43
-     *
44
-     * @Constructor
45
-     * @access private
46
-     * @return void
47
-     */
48
-    private function __construct()
49
-    {
50
-        $this->_total_inserts = 0;
51
-        $this->_total_updates = 0;
52
-        $this->_total_insert_errors = 0;
53
-        $this->_total_update_errors = 0;
54
-    }
55
-
56
-
57
-    /**
58
-     *    @ singleton method used to instantiate class object
59
-     *    @ access public
60
-     *
61
-     * @return EE_Import
62
-     */
63
-    public static function instance()
64
-    {
65
-        // check if class object is instantiated
66
-        if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_Import)) {
67
-            self::$_instance = new self();
68
-        }
69
-        return self::$_instance;
70
-    }
71
-
72
-    /**
73
-     * Resets the importer
74
-     *
75
-     * @return EE_Import
76
-     */
77
-    public static function reset()
78
-    {
79
-        self::$_instance = null;
80
-        return self::instance();
81
-    }
82
-
83
-
84
-    /**
85
-     *    @ generates HTML for a file upload input and form
86
-     *    @ access    public
87
-     *
88
-     * @param    string $title  - heading for the form
89
-     * @param    string $intro  - additional text explaing what to do
90
-     * @param    string $page   - EE Admin page to direct form to - in the form "espresso_{pageslug}"
91
-     * @param    string $action - EE Admin page route array "action" that form will direct to
92
-     * @param    string $type   - type of file to import
93
-     *                          @ return    string
94
-     */
95
-    public function upload_form($title, $intro, $form_url, $action, $type)
96
-    {
97
-
98
-        $form_url = EE_Admin_Page::add_query_args_and_nonce(array('action' => $action), $form_url);
99
-
100
-        ob_start();
101
-        ?>
17
+	const do_insert = 'insert';
18
+	const do_update = 'update';
19
+	const do_nothing = 'nothing';
20
+
21
+
22
+	// instance of the EE_Import object
23
+	private static $_instance = null;
24
+
25
+	private static $_csv_array = array();
26
+
27
+	/**
28
+	 *
29
+	 * @var array of model names
30
+	 */
31
+	private static $_model_list = array();
32
+
33
+	private static $_columns_to_save = array();
34
+
35
+	protected $_total_inserts = 0;
36
+	protected $_total_updates = 0;
37
+	protected $_total_insert_errors = 0;
38
+	protected $_total_update_errors = 0;
39
+
40
+
41
+	/**
42
+	 *        private constructor to prevent direct creation
43
+	 *
44
+	 * @Constructor
45
+	 * @access private
46
+	 * @return void
47
+	 */
48
+	private function __construct()
49
+	{
50
+		$this->_total_inserts = 0;
51
+		$this->_total_updates = 0;
52
+		$this->_total_insert_errors = 0;
53
+		$this->_total_update_errors = 0;
54
+	}
55
+
56
+
57
+	/**
58
+	 *    @ singleton method used to instantiate class object
59
+	 *    @ access public
60
+	 *
61
+	 * @return EE_Import
62
+	 */
63
+	public static function instance()
64
+	{
65
+		// check if class object is instantiated
66
+		if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_Import)) {
67
+			self::$_instance = new self();
68
+		}
69
+		return self::$_instance;
70
+	}
71
+
72
+	/**
73
+	 * Resets the importer
74
+	 *
75
+	 * @return EE_Import
76
+	 */
77
+	public static function reset()
78
+	{
79
+		self::$_instance = null;
80
+		return self::instance();
81
+	}
82
+
83
+
84
+	/**
85
+	 *    @ generates HTML for a file upload input and form
86
+	 *    @ access    public
87
+	 *
88
+	 * @param    string $title  - heading for the form
89
+	 * @param    string $intro  - additional text explaing what to do
90
+	 * @param    string $page   - EE Admin page to direct form to - in the form "espresso_{pageslug}"
91
+	 * @param    string $action - EE Admin page route array "action" that form will direct to
92
+	 * @param    string $type   - type of file to import
93
+	 *                          @ return    string
94
+	 */
95
+	public function upload_form($title, $intro, $form_url, $action, $type)
96
+	{
97
+
98
+		$form_url = EE_Admin_Page::add_query_args_and_nonce(array('action' => $action), $form_url);
99
+
100
+		ob_start();
101
+		?>
102 102
         <div class="ee-upload-form-dv">
103 103
             <h3><?php echo $title; ?></h3>
104 104
             <p><?php echo $intro; ?></p>
@@ -114,874 +114,874 @@  discard block
 block discarded – undo
114 114
                 <b><?php _e('Attention', 'event_espresso'); ?></b><br/>
115 115
                 <?php echo sprintf(__('Accepts .%s file types only.', 'event_espresso'), $type); ?>
116 116
                 <?php echo __(
117
-                    'Please only import CSV files exported from Event Espresso, or compatible 3rd-party software.',
118
-                    'event_espresso'
119
-                ); ?>
117
+					'Please only import CSV files exported from Event Espresso, or compatible 3rd-party software.',
118
+					'event_espresso'
119
+				); ?>
120 120
             </p>
121 121
 
122 122
         </div>
123 123
 
124 124
         <?php
125
-        $uploader = ob_get_clean();
126
-        return $uploader;
127
-    }
128
-
129
-
130
-    /**
131
-     * @Import Event Espresso data - some code "borrowed" from event espresso csv_import.php
132
-     * @access public
133
-     * @return boolean success
134
-     */
135
-    public function import()
136
-    {
137
-
138
-        require_once(EE_CLASSES . 'EE_CSV.class.php');
139
-        $this->EE_CSV = EE_CSV::instance();
140
-
141
-        if (isset($_REQUEST['import'])) {
142
-            if (isset($_POST['csv_submitted'])) {
143
-                switch ($_FILES['file']['error'][0]) {
144
-                    case UPLOAD_ERR_OK:
145
-                        $error_msg = false;
146
-                        break;
147
-                    case UPLOAD_ERR_INI_SIZE:
148
-                        $error_msg = __(
149
-                            "'The uploaded file exceeds the upload_max_filesize directive in php.ini.'",
150
-                            "event_espresso"
151
-                        );
152
-                        break;
153
-                    case UPLOAD_ERR_FORM_SIZE:
154
-                        $error_msg = __(
155
-                            'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
156
-                            "event_espresso"
157
-                        );
158
-                        break;
159
-                    case UPLOAD_ERR_PARTIAL:
160
-                        $error_msg = __('The uploaded file was only partially uploaded.', "event_espresso");
161
-                        break;
162
-                    case UPLOAD_ERR_NO_FILE:
163
-                        $error_msg = __('No file was uploaded.', "event_espresso");
164
-                        break;
165
-                    case UPLOAD_ERR_NO_TMP_DIR:
166
-                        $error_msg = __('Missing a temporary folder.', "event_espresso");
167
-                        break;
168
-                    case UPLOAD_ERR_CANT_WRITE:
169
-                        $error_msg = __('Failed to write file to disk.', "event_espresso");
170
-                        break;
171
-                    case UPLOAD_ERR_EXTENSION:
172
-                        $error_msg = __('File upload stopped by extension.', "event_espresso");
173
-                        break;
174
-                    default:
175
-                        $error_msg = __(
176
-                            'An unknown error occurred and the file could not be uploaded',
177
-                            "event_espresso"
178
-                        );
179
-                        break;
180
-                }
181
-
182
-                if (! $error_msg) {
183
-                    $filename = $_FILES['file']['name'][0];
184
-                    $file_ext = substr(strrchr($filename, '.'), 1);
185
-                    $file_type = $_FILES['file']['type'][0];
186
-                    $temp_file = $_FILES['file']['tmp_name'][0];
187
-                    $filesize = $_FILES['file']['size'][0] / 1024;// convert from bytes to KB
188
-
189
-                    if ($file_ext == 'csv') {
190
-                        $max_upload = $this->EE_CSV->get_max_upload_size();// max upload size in KB
191
-                        if ($filesize < $max_upload || true) {
192
-                            $wp_upload_dir = str_replace(array('\\', '/'), DS, wp_upload_dir());
193
-                            $path_to_file = $wp_upload_dir['basedir'] . DS . 'espresso' . DS . $filename;
194
-
195
-                            if (move_uploaded_file($temp_file, $path_to_file)) {
196
-                                // convert csv to array
197
-                                $this->csv_array = $this->EE_CSV->import_csv_to_model_data_array($path_to_file);
198
-
199
-                                // was data successfully stored in an array?
200
-                                if (is_array($this->csv_array)) {
201
-                                    $import_what = str_replace('csv_import_', '', $_REQUEST['action']);
202
-                                    $import_what = str_replace('_', ' ', ucwords($import_what));
203
-                                    $processed_data = $this->csv_array;
204
-                                    $this->columns_to_save = false;
205
-
206
-                                    // if any imports require funcky processing, we'll catch them in the switch
207
-                                    switch ($_REQUEST['action']) {
208
-                                        case "import_events":
209
-                                        case "event_list":
210
-                                            $import_what = 'Event Details';
211
-                                            break;
212
-
213
-                                        case 'groupon_import_csv':
214
-                                            $import_what = 'Groupon Codes';
215
-                                            $processed_data = $this->process_groupon_codes();
216
-                                            break;
217
-                                    }
218
-                                    // save processed codes to db
219
-                                    if ($this->save_csv_data_array_to_db($processed_data, $this->columns_to_save)) {
220
-                                        return true;
221
-                                    }
222
-                                } else {
223
-                                    // no array? must be an error
224
-                                    EE_Error::add_error(
225
-                                        sprintf(__("No file seems to have been uploaded", "event_espresso")),
226
-                                        __FILE__,
227
-                                        __FUNCTION__,
228
-                                        __LINE__
229
-                                    );
230
-                                    return false;
231
-                                }
232
-                            } else {
233
-                                EE_Error::add_error(
234
-                                    sprintf(__("%s was not successfully uploaded", "event_espresso"), $filename),
235
-                                    __FILE__,
236
-                                    __FUNCTION__,
237
-                                    __LINE__
238
-                                );
239
-                                return false;
240
-                            }
241
-                        } else {
242
-                            EE_Error::add_error(
243
-                                sprintf(
244
-                                    __(
245
-                                        "%s was too large of a file and could not be uploaded. The max filesize is %s' KB.",
246
-                                        "event_espresso"
247
-                                    ),
248
-                                    $filename,
249
-                                    $max_upload
250
-                                ),
251
-                                __FILE__,
252
-                                __FUNCTION__,
253
-                                __LINE__
254
-                            );
255
-                            return false;
256
-                        }
257
-                    } else {
258
-                        EE_Error::add_error(
259
-                            sprintf(__("%s  had an invalid file extension, not uploaded", "event_espresso"), $filename),
260
-                            __FILE__,
261
-                            __FUNCTION__,
262
-                            __LINE__
263
-                        );
264
-                        return false;
265
-                    }
266
-                } else {
267
-                    EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
268
-                    return false;
269
-                }
270
-            }
271
-        }
272
-        return;
273
-    }
274
-
275
-
276
-    /**
277
-     *    Given an array of data (usually from a CSV import) attempts to save that data to the db.
278
-     *    If $model_name ISN'T provided, assumes that this is a 3d array, with toplevel keys being model names,
279
-     *    next level being numeric indexes adn each value representing a model object, and the last layer down
280
-     *    being keys of model fields and their proposed values.
281
-     *    If $model_name IS provided, assumes a 2d array of the bottom two layers previously mentioned.
282
-     *    If the CSV data says (in the metadata row) that it's from the SAME database,
283
-     *    we treat the IDs in the CSV as the normal IDs, and try to update those records. However, if those
284
-     *    IDs DON'T exist in the database, they're treated as temporary IDs,
285
-     *    which can used elsewhere to refer to the same object. Once an item
286
-     *    with a temporary ID gets inserted, we record its mapping from temporary
287
-     *    ID to real ID, and use the real ID in place of the temporary ID
288
-     *    when that temporary ID was used as a foreign key.
289
-     *    If the CSV data says (in the metadata again) that it's from a DIFFERENT database,
290
-     *    we treat all the IDs in the CSV as temporary ID- eg, if the CSV specifies an event with
291
-     *    ID 1, and the database already has an event with ID 1, we assume that's just a coincidence,
292
-     *    and insert a new event, and map it's temporary ID of 1 over to its new real ID.
293
-     *    An important exception are non-auto-increment primary keys. If one entry in the
294
-     *    CSV file has the same ID as one in the DB, we assume they are meant to be
295
-     *    the same item, and instead update the item in the DB with that same ID.
296
-     *    Also note, we remember the mappings permanently. So the 2nd, 3rd, and 10000th
297
-     *    time you import a CSV from a different site, we remember their mappings, and
298
-     * will try to update the item in the DB instead of inserting another item (eg
299
-     * if we previously imported an event with temporary ID 1, and then it got a
300
-     * real ID of 123, we remember that. So the next time we import an event with
301
-     * temporary ID, from the same site, we know that it's real ID is 123, and will
302
-     * update that event, instead of adding a new event).
303
-     *
304
-     * @access public
305
-     * @param array $csv_data_array - the array containing the csv data produced from
306
-     *                              EE_CSV::import_csv_to_model_data_array()
307
-     * @param array $fields_to_save - an array containing the csv column names as keys with the corresponding db table
308
-     *                              fields they will be saved to
309
-     * @return TRUE on success, FALSE on fail
310
-     * @throws \EE_Error
311
-     */
312
-    public function save_csv_data_array_to_db($csv_data_array, $model_name = false)
313
-    {
314
-        $success = false;
315
-        $error = false;
316
-        // whther to treat this import as if it's data froma different database or not
317
-        // ie, if it IS from a different database, ignore foreign keys whihf
318
-        $export_from_site_a_to_b = true;
319
-        // first level of array is not table information but a table name was passed to the function
320
-        // array is only two levels deep, so let's fix that by adding a level, else the next steps will fail
321
-        if ($model_name) {
322
-            $csv_data_array = array($csv_data_array);
323
-        }
324
-        // begin looking through the $csv_data_array, expecting the toplevel key to be the model's name...
325
-        $old_site_url = 'none-specified';
326
-        // hanlde metadata
327
-        if (isset($csv_data_array[ EE_CSV::metadata_header ])) {
328
-            $csv_metadata = array_shift($csv_data_array[ EE_CSV::metadata_header ]);
329
-            // ok so its metadata, dont try to save it to ehte db obviously...
330
-            if (isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()) {
331
-                EE_Error::add_attention(
332
-                    sprintf(
333
-                        __(
334
-                            "CSV Data appears to be from the same database, so attempting to update data",
335
-                            "event_espresso"
336
-                        )
337
-                    )
338
-                );
339
-                $export_from_site_a_to_b = false;
340
-            } else {
341
-                $old_site_url = isset($csv_metadata['site_url']) ? $csv_metadata['site_url'] : $old_site_url;
342
-                EE_Error::add_attention(
343
-                    sprintf(
344
-                        __(
345
-                            "CSV Data appears to be from a different database (%s instead of %s), so we assume IDs in the CSV data DO NOT correspond to IDs in this database",
346
-                            "event_espresso"
347
-                        ),
348
-                        $old_site_url,
349
-                        site_url()
350
-                    )
351
-                );
352
-            };
353
-            unset($csv_data_array[ EE_CSV::metadata_header ]);
354
-        }
355
-        /**
356
-         * @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, bottom-level keys being the original key, and
357
-         * the value will be the newly-inserted ID.
358
-         * If we have already imported data from the same website via CSV, it shoudl be kept in this wp option
359
-         */
360
-        $old_db_to_new_db_mapping = get_option('ee_id_mapping_from' . sanitize_title($old_site_url), array());
361
-        if ($old_db_to_new_db_mapping) {
362
-            EE_Error::add_attention(
363
-                sprintf(
364
-                    __(
365
-                        "We noticed you have imported data via CSV from %s before. Because of this, IDs in your CSV have been mapped to their new IDs in %s",
366
-                        "event_espresso"
367
-                    ),
368
-                    $old_site_url,
369
-                    site_url()
370
-                )
371
-            );
372
-        }
373
-        $old_db_to_new_db_mapping = $this->save_data_rows_to_db(
374
-            $csv_data_array,
375
-            $export_from_site_a_to_b,
376
-            $old_db_to_new_db_mapping
377
-        );
378
-
379
-        // save the mapping from old db to new db in case they try re-importing the same data from the same website again
380
-        update_option('ee_id_mapping_from' . sanitize_title($old_site_url), $old_db_to_new_db_mapping);
381
-
382
-        if ($this->_total_updates > 0) {
383
-            EE_Error::add_success(
384
-                sprintf(
385
-                    __("%s existing records in the database were updated.", "event_espresso"),
386
-                    $this->_total_updates
387
-                )
388
-            );
389
-            $success = true;
390
-        }
391
-        if ($this->_total_inserts > 0) {
392
-            EE_Error::add_success(
393
-                sprintf(__("%s new records were added to the database.", "event_espresso"), $this->_total_inserts)
394
-            );
395
-            $success = true;
396
-        }
397
-
398
-        if ($this->_total_update_errors > 0) {
399
-            EE_Error::add_error(
400
-                sprintf(
401
-                    __(
402
-                        "'One or more errors occurred, and a total of %s existing records in the database were <strong>not</strong> updated.'",
403
-                        "event_espresso"
404
-                    ),
405
-                    $this->_total_update_errors
406
-                ),
407
-                __FILE__,
408
-                __FUNCTION__,
409
-                __LINE__
410
-            );
411
-            $error = true;
412
-        }
413
-        if ($this->_total_insert_errors > 0) {
414
-            EE_Error::add_error(
415
-                sprintf(
416
-                    __(
417
-                        "One or more errors occurred, and a total of %s new records were <strong>not</strong> added to the database.'",
418
-                        "event_espresso"
419
-                    ),
420
-                    $this->_total_insert_errors
421
-                ),
422
-                __FILE__,
423
-                __FUNCTION__,
424
-                __LINE__
425
-            );
426
-            $error = true;
427
-        }
428
-
429
-        // lastly, we need to update the datetime and ticket sold amounts
430
-        // as those may have been affected by this
431
-        EEM_Ticket::instance()->update_tickets_sold(EEM_Ticket::instance()->get_all());
432
-
433
-        // if there was at least one success and absolutely no errors
434
-        if ($success && ! $error) {
435
-            return true;
436
-        } else {
437
-            return false;
438
-        }
439
-    }
440
-
441
-
442
-    /**
443
-     * Processes the array of data, given the knowledge that it's from the same database or a different one,
444
-     * and the mapping from temporary IDs to real IDs.
445
-     * If the data is from a different database, we treat the primary keys and their corresponding
446
-     * foreign keys as "temp Ids", basically identifiers that get mapped to real primary keys
447
-     * in the real target database. As items are inserted, their temporary primary keys
448
-     * are mapped to the real IDs in the target database. Also, before doing any update or
449
-     * insert, we replace all the temp ID which are foreign keys with their mapped real IDs.
450
-     * An exception: string primary keys are treated as real IDs, or else we'd need to
451
-     * dynamically generate new string primary keys which would be very awkard for the country table etc.
452
-     * Also, models with no primary key are strange too. We combine use their primar key INDEX (a
453
-     * combination of fields) to create a unique string identifying the row and store
454
-     * those in the mapping.
455
-     *
456
-     * If the data is from the same database, we usually treat primary keys as real IDs.
457
-     * An exception is if there is nothing in the database for that ID. If that's the case,
458
-     * we need to insert a new row for that ID, and then map from the non-existent ID
459
-     * to the newly-inserted real ID.
460
-     *
461
-     * @param type $csv_data_array
462
-     * @param type $export_from_site_a_to_b
463
-     * @param type $old_db_to_new_db_mapping
464
-     * @return array updated $old_db_to_new_db_mapping
465
-     */
466
-    public function save_data_rows_to_db($csv_data_array, $export_from_site_a_to_b, $old_db_to_new_db_mapping)
467
-    {
468
-        foreach ($csv_data_array as $model_name_in_csv_data => $model_data_from_import) {
469
-            // now check that assumption was correct. If
470
-            if (EE_Registry::instance()->is_model_name($model_name_in_csv_data)) {
471
-                $model_name = $model_name_in_csv_data;
472
-            } else {
473
-                // no table info in the array and no table name passed to the function?? FAIL
474
-                EE_Error::add_error(
475
-                    __(
476
-                        'No table information was specified and/or found, therefore the import could not be completed',
477
-                        'event_espresso'
478
-                    ),
479
-                    __FILE__,
480
-                    __FUNCTION__,
481
-                    __LINE__
482
-                );
483
-                return false;
484
-            }
485
-            /* @var $model EEM_Base */
486
-            $model = EE_Registry::instance()->load_model($model_name);
487
-
488
-            // so without further ado, scanning all the data provided for primary keys and their inital values
489
-            foreach ($model_data_from_import as $model_object_data) {
490
-                // before we do ANYTHING, make sure the csv row wasn't just completely blank
491
-                $row_is_completely_empty = true;
492
-                foreach ($model_object_data as $field) {
493
-                    if ($field) {
494
-                        $row_is_completely_empty = false;
495
-                    }
496
-                }
497
-                if ($row_is_completely_empty) {
498
-                    continue;
499
-                }
500
-                // find the PK in the row of data (or a combined key if
501
-                // there is no primary key)
502
-                if ($model->has_primary_key_field()) {
503
-                    $id_in_csv = $model_object_data[ $model->primary_key_name() ];
504
-                } else {
505
-                    $id_in_csv = $model->get_index_primary_key_string($model_object_data);
506
-                }
507
-
508
-
509
-                $model_object_data = $this->_replace_temp_ids_with_mappings(
510
-                    $model_object_data,
511
-                    $model,
512
-                    $old_db_to_new_db_mapping,
513
-                    $export_from_site_a_to_b
514
-                );
515
-                // now we need to decide if we're going to add a new model object given the $model_object_data,
516
-                // or just update.
517
-                if ($export_from_site_a_to_b) {
518
-                    $what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_other_db(
519
-                        $id_in_csv,
520
-                        $model_object_data,
521
-                        $model,
522
-                        $old_db_to_new_db_mapping
523
-                    );
524
-                } else {// this is just a re-import
525
-                    $what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_same_db(
526
-                        $id_in_csv,
527
-                        $model_object_data,
528
-                        $model,
529
-                        $old_db_to_new_db_mapping
530
-                    );
531
-                }
532
-                if ($what_to_do == self::do_nothing) {
533
-                    continue;
534
-                }
535
-
536
-                // double-check we actually want to insert, if that's what we're planning
537
-                // based on whether this item would be unique in the DB or not
538
-                if ($what_to_do == self::do_insert) {
539
-                    // we're supposed to be inserting. But wait, will this thing
540
-                    // be acceptable if inserted?
541
-                    $conflicting = $model->get_one_conflicting($model_object_data, false);
542
-                    if ($conflicting) {
543
-                        // ok, this item would conflict if inserted. Just update the item that it conflicts with.
544
-                        $what_to_do = self::do_update;
545
-                        // and if this model has a primary key, remember its mapping
546
-                        if ($model->has_primary_key_field()) {
547
-                            $old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ] = $conflicting->ID();
548
-                            $model_object_data[ $model->primary_key_name() ] = $conflicting->ID();
549
-                        } else {
550
-                            // we want to update this conflicting item, instead of inserting a conflicting item
551
-                            // so we need to make sure they match entirely (its possible that they only conflicted on one field, but we need them to match on other fields
552
-                            // for the WHERE conditions in the update). At the time of this comment, there were no models like this
553
-                            foreach ($model->get_combined_primary_key_fields() as $key_field) {
554
-                                $model_object_data[ $key_field->get_name() ] = $conflicting->get(
555
-                                    $key_field->get_name()
556
-                                );
557
-                            }
558
-                        }
559
-                    }
560
-                }
561
-                if ($what_to_do == self::do_insert) {
562
-                    $old_db_to_new_db_mapping = $this->_insert_from_data_array(
563
-                        $id_in_csv,
564
-                        $model_object_data,
565
-                        $model,
566
-                        $old_db_to_new_db_mapping
567
-                    );
568
-                } elseif ($what_to_do == self::do_update) {
569
-                    $old_db_to_new_db_mapping = $this->_update_from_data_array(
570
-                        $id_in_csv,
571
-                        $model_object_data,
572
-                        $model,
573
-                        $old_db_to_new_db_mapping
574
-                    );
575
-                } else {
576
-                    throw new EE_Error(
577
-                        sprintf(
578
-                            __(
579
-                                'Programming error. We shoudl be inserting or updating, but instead we are being told to "%s", whifh is invalid',
580
-                                'event_espresso'
581
-                            ),
582
-                            $what_to_do
583
-                        )
584
-                    );
585
-                }
586
-            }
587
-        }
588
-        return $old_db_to_new_db_mapping;
589
-    }
590
-
591
-
592
-    /**
593
-     * Decides whether or not to insert, given that this data is from another database.
594
-     * So, if the primary key of this $model_object_data already exists in the database,
595
-     * it's just a coincidence and we should still insert. The only time we should
596
-     * update is when we know what it maps to, or there's something that would
597
-     * conflict (and we should instead just update that conflicting thing)
598
-     *
599
-     * @param string   $id_in_csv
600
-     * @param array    $model_object_data        by reference so it can be modified
601
-     * @param EEM_Base $model
602
-     * @param array    $old_db_to_new_db_mapping by reference so it can be modified
603
-     * @return string one of the consts on this class that starts with do_*
604
-     */
605
-    protected function _decide_whether_to_insert_or_update_given_data_from_other_db(
606
-        $id_in_csv,
607
-        $model_object_data,
608
-        $model,
609
-        $old_db_to_new_db_mapping
610
-    ) {
611
-        $model_name = $model->get_this_model_name();
612
-        // if it's a site-to-site export-and-import, see if this modelobject's id
613
-        // in the old data that we know of
614
-        if (isset($old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ])) {
615
-            return self::do_update;
616
-        } else {
617
-            return self::do_insert;
618
-        }
619
-    }
620
-
621
-    /**
622
-     * If this thing basically already exists in the database, we want to update it;
623
-     * otherwise insert it (ie, someone tweaked the CSV file, or the item was
624
-     * deleted in the database so it should be re-inserted)
625
-     *
626
-     * @param type     $id_in_csv
627
-     * @param type     $model_object_data
628
-     * @param EEM_Base $model
629
-     * @param type     $old_db_to_new_db_mapping
630
-     * @return
631
-     */
632
-    protected function _decide_whether_to_insert_or_update_given_data_from_same_db(
633
-        $id_in_csv,
634
-        $model_object_data,
635
-        $model
636
-    ) {
637
-        // in this case, check if this thing ACTUALLY exists in the database
638
-        if ($model->get_one_conflicting($model_object_data)) {
639
-            return self::do_update;
640
-        } else {
641
-            return self::do_insert;
642
-        }
643
-    }
644
-
645
-    /**
646
-     * Using the $old_db_to_new_db_mapping array, replaces all the temporary IDs
647
-     * with their mapped real IDs. Eg, if importing from site A to B, the mapping
648
-     * file may indicate that the ID "my_event_id" maps to an actual event ID of 123.
649
-     * So this function searches for any event temp Ids called "my_event_id" and
650
-     * replaces them with 123.
651
-     * Also, if there is no temp ID for the INT foreign keys from another database,
652
-     * replaces them with 0 or the field's default.
653
-     *
654
-     * @param type     $model_object_data
655
-     * @param EEM_Base $model
656
-     * @param type     $old_db_to_new_db_mapping
657
-     * @param boolean  $export_from_site_a_to_b
658
-     * @return array updated model object data with temp IDs removed
659
-     */
660
-    protected function _replace_temp_ids_with_mappings(
661
-        $model_object_data,
662
-        $model,
663
-        $old_db_to_new_db_mapping,
664
-        $export_from_site_a_to_b
665
-    ) {
666
-        // if this model object's primary key is in the mapping, replace it
667
-        if ($model->has_primary_key_field() &&
668
-            $model->get_primary_key_field()->is_auto_increment() &&
669
-            isset($old_db_to_new_db_mapping[ $model->get_this_model_name() ]) &&
670
-            isset(
671
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ]
672
-            )) {
673
-            $model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name(
674
-            ) ][ $model_object_data[ $model->primary_key_name() ] ];
675
-        }
676
-
677
-        try {
678
-            $model_name_field = $model->get_field_containing_related_model_name();
679
-            $models_pointed_to_by_model_name_field = $model_name_field->get_model_names_pointed_to();
680
-        } catch (EE_Error $e) {
681
-            $model_name_field = null;
682
-            $models_pointed_to_by_model_name_field = array();
683
-        }
684
-        foreach ($model->field_settings(true) as $field_obj) {
685
-            if ($field_obj instanceof EE_Foreign_Key_Int_Field) {
686
-                $models_pointed_to = $field_obj->get_model_names_pointed_to();
687
-                $found_a_mapping = false;
688
-                foreach ($models_pointed_to as $model_pointed_to_by_fk) {
689
-                    if ($model_name_field) {
690
-                        $value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ];
691
-                        if ($value_of_model_name_field == $model_pointed_to_by_fk) {
692
-                            $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
693
-                                $model_object_data[ $field_obj->get_name() ],
694
-                                $model_pointed_to_by_fk,
695
-                                $old_db_to_new_db_mapping,
696
-                                $export_from_site_a_to_b
697
-                            );
698
-                            $found_a_mapping = true;
699
-                            break;
700
-                        }
701
-                    } else {
702
-                        $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
703
-                            $model_object_data[ $field_obj->get_name() ],
704
-                            $model_pointed_to_by_fk,
705
-                            $old_db_to_new_db_mapping,
706
-                            $export_from_site_a_to_b
707
-                        );
708
-                        $found_a_mapping = true;
709
-                    }
710
-                    // once we've found a mapping for this field no need to continue
711
-                    if ($found_a_mapping) {
712
-                        break;
713
-                    }
714
-                }
715
-            } else {
716
-                // it's a string foreign key (which we leave alone, because those are things
717
-                // like country names, which we'd really rather not make 2 USAs etc (we'd actually
718
-                // prefer to just update one)
719
-                // or it's just a regular value that ought to be replaced
720
-            }
721
-        }
722
-        //
723
-        if ($model instanceof EEM_Term_Taxonomy) {
724
-            $model_object_data = $this->_handle_split_term_ids($model_object_data);
725
-        }
726
-        return $model_object_data;
727
-    }
728
-
729
-    /**
730
-     * If the data was exported PRE-4.2, but then imported POST-4.2, then the term_id
731
-     * this term-taxonomy refers to may be out-of-date so we need to update it.
732
-     * see https://make.wordpress.org/core/2015/02/16/taxonomy-term-splitting-in-4-2-a-developer-guide/
733
-     *
734
-     * @param type $model_object_data
735
-     * @return array new model object data
736
-     */
737
-    protected function _handle_split_term_ids($model_object_data)
738
-    {
739
-        if (isset($model_object_data['term_id'])
740
-            && isset($model_object_data['taxonomy'])
741
-            && apply_filters(
742
-                'FHEE__EE_Import__handle_split_term_ids__function_exists',
743
-                function_exists('wp_get_split_term'),
744
-                $model_object_data
745
-            )) {
746
-            $new_term_id = wp_get_split_term($model_object_data['term_id'], $model_object_data['taxonomy']);
747
-            if ($new_term_id) {
748
-                $model_object_data['term_id'] = $new_term_id;
749
-            }
750
-        }
751
-        return $model_object_data;
752
-    }
753
-
754
-    /**
755
-     * Given the object's ID and its model's name, find it int he mapping data,
756
-     * bearing in mind where it came from
757
-     *
758
-     * @param type   $object_id
759
-     * @param string $model_name
760
-     * @param array  $old_db_to_new_db_mapping
761
-     * @param type   $export_from_site_a_to_b
762
-     * @return int
763
-     */
764
-    protected function _find_mapping_in($object_id, $model_name, $old_db_to_new_db_mapping, $export_from_site_a_to_b)
765
-    {
766
-        if (isset($old_db_to_new_db_mapping[ $model_name ][ $object_id ])) {
767
-            return $old_db_to_new_db_mapping[ $model_name ][ $object_id ];
768
-        } elseif ($object_id == '0' || $object_id == '') {
769
-            // leave as-is
770
-            return $object_id;
771
-        } elseif ($export_from_site_a_to_b) {
772
-            // we couldn't find a mapping for this, and it's from a different site,
773
-            // so blank it out
774
-            return null;
775
-        } elseif (! $export_from_site_a_to_b) {
776
-            // we coudln't find a mapping for this, but it's from thsi DB anyway
777
-            // so let's just leave it as-is
778
-            return $object_id;
779
-        }
780
-    }
781
-
782
-    /**
783
-     *
784
-     * @param type     $id_in_csv
785
-     * @param type     $model_object_data
786
-     * @param EEM_Base $model
787
-     * @param type     $old_db_to_new_db_mapping
788
-     * @return array updated $old_db_to_new_db_mapping
789
-     */
790
-    protected function _insert_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
791
-    {
792
-        // remove the primary key, if there is one (we don't want it for inserts OR updates)
793
-        // we'll put it back in if we need it
794
-        if ($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()) {
795
-            $effective_id = $model_object_data[ $model->primary_key_name() ];
796
-            unset($model_object_data[ $model->primary_key_name() ]);
797
-        } else {
798
-            $effective_id = $model->get_index_primary_key_string($model_object_data);
799
-        }
800
-        // the model takes care of validating the CSV's input
801
-        try {
802
-            $new_id = $model->insert($model_object_data);
803
-            if ($new_id) {
804
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_id;
805
-                $this->_total_inserts++;
806
-                EE_Error::add_success(
807
-                    sprintf(
808
-                        __("Successfully added new %s (with id %s) with csv data %s", "event_espresso"),
809
-                        $model->get_this_model_name(),
810
-                        $new_id,
811
-                        implode(",", $model_object_data)
812
-                    )
813
-                );
814
-            } else {
815
-                $this->_total_insert_errors++;
816
-                // put the ID used back in there for the error message
817
-                if ($model->has_primary_key_field()) {
818
-                    $model_object_data[ $model->primary_key_name() ] = $effective_id;
819
-                }
820
-                EE_Error::add_error(
821
-                    sprintf(
822
-                        __("Could not insert new %s with the csv data: %s", "event_espresso"),
823
-                        $model->get_this_model_name(),
824
-                        http_build_query($model_object_data)
825
-                    ),
826
-                    __FILE__,
827
-                    __FUNCTION__,
828
-                    __LINE__
829
-                );
830
-            }
831
-        } catch (EE_Error $e) {
832
-            $this->_total_insert_errors++;
833
-            if ($model->has_primary_key_field()) {
834
-                $model_object_data[ $model->primary_key_name() ] = $effective_id;
835
-            }
836
-            EE_Error::add_error(
837
-                sprintf(
838
-                    __("Could not insert new %s with the csv data: %s because %s", "event_espresso"),
839
-                    $model->get_this_model_name(),
840
-                    implode(",", $model_object_data),
841
-                    $e->getMessage()
842
-                ),
843
-                __FILE__,
844
-                __FUNCTION__,
845
-                __LINE__
846
-            );
847
-        }
848
-        return $old_db_to_new_db_mapping;
849
-    }
850
-
851
-    /**
852
-     * Given the model object data, finds the row to update and updates it
853
-     *
854
-     * @param string|int $id_in_csv
855
-     * @param array      $model_object_data
856
-     * @param EEM_Base   $model
857
-     * @param array      $old_db_to_new_db_mapping
858
-     * @return array updated $old_db_to_new_db_mapping
859
-     */
860
-    protected function _update_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
861
-    {
862
-        try {
863
-            // let's keep two copies of the model object data:
864
-            // one for performing an update, one for everthing else
865
-            $model_object_data_for_update = $model_object_data;
866
-            if ($model->has_primary_key_field()) {
867
-                $conditions = array($model->primary_key_name() => $model_object_data[ $model->primary_key_name() ]);
868
-                // remove the primary key because we shouldn't use it for updating
869
-                unset($model_object_data_for_update[ $model->primary_key_name() ]);
870
-            } elseif ($model->get_combined_primary_key_fields() > 1) {
871
-                $conditions = array();
872
-                foreach ($model->get_combined_primary_key_fields() as $key_field) {
873
-                    $conditions[ $key_field->get_name() ] = $model_object_data[ $key_field->get_name() ];
874
-                }
875
-            } else {
876
-                $model->primary_key_name(
877
-                );// this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
878
-            }
879
-
880
-            $success = $model->update($model_object_data_for_update, array($conditions));
881
-            if ($success) {
882
-                $this->_total_updates++;
883
-                EE_Error::add_success(
884
-                    sprintf(
885
-                        __("Successfully updated %s with csv data %s", "event_espresso"),
886
-                        $model->get_this_model_name(),
887
-                        implode(",", $model_object_data_for_update)
888
-                    )
889
-                );
890
-                // we should still record the mapping even though it was an update
891
-                // because if we were going to insert somethign but it was going to conflict
892
-                // we would have last-minute decided to update. So we'd like to know what we updated
893
-                // and so we record what record ended up being updated using the mapping
894
-                if ($model->has_primary_key_field()) {
895
-                    $new_key_for_mapping = $model_object_data[ $model->primary_key_name() ];
896
-                } else {
897
-                    // no primary key just a combined key
898
-                    $new_key_for_mapping = $model->get_index_primary_key_string($model_object_data);
899
-                }
900
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping;
901
-            } else {
902
-                $matched_items = $model->get_all(array($conditions));
903
-                if (! $matched_items) {
904
-                    // no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck?
905
-                    $this->_total_update_errors++;
906
-                    EE_Error::add_error(
907
-                        sprintf(
908
-                            __(
909
-                                "Could not update %s with the csv data: '%s' for an unknown reason (using WHERE conditions %s)",
910
-                                "event_espresso"
911
-                            ),
912
-                            $model->get_this_model_name(),
913
-                            http_build_query($model_object_data),
914
-                            http_build_query($conditions)
915
-                        ),
916
-                        __FILE__,
917
-                        __FUNCTION__,
918
-                        __LINE__
919
-                    );
920
-                } else {
921
-                    $this->_total_updates++;
922
-                    EE_Error::add_success(
923
-                        sprintf(
924
-                            __(
925
-                                "%s with csv data '%s' was found in the database and didn't need updating because all the data is identical.",
926
-                                "event_espresso"
927
-                            ),
928
-                            $model->get_this_model_name(),
929
-                            implode(",", $model_object_data)
930
-                        )
931
-                    );
932
-                }
933
-            }
934
-        } catch (EE_Error $e) {
935
-            $this->_total_update_errors++;
936
-            $basic_message = sprintf(
937
-                __("Could not update %s with the csv data: %s because %s", "event_espresso"),
938
-                $model->get_this_model_name(),
939
-                implode(",", $model_object_data),
940
-                $e->getMessage()
941
-            );
942
-            $debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString();
943
-            EE_Error::add_error("$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__);
944
-        }
945
-        return $old_db_to_new_db_mapping;
946
-    }
947
-
948
-    /**
949
-     * Gets the number of inserts performed since importer was instantiated or reset
950
-     *
951
-     * @return int
952
-     */
953
-    public function get_total_inserts()
954
-    {
955
-        return $this->_total_inserts;
956
-    }
957
-
958
-    /**
959
-     *  Gets the number of insert errors since importer was instantiated or reset
960
-     *
961
-     * @return int
962
-     */
963
-    public function get_total_insert_errors()
964
-    {
965
-        return $this->_total_insert_errors;
966
-    }
967
-
968
-    /**
969
-     *  Gets the number of updates performed since importer was instantiated or reset
970
-     *
971
-     * @return int
972
-     */
973
-    public function get_total_updates()
974
-    {
975
-        return $this->_total_updates;
976
-    }
977
-
978
-    /**
979
-     *  Gets the number of update errors since importer was instantiated or reset
980
-     *
981
-     * @return int
982
-     */
983
-    public function get_total_update_errors()
984
-    {
985
-        return $this->_total_update_errors;
986
-    }
125
+		$uploader = ob_get_clean();
126
+		return $uploader;
127
+	}
128
+
129
+
130
+	/**
131
+	 * @Import Event Espresso data - some code "borrowed" from event espresso csv_import.php
132
+	 * @access public
133
+	 * @return boolean success
134
+	 */
135
+	public function import()
136
+	{
137
+
138
+		require_once(EE_CLASSES . 'EE_CSV.class.php');
139
+		$this->EE_CSV = EE_CSV::instance();
140
+
141
+		if (isset($_REQUEST['import'])) {
142
+			if (isset($_POST['csv_submitted'])) {
143
+				switch ($_FILES['file']['error'][0]) {
144
+					case UPLOAD_ERR_OK:
145
+						$error_msg = false;
146
+						break;
147
+					case UPLOAD_ERR_INI_SIZE:
148
+						$error_msg = __(
149
+							"'The uploaded file exceeds the upload_max_filesize directive in php.ini.'",
150
+							"event_espresso"
151
+						);
152
+						break;
153
+					case UPLOAD_ERR_FORM_SIZE:
154
+						$error_msg = __(
155
+							'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
156
+							"event_espresso"
157
+						);
158
+						break;
159
+					case UPLOAD_ERR_PARTIAL:
160
+						$error_msg = __('The uploaded file was only partially uploaded.', "event_espresso");
161
+						break;
162
+					case UPLOAD_ERR_NO_FILE:
163
+						$error_msg = __('No file was uploaded.', "event_espresso");
164
+						break;
165
+					case UPLOAD_ERR_NO_TMP_DIR:
166
+						$error_msg = __('Missing a temporary folder.', "event_espresso");
167
+						break;
168
+					case UPLOAD_ERR_CANT_WRITE:
169
+						$error_msg = __('Failed to write file to disk.', "event_espresso");
170
+						break;
171
+					case UPLOAD_ERR_EXTENSION:
172
+						$error_msg = __('File upload stopped by extension.', "event_espresso");
173
+						break;
174
+					default:
175
+						$error_msg = __(
176
+							'An unknown error occurred and the file could not be uploaded',
177
+							"event_espresso"
178
+						);
179
+						break;
180
+				}
181
+
182
+				if (! $error_msg) {
183
+					$filename = $_FILES['file']['name'][0];
184
+					$file_ext = substr(strrchr($filename, '.'), 1);
185
+					$file_type = $_FILES['file']['type'][0];
186
+					$temp_file = $_FILES['file']['tmp_name'][0];
187
+					$filesize = $_FILES['file']['size'][0] / 1024;// convert from bytes to KB
188
+
189
+					if ($file_ext == 'csv') {
190
+						$max_upload = $this->EE_CSV->get_max_upload_size();// max upload size in KB
191
+						if ($filesize < $max_upload || true) {
192
+							$wp_upload_dir = str_replace(array('\\', '/'), DS, wp_upload_dir());
193
+							$path_to_file = $wp_upload_dir['basedir'] . DS . 'espresso' . DS . $filename;
194
+
195
+							if (move_uploaded_file($temp_file, $path_to_file)) {
196
+								// convert csv to array
197
+								$this->csv_array = $this->EE_CSV->import_csv_to_model_data_array($path_to_file);
198
+
199
+								// was data successfully stored in an array?
200
+								if (is_array($this->csv_array)) {
201
+									$import_what = str_replace('csv_import_', '', $_REQUEST['action']);
202
+									$import_what = str_replace('_', ' ', ucwords($import_what));
203
+									$processed_data = $this->csv_array;
204
+									$this->columns_to_save = false;
205
+
206
+									// if any imports require funcky processing, we'll catch them in the switch
207
+									switch ($_REQUEST['action']) {
208
+										case "import_events":
209
+										case "event_list":
210
+											$import_what = 'Event Details';
211
+											break;
212
+
213
+										case 'groupon_import_csv':
214
+											$import_what = 'Groupon Codes';
215
+											$processed_data = $this->process_groupon_codes();
216
+											break;
217
+									}
218
+									// save processed codes to db
219
+									if ($this->save_csv_data_array_to_db($processed_data, $this->columns_to_save)) {
220
+										return true;
221
+									}
222
+								} else {
223
+									// no array? must be an error
224
+									EE_Error::add_error(
225
+										sprintf(__("No file seems to have been uploaded", "event_espresso")),
226
+										__FILE__,
227
+										__FUNCTION__,
228
+										__LINE__
229
+									);
230
+									return false;
231
+								}
232
+							} else {
233
+								EE_Error::add_error(
234
+									sprintf(__("%s was not successfully uploaded", "event_espresso"), $filename),
235
+									__FILE__,
236
+									__FUNCTION__,
237
+									__LINE__
238
+								);
239
+								return false;
240
+							}
241
+						} else {
242
+							EE_Error::add_error(
243
+								sprintf(
244
+									__(
245
+										"%s was too large of a file and could not be uploaded. The max filesize is %s' KB.",
246
+										"event_espresso"
247
+									),
248
+									$filename,
249
+									$max_upload
250
+								),
251
+								__FILE__,
252
+								__FUNCTION__,
253
+								__LINE__
254
+							);
255
+							return false;
256
+						}
257
+					} else {
258
+						EE_Error::add_error(
259
+							sprintf(__("%s  had an invalid file extension, not uploaded", "event_espresso"), $filename),
260
+							__FILE__,
261
+							__FUNCTION__,
262
+							__LINE__
263
+						);
264
+						return false;
265
+					}
266
+				} else {
267
+					EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
268
+					return false;
269
+				}
270
+			}
271
+		}
272
+		return;
273
+	}
274
+
275
+
276
+	/**
277
+	 *    Given an array of data (usually from a CSV import) attempts to save that data to the db.
278
+	 *    If $model_name ISN'T provided, assumes that this is a 3d array, with toplevel keys being model names,
279
+	 *    next level being numeric indexes adn each value representing a model object, and the last layer down
280
+	 *    being keys of model fields and their proposed values.
281
+	 *    If $model_name IS provided, assumes a 2d array of the bottom two layers previously mentioned.
282
+	 *    If the CSV data says (in the metadata row) that it's from the SAME database,
283
+	 *    we treat the IDs in the CSV as the normal IDs, and try to update those records. However, if those
284
+	 *    IDs DON'T exist in the database, they're treated as temporary IDs,
285
+	 *    which can used elsewhere to refer to the same object. Once an item
286
+	 *    with a temporary ID gets inserted, we record its mapping from temporary
287
+	 *    ID to real ID, and use the real ID in place of the temporary ID
288
+	 *    when that temporary ID was used as a foreign key.
289
+	 *    If the CSV data says (in the metadata again) that it's from a DIFFERENT database,
290
+	 *    we treat all the IDs in the CSV as temporary ID- eg, if the CSV specifies an event with
291
+	 *    ID 1, and the database already has an event with ID 1, we assume that's just a coincidence,
292
+	 *    and insert a new event, and map it's temporary ID of 1 over to its new real ID.
293
+	 *    An important exception are non-auto-increment primary keys. If one entry in the
294
+	 *    CSV file has the same ID as one in the DB, we assume they are meant to be
295
+	 *    the same item, and instead update the item in the DB with that same ID.
296
+	 *    Also note, we remember the mappings permanently. So the 2nd, 3rd, and 10000th
297
+	 *    time you import a CSV from a different site, we remember their mappings, and
298
+	 * will try to update the item in the DB instead of inserting another item (eg
299
+	 * if we previously imported an event with temporary ID 1, and then it got a
300
+	 * real ID of 123, we remember that. So the next time we import an event with
301
+	 * temporary ID, from the same site, we know that it's real ID is 123, and will
302
+	 * update that event, instead of adding a new event).
303
+	 *
304
+	 * @access public
305
+	 * @param array $csv_data_array - the array containing the csv data produced from
306
+	 *                              EE_CSV::import_csv_to_model_data_array()
307
+	 * @param array $fields_to_save - an array containing the csv column names as keys with the corresponding db table
308
+	 *                              fields they will be saved to
309
+	 * @return TRUE on success, FALSE on fail
310
+	 * @throws \EE_Error
311
+	 */
312
+	public function save_csv_data_array_to_db($csv_data_array, $model_name = false)
313
+	{
314
+		$success = false;
315
+		$error = false;
316
+		// whther to treat this import as if it's data froma different database or not
317
+		// ie, if it IS from a different database, ignore foreign keys whihf
318
+		$export_from_site_a_to_b = true;
319
+		// first level of array is not table information but a table name was passed to the function
320
+		// array is only two levels deep, so let's fix that by adding a level, else the next steps will fail
321
+		if ($model_name) {
322
+			$csv_data_array = array($csv_data_array);
323
+		}
324
+		// begin looking through the $csv_data_array, expecting the toplevel key to be the model's name...
325
+		$old_site_url = 'none-specified';
326
+		// hanlde metadata
327
+		if (isset($csv_data_array[ EE_CSV::metadata_header ])) {
328
+			$csv_metadata = array_shift($csv_data_array[ EE_CSV::metadata_header ]);
329
+			// ok so its metadata, dont try to save it to ehte db obviously...
330
+			if (isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()) {
331
+				EE_Error::add_attention(
332
+					sprintf(
333
+						__(
334
+							"CSV Data appears to be from the same database, so attempting to update data",
335
+							"event_espresso"
336
+						)
337
+					)
338
+				);
339
+				$export_from_site_a_to_b = false;
340
+			} else {
341
+				$old_site_url = isset($csv_metadata['site_url']) ? $csv_metadata['site_url'] : $old_site_url;
342
+				EE_Error::add_attention(
343
+					sprintf(
344
+						__(
345
+							"CSV Data appears to be from a different database (%s instead of %s), so we assume IDs in the CSV data DO NOT correspond to IDs in this database",
346
+							"event_espresso"
347
+						),
348
+						$old_site_url,
349
+						site_url()
350
+					)
351
+				);
352
+			};
353
+			unset($csv_data_array[ EE_CSV::metadata_header ]);
354
+		}
355
+		/**
356
+		 * @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, bottom-level keys being the original key, and
357
+		 * the value will be the newly-inserted ID.
358
+		 * If we have already imported data from the same website via CSV, it shoudl be kept in this wp option
359
+		 */
360
+		$old_db_to_new_db_mapping = get_option('ee_id_mapping_from' . sanitize_title($old_site_url), array());
361
+		if ($old_db_to_new_db_mapping) {
362
+			EE_Error::add_attention(
363
+				sprintf(
364
+					__(
365
+						"We noticed you have imported data via CSV from %s before. Because of this, IDs in your CSV have been mapped to their new IDs in %s",
366
+						"event_espresso"
367
+					),
368
+					$old_site_url,
369
+					site_url()
370
+				)
371
+			);
372
+		}
373
+		$old_db_to_new_db_mapping = $this->save_data_rows_to_db(
374
+			$csv_data_array,
375
+			$export_from_site_a_to_b,
376
+			$old_db_to_new_db_mapping
377
+		);
378
+
379
+		// save the mapping from old db to new db in case they try re-importing the same data from the same website again
380
+		update_option('ee_id_mapping_from' . sanitize_title($old_site_url), $old_db_to_new_db_mapping);
381
+
382
+		if ($this->_total_updates > 0) {
383
+			EE_Error::add_success(
384
+				sprintf(
385
+					__("%s existing records in the database were updated.", "event_espresso"),
386
+					$this->_total_updates
387
+				)
388
+			);
389
+			$success = true;
390
+		}
391
+		if ($this->_total_inserts > 0) {
392
+			EE_Error::add_success(
393
+				sprintf(__("%s new records were added to the database.", "event_espresso"), $this->_total_inserts)
394
+			);
395
+			$success = true;
396
+		}
397
+
398
+		if ($this->_total_update_errors > 0) {
399
+			EE_Error::add_error(
400
+				sprintf(
401
+					__(
402
+						"'One or more errors occurred, and a total of %s existing records in the database were <strong>not</strong> updated.'",
403
+						"event_espresso"
404
+					),
405
+					$this->_total_update_errors
406
+				),
407
+				__FILE__,
408
+				__FUNCTION__,
409
+				__LINE__
410
+			);
411
+			$error = true;
412
+		}
413
+		if ($this->_total_insert_errors > 0) {
414
+			EE_Error::add_error(
415
+				sprintf(
416
+					__(
417
+						"One or more errors occurred, and a total of %s new records were <strong>not</strong> added to the database.'",
418
+						"event_espresso"
419
+					),
420
+					$this->_total_insert_errors
421
+				),
422
+				__FILE__,
423
+				__FUNCTION__,
424
+				__LINE__
425
+			);
426
+			$error = true;
427
+		}
428
+
429
+		// lastly, we need to update the datetime and ticket sold amounts
430
+		// as those may have been affected by this
431
+		EEM_Ticket::instance()->update_tickets_sold(EEM_Ticket::instance()->get_all());
432
+
433
+		// if there was at least one success and absolutely no errors
434
+		if ($success && ! $error) {
435
+			return true;
436
+		} else {
437
+			return false;
438
+		}
439
+	}
440
+
441
+
442
+	/**
443
+	 * Processes the array of data, given the knowledge that it's from the same database or a different one,
444
+	 * and the mapping from temporary IDs to real IDs.
445
+	 * If the data is from a different database, we treat the primary keys and their corresponding
446
+	 * foreign keys as "temp Ids", basically identifiers that get mapped to real primary keys
447
+	 * in the real target database. As items are inserted, their temporary primary keys
448
+	 * are mapped to the real IDs in the target database. Also, before doing any update or
449
+	 * insert, we replace all the temp ID which are foreign keys with their mapped real IDs.
450
+	 * An exception: string primary keys are treated as real IDs, or else we'd need to
451
+	 * dynamically generate new string primary keys which would be very awkard for the country table etc.
452
+	 * Also, models with no primary key are strange too. We combine use their primar key INDEX (a
453
+	 * combination of fields) to create a unique string identifying the row and store
454
+	 * those in the mapping.
455
+	 *
456
+	 * If the data is from the same database, we usually treat primary keys as real IDs.
457
+	 * An exception is if there is nothing in the database for that ID. If that's the case,
458
+	 * we need to insert a new row for that ID, and then map from the non-existent ID
459
+	 * to the newly-inserted real ID.
460
+	 *
461
+	 * @param type $csv_data_array
462
+	 * @param type $export_from_site_a_to_b
463
+	 * @param type $old_db_to_new_db_mapping
464
+	 * @return array updated $old_db_to_new_db_mapping
465
+	 */
466
+	public function save_data_rows_to_db($csv_data_array, $export_from_site_a_to_b, $old_db_to_new_db_mapping)
467
+	{
468
+		foreach ($csv_data_array as $model_name_in_csv_data => $model_data_from_import) {
469
+			// now check that assumption was correct. If
470
+			if (EE_Registry::instance()->is_model_name($model_name_in_csv_data)) {
471
+				$model_name = $model_name_in_csv_data;
472
+			} else {
473
+				// no table info in the array and no table name passed to the function?? FAIL
474
+				EE_Error::add_error(
475
+					__(
476
+						'No table information was specified and/or found, therefore the import could not be completed',
477
+						'event_espresso'
478
+					),
479
+					__FILE__,
480
+					__FUNCTION__,
481
+					__LINE__
482
+				);
483
+				return false;
484
+			}
485
+			/* @var $model EEM_Base */
486
+			$model = EE_Registry::instance()->load_model($model_name);
487
+
488
+			// so without further ado, scanning all the data provided for primary keys and their inital values
489
+			foreach ($model_data_from_import as $model_object_data) {
490
+				// before we do ANYTHING, make sure the csv row wasn't just completely blank
491
+				$row_is_completely_empty = true;
492
+				foreach ($model_object_data as $field) {
493
+					if ($field) {
494
+						$row_is_completely_empty = false;
495
+					}
496
+				}
497
+				if ($row_is_completely_empty) {
498
+					continue;
499
+				}
500
+				// find the PK in the row of data (or a combined key if
501
+				// there is no primary key)
502
+				if ($model->has_primary_key_field()) {
503
+					$id_in_csv = $model_object_data[ $model->primary_key_name() ];
504
+				} else {
505
+					$id_in_csv = $model->get_index_primary_key_string($model_object_data);
506
+				}
507
+
508
+
509
+				$model_object_data = $this->_replace_temp_ids_with_mappings(
510
+					$model_object_data,
511
+					$model,
512
+					$old_db_to_new_db_mapping,
513
+					$export_from_site_a_to_b
514
+				);
515
+				// now we need to decide if we're going to add a new model object given the $model_object_data,
516
+				// or just update.
517
+				if ($export_from_site_a_to_b) {
518
+					$what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_other_db(
519
+						$id_in_csv,
520
+						$model_object_data,
521
+						$model,
522
+						$old_db_to_new_db_mapping
523
+					);
524
+				} else {// this is just a re-import
525
+					$what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_same_db(
526
+						$id_in_csv,
527
+						$model_object_data,
528
+						$model,
529
+						$old_db_to_new_db_mapping
530
+					);
531
+				}
532
+				if ($what_to_do == self::do_nothing) {
533
+					continue;
534
+				}
535
+
536
+				// double-check we actually want to insert, if that's what we're planning
537
+				// based on whether this item would be unique in the DB or not
538
+				if ($what_to_do == self::do_insert) {
539
+					// we're supposed to be inserting. But wait, will this thing
540
+					// be acceptable if inserted?
541
+					$conflicting = $model->get_one_conflicting($model_object_data, false);
542
+					if ($conflicting) {
543
+						// ok, this item would conflict if inserted. Just update the item that it conflicts with.
544
+						$what_to_do = self::do_update;
545
+						// and if this model has a primary key, remember its mapping
546
+						if ($model->has_primary_key_field()) {
547
+							$old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ] = $conflicting->ID();
548
+							$model_object_data[ $model->primary_key_name() ] = $conflicting->ID();
549
+						} else {
550
+							// we want to update this conflicting item, instead of inserting a conflicting item
551
+							// so we need to make sure they match entirely (its possible that they only conflicted on one field, but we need them to match on other fields
552
+							// for the WHERE conditions in the update). At the time of this comment, there were no models like this
553
+							foreach ($model->get_combined_primary_key_fields() as $key_field) {
554
+								$model_object_data[ $key_field->get_name() ] = $conflicting->get(
555
+									$key_field->get_name()
556
+								);
557
+							}
558
+						}
559
+					}
560
+				}
561
+				if ($what_to_do == self::do_insert) {
562
+					$old_db_to_new_db_mapping = $this->_insert_from_data_array(
563
+						$id_in_csv,
564
+						$model_object_data,
565
+						$model,
566
+						$old_db_to_new_db_mapping
567
+					);
568
+				} elseif ($what_to_do == self::do_update) {
569
+					$old_db_to_new_db_mapping = $this->_update_from_data_array(
570
+						$id_in_csv,
571
+						$model_object_data,
572
+						$model,
573
+						$old_db_to_new_db_mapping
574
+					);
575
+				} else {
576
+					throw new EE_Error(
577
+						sprintf(
578
+							__(
579
+								'Programming error. We shoudl be inserting or updating, but instead we are being told to "%s", whifh is invalid',
580
+								'event_espresso'
581
+							),
582
+							$what_to_do
583
+						)
584
+					);
585
+				}
586
+			}
587
+		}
588
+		return $old_db_to_new_db_mapping;
589
+	}
590
+
591
+
592
+	/**
593
+	 * Decides whether or not to insert, given that this data is from another database.
594
+	 * So, if the primary key of this $model_object_data already exists in the database,
595
+	 * it's just a coincidence and we should still insert. The only time we should
596
+	 * update is when we know what it maps to, or there's something that would
597
+	 * conflict (and we should instead just update that conflicting thing)
598
+	 *
599
+	 * @param string   $id_in_csv
600
+	 * @param array    $model_object_data        by reference so it can be modified
601
+	 * @param EEM_Base $model
602
+	 * @param array    $old_db_to_new_db_mapping by reference so it can be modified
603
+	 * @return string one of the consts on this class that starts with do_*
604
+	 */
605
+	protected function _decide_whether_to_insert_or_update_given_data_from_other_db(
606
+		$id_in_csv,
607
+		$model_object_data,
608
+		$model,
609
+		$old_db_to_new_db_mapping
610
+	) {
611
+		$model_name = $model->get_this_model_name();
612
+		// if it's a site-to-site export-and-import, see if this modelobject's id
613
+		// in the old data that we know of
614
+		if (isset($old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ])) {
615
+			return self::do_update;
616
+		} else {
617
+			return self::do_insert;
618
+		}
619
+	}
620
+
621
+	/**
622
+	 * If this thing basically already exists in the database, we want to update it;
623
+	 * otherwise insert it (ie, someone tweaked the CSV file, or the item was
624
+	 * deleted in the database so it should be re-inserted)
625
+	 *
626
+	 * @param type     $id_in_csv
627
+	 * @param type     $model_object_data
628
+	 * @param EEM_Base $model
629
+	 * @param type     $old_db_to_new_db_mapping
630
+	 * @return
631
+	 */
632
+	protected function _decide_whether_to_insert_or_update_given_data_from_same_db(
633
+		$id_in_csv,
634
+		$model_object_data,
635
+		$model
636
+	) {
637
+		// in this case, check if this thing ACTUALLY exists in the database
638
+		if ($model->get_one_conflicting($model_object_data)) {
639
+			return self::do_update;
640
+		} else {
641
+			return self::do_insert;
642
+		}
643
+	}
644
+
645
+	/**
646
+	 * Using the $old_db_to_new_db_mapping array, replaces all the temporary IDs
647
+	 * with their mapped real IDs. Eg, if importing from site A to B, the mapping
648
+	 * file may indicate that the ID "my_event_id" maps to an actual event ID of 123.
649
+	 * So this function searches for any event temp Ids called "my_event_id" and
650
+	 * replaces them with 123.
651
+	 * Also, if there is no temp ID for the INT foreign keys from another database,
652
+	 * replaces them with 0 or the field's default.
653
+	 *
654
+	 * @param type     $model_object_data
655
+	 * @param EEM_Base $model
656
+	 * @param type     $old_db_to_new_db_mapping
657
+	 * @param boolean  $export_from_site_a_to_b
658
+	 * @return array updated model object data with temp IDs removed
659
+	 */
660
+	protected function _replace_temp_ids_with_mappings(
661
+		$model_object_data,
662
+		$model,
663
+		$old_db_to_new_db_mapping,
664
+		$export_from_site_a_to_b
665
+	) {
666
+		// if this model object's primary key is in the mapping, replace it
667
+		if ($model->has_primary_key_field() &&
668
+			$model->get_primary_key_field()->is_auto_increment() &&
669
+			isset($old_db_to_new_db_mapping[ $model->get_this_model_name() ]) &&
670
+			isset(
671
+				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ]
672
+			)) {
673
+			$model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name(
674
+			) ][ $model_object_data[ $model->primary_key_name() ] ];
675
+		}
676
+
677
+		try {
678
+			$model_name_field = $model->get_field_containing_related_model_name();
679
+			$models_pointed_to_by_model_name_field = $model_name_field->get_model_names_pointed_to();
680
+		} catch (EE_Error $e) {
681
+			$model_name_field = null;
682
+			$models_pointed_to_by_model_name_field = array();
683
+		}
684
+		foreach ($model->field_settings(true) as $field_obj) {
685
+			if ($field_obj instanceof EE_Foreign_Key_Int_Field) {
686
+				$models_pointed_to = $field_obj->get_model_names_pointed_to();
687
+				$found_a_mapping = false;
688
+				foreach ($models_pointed_to as $model_pointed_to_by_fk) {
689
+					if ($model_name_field) {
690
+						$value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ];
691
+						if ($value_of_model_name_field == $model_pointed_to_by_fk) {
692
+							$model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
693
+								$model_object_data[ $field_obj->get_name() ],
694
+								$model_pointed_to_by_fk,
695
+								$old_db_to_new_db_mapping,
696
+								$export_from_site_a_to_b
697
+							);
698
+							$found_a_mapping = true;
699
+							break;
700
+						}
701
+					} else {
702
+						$model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
703
+							$model_object_data[ $field_obj->get_name() ],
704
+							$model_pointed_to_by_fk,
705
+							$old_db_to_new_db_mapping,
706
+							$export_from_site_a_to_b
707
+						);
708
+						$found_a_mapping = true;
709
+					}
710
+					// once we've found a mapping for this field no need to continue
711
+					if ($found_a_mapping) {
712
+						break;
713
+					}
714
+				}
715
+			} else {
716
+				// it's a string foreign key (which we leave alone, because those are things
717
+				// like country names, which we'd really rather not make 2 USAs etc (we'd actually
718
+				// prefer to just update one)
719
+				// or it's just a regular value that ought to be replaced
720
+			}
721
+		}
722
+		//
723
+		if ($model instanceof EEM_Term_Taxonomy) {
724
+			$model_object_data = $this->_handle_split_term_ids($model_object_data);
725
+		}
726
+		return $model_object_data;
727
+	}
728
+
729
+	/**
730
+	 * If the data was exported PRE-4.2, but then imported POST-4.2, then the term_id
731
+	 * this term-taxonomy refers to may be out-of-date so we need to update it.
732
+	 * see https://make.wordpress.org/core/2015/02/16/taxonomy-term-splitting-in-4-2-a-developer-guide/
733
+	 *
734
+	 * @param type $model_object_data
735
+	 * @return array new model object data
736
+	 */
737
+	protected function _handle_split_term_ids($model_object_data)
738
+	{
739
+		if (isset($model_object_data['term_id'])
740
+			&& isset($model_object_data['taxonomy'])
741
+			&& apply_filters(
742
+				'FHEE__EE_Import__handle_split_term_ids__function_exists',
743
+				function_exists('wp_get_split_term'),
744
+				$model_object_data
745
+			)) {
746
+			$new_term_id = wp_get_split_term($model_object_data['term_id'], $model_object_data['taxonomy']);
747
+			if ($new_term_id) {
748
+				$model_object_data['term_id'] = $new_term_id;
749
+			}
750
+		}
751
+		return $model_object_data;
752
+	}
753
+
754
+	/**
755
+	 * Given the object's ID and its model's name, find it int he mapping data,
756
+	 * bearing in mind where it came from
757
+	 *
758
+	 * @param type   $object_id
759
+	 * @param string $model_name
760
+	 * @param array  $old_db_to_new_db_mapping
761
+	 * @param type   $export_from_site_a_to_b
762
+	 * @return int
763
+	 */
764
+	protected function _find_mapping_in($object_id, $model_name, $old_db_to_new_db_mapping, $export_from_site_a_to_b)
765
+	{
766
+		if (isset($old_db_to_new_db_mapping[ $model_name ][ $object_id ])) {
767
+			return $old_db_to_new_db_mapping[ $model_name ][ $object_id ];
768
+		} elseif ($object_id == '0' || $object_id == '') {
769
+			// leave as-is
770
+			return $object_id;
771
+		} elseif ($export_from_site_a_to_b) {
772
+			// we couldn't find a mapping for this, and it's from a different site,
773
+			// so blank it out
774
+			return null;
775
+		} elseif (! $export_from_site_a_to_b) {
776
+			// we coudln't find a mapping for this, but it's from thsi DB anyway
777
+			// so let's just leave it as-is
778
+			return $object_id;
779
+		}
780
+	}
781
+
782
+	/**
783
+	 *
784
+	 * @param type     $id_in_csv
785
+	 * @param type     $model_object_data
786
+	 * @param EEM_Base $model
787
+	 * @param type     $old_db_to_new_db_mapping
788
+	 * @return array updated $old_db_to_new_db_mapping
789
+	 */
790
+	protected function _insert_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
791
+	{
792
+		// remove the primary key, if there is one (we don't want it for inserts OR updates)
793
+		// we'll put it back in if we need it
794
+		if ($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()) {
795
+			$effective_id = $model_object_data[ $model->primary_key_name() ];
796
+			unset($model_object_data[ $model->primary_key_name() ]);
797
+		} else {
798
+			$effective_id = $model->get_index_primary_key_string($model_object_data);
799
+		}
800
+		// the model takes care of validating the CSV's input
801
+		try {
802
+			$new_id = $model->insert($model_object_data);
803
+			if ($new_id) {
804
+				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_id;
805
+				$this->_total_inserts++;
806
+				EE_Error::add_success(
807
+					sprintf(
808
+						__("Successfully added new %s (with id %s) with csv data %s", "event_espresso"),
809
+						$model->get_this_model_name(),
810
+						$new_id,
811
+						implode(",", $model_object_data)
812
+					)
813
+				);
814
+			} else {
815
+				$this->_total_insert_errors++;
816
+				// put the ID used back in there for the error message
817
+				if ($model->has_primary_key_field()) {
818
+					$model_object_data[ $model->primary_key_name() ] = $effective_id;
819
+				}
820
+				EE_Error::add_error(
821
+					sprintf(
822
+						__("Could not insert new %s with the csv data: %s", "event_espresso"),
823
+						$model->get_this_model_name(),
824
+						http_build_query($model_object_data)
825
+					),
826
+					__FILE__,
827
+					__FUNCTION__,
828
+					__LINE__
829
+				);
830
+			}
831
+		} catch (EE_Error $e) {
832
+			$this->_total_insert_errors++;
833
+			if ($model->has_primary_key_field()) {
834
+				$model_object_data[ $model->primary_key_name() ] = $effective_id;
835
+			}
836
+			EE_Error::add_error(
837
+				sprintf(
838
+					__("Could not insert new %s with the csv data: %s because %s", "event_espresso"),
839
+					$model->get_this_model_name(),
840
+					implode(",", $model_object_data),
841
+					$e->getMessage()
842
+				),
843
+				__FILE__,
844
+				__FUNCTION__,
845
+				__LINE__
846
+			);
847
+		}
848
+		return $old_db_to_new_db_mapping;
849
+	}
850
+
851
+	/**
852
+	 * Given the model object data, finds the row to update and updates it
853
+	 *
854
+	 * @param string|int $id_in_csv
855
+	 * @param array      $model_object_data
856
+	 * @param EEM_Base   $model
857
+	 * @param array      $old_db_to_new_db_mapping
858
+	 * @return array updated $old_db_to_new_db_mapping
859
+	 */
860
+	protected function _update_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
861
+	{
862
+		try {
863
+			// let's keep two copies of the model object data:
864
+			// one for performing an update, one for everthing else
865
+			$model_object_data_for_update = $model_object_data;
866
+			if ($model->has_primary_key_field()) {
867
+				$conditions = array($model->primary_key_name() => $model_object_data[ $model->primary_key_name() ]);
868
+				// remove the primary key because we shouldn't use it for updating
869
+				unset($model_object_data_for_update[ $model->primary_key_name() ]);
870
+			} elseif ($model->get_combined_primary_key_fields() > 1) {
871
+				$conditions = array();
872
+				foreach ($model->get_combined_primary_key_fields() as $key_field) {
873
+					$conditions[ $key_field->get_name() ] = $model_object_data[ $key_field->get_name() ];
874
+				}
875
+			} else {
876
+				$model->primary_key_name(
877
+				);// this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
878
+			}
879
+
880
+			$success = $model->update($model_object_data_for_update, array($conditions));
881
+			if ($success) {
882
+				$this->_total_updates++;
883
+				EE_Error::add_success(
884
+					sprintf(
885
+						__("Successfully updated %s with csv data %s", "event_espresso"),
886
+						$model->get_this_model_name(),
887
+						implode(",", $model_object_data_for_update)
888
+					)
889
+				);
890
+				// we should still record the mapping even though it was an update
891
+				// because if we were going to insert somethign but it was going to conflict
892
+				// we would have last-minute decided to update. So we'd like to know what we updated
893
+				// and so we record what record ended up being updated using the mapping
894
+				if ($model->has_primary_key_field()) {
895
+					$new_key_for_mapping = $model_object_data[ $model->primary_key_name() ];
896
+				} else {
897
+					// no primary key just a combined key
898
+					$new_key_for_mapping = $model->get_index_primary_key_string($model_object_data);
899
+				}
900
+				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping;
901
+			} else {
902
+				$matched_items = $model->get_all(array($conditions));
903
+				if (! $matched_items) {
904
+					// no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck?
905
+					$this->_total_update_errors++;
906
+					EE_Error::add_error(
907
+						sprintf(
908
+							__(
909
+								"Could not update %s with the csv data: '%s' for an unknown reason (using WHERE conditions %s)",
910
+								"event_espresso"
911
+							),
912
+							$model->get_this_model_name(),
913
+							http_build_query($model_object_data),
914
+							http_build_query($conditions)
915
+						),
916
+						__FILE__,
917
+						__FUNCTION__,
918
+						__LINE__
919
+					);
920
+				} else {
921
+					$this->_total_updates++;
922
+					EE_Error::add_success(
923
+						sprintf(
924
+							__(
925
+								"%s with csv data '%s' was found in the database and didn't need updating because all the data is identical.",
926
+								"event_espresso"
927
+							),
928
+							$model->get_this_model_name(),
929
+							implode(",", $model_object_data)
930
+						)
931
+					);
932
+				}
933
+			}
934
+		} catch (EE_Error $e) {
935
+			$this->_total_update_errors++;
936
+			$basic_message = sprintf(
937
+				__("Could not update %s with the csv data: %s because %s", "event_espresso"),
938
+				$model->get_this_model_name(),
939
+				implode(",", $model_object_data),
940
+				$e->getMessage()
941
+			);
942
+			$debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString();
943
+			EE_Error::add_error("$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__);
944
+		}
945
+		return $old_db_to_new_db_mapping;
946
+	}
947
+
948
+	/**
949
+	 * Gets the number of inserts performed since importer was instantiated or reset
950
+	 *
951
+	 * @return int
952
+	 */
953
+	public function get_total_inserts()
954
+	{
955
+		return $this->_total_inserts;
956
+	}
957
+
958
+	/**
959
+	 *  Gets the number of insert errors since importer was instantiated or reset
960
+	 *
961
+	 * @return int
962
+	 */
963
+	public function get_total_insert_errors()
964
+	{
965
+		return $this->_total_insert_errors;
966
+	}
967
+
968
+	/**
969
+	 *  Gets the number of updates performed since importer was instantiated or reset
970
+	 *
971
+	 * @return int
972
+	 */
973
+	public function get_total_updates()
974
+	{
975
+		return $this->_total_updates;
976
+	}
977
+
978
+	/**
979
+	 *  Gets the number of update errors since importer was instantiated or reset
980
+	 *
981
+	 * @return int
982
+	 */
983
+	public function get_total_update_errors()
984
+	{
985
+		return $this->_total_update_errors;
986
+	}
987 987
 }
Please login to merge, or discard this patch.
Spacing   +40 added lines, -40 removed lines patch added patch discarded remove patch
@@ -135,7 +135,7 @@  discard block
 block discarded – undo
135 135
     public function import()
136 136
     {
137 137
 
138
-        require_once(EE_CLASSES . 'EE_CSV.class.php');
138
+        require_once(EE_CLASSES.'EE_CSV.class.php');
139 139
         $this->EE_CSV = EE_CSV::instance();
140 140
 
141 141
         if (isset($_REQUEST['import'])) {
@@ -179,18 +179,18 @@  discard block
 block discarded – undo
179 179
                         break;
180 180
                 }
181 181
 
182
-                if (! $error_msg) {
182
+                if ( ! $error_msg) {
183 183
                     $filename = $_FILES['file']['name'][0];
184 184
                     $file_ext = substr(strrchr($filename, '.'), 1);
185 185
                     $file_type = $_FILES['file']['type'][0];
186 186
                     $temp_file = $_FILES['file']['tmp_name'][0];
187
-                    $filesize = $_FILES['file']['size'][0] / 1024;// convert from bytes to KB
187
+                    $filesize = $_FILES['file']['size'][0] / 1024; // convert from bytes to KB
188 188
 
189 189
                     if ($file_ext == 'csv') {
190
-                        $max_upload = $this->EE_CSV->get_max_upload_size();// max upload size in KB
190
+                        $max_upload = $this->EE_CSV->get_max_upload_size(); // max upload size in KB
191 191
                         if ($filesize < $max_upload || true) {
192 192
                             $wp_upload_dir = str_replace(array('\\', '/'), DS, wp_upload_dir());
193
-                            $path_to_file = $wp_upload_dir['basedir'] . DS . 'espresso' . DS . $filename;
193
+                            $path_to_file = $wp_upload_dir['basedir'].DS.'espresso'.DS.$filename;
194 194
 
195 195
                             if (move_uploaded_file($temp_file, $path_to_file)) {
196 196
                                 // convert csv to array
@@ -324,8 +324,8 @@  discard block
 block discarded – undo
324 324
         // begin looking through the $csv_data_array, expecting the toplevel key to be the model's name...
325 325
         $old_site_url = 'none-specified';
326 326
         // hanlde metadata
327
-        if (isset($csv_data_array[ EE_CSV::metadata_header ])) {
328
-            $csv_metadata = array_shift($csv_data_array[ EE_CSV::metadata_header ]);
327
+        if (isset($csv_data_array[EE_CSV::metadata_header])) {
328
+            $csv_metadata = array_shift($csv_data_array[EE_CSV::metadata_header]);
329 329
             // ok so its metadata, dont try to save it to ehte db obviously...
330 330
             if (isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()) {
331 331
                 EE_Error::add_attention(
@@ -350,14 +350,14 @@  discard block
 block discarded – undo
350 350
                     )
351 351
                 );
352 352
             };
353
-            unset($csv_data_array[ EE_CSV::metadata_header ]);
353
+            unset($csv_data_array[EE_CSV::metadata_header]);
354 354
         }
355 355
         /**
356 356
          * @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, bottom-level keys being the original key, and
357 357
          * the value will be the newly-inserted ID.
358 358
          * If we have already imported data from the same website via CSV, it shoudl be kept in this wp option
359 359
          */
360
-        $old_db_to_new_db_mapping = get_option('ee_id_mapping_from' . sanitize_title($old_site_url), array());
360
+        $old_db_to_new_db_mapping = get_option('ee_id_mapping_from'.sanitize_title($old_site_url), array());
361 361
         if ($old_db_to_new_db_mapping) {
362 362
             EE_Error::add_attention(
363 363
                 sprintf(
@@ -377,7 +377,7 @@  discard block
 block discarded – undo
377 377
         );
378 378
 
379 379
         // save the mapping from old db to new db in case they try re-importing the same data from the same website again
380
-        update_option('ee_id_mapping_from' . sanitize_title($old_site_url), $old_db_to_new_db_mapping);
380
+        update_option('ee_id_mapping_from'.sanitize_title($old_site_url), $old_db_to_new_db_mapping);
381 381
 
382 382
         if ($this->_total_updates > 0) {
383 383
             EE_Error::add_success(
@@ -500,7 +500,7 @@  discard block
 block discarded – undo
500 500
                 // find the PK in the row of data (or a combined key if
501 501
                 // there is no primary key)
502 502
                 if ($model->has_primary_key_field()) {
503
-                    $id_in_csv = $model_object_data[ $model->primary_key_name() ];
503
+                    $id_in_csv = $model_object_data[$model->primary_key_name()];
504 504
                 } else {
505 505
                     $id_in_csv = $model->get_index_primary_key_string($model_object_data);
506 506
                 }
@@ -544,14 +544,14 @@  discard block
 block discarded – undo
544 544
                         $what_to_do = self::do_update;
545 545
                         // and if this model has a primary key, remember its mapping
546 546
                         if ($model->has_primary_key_field()) {
547
-                            $old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ] = $conflicting->ID();
548
-                            $model_object_data[ $model->primary_key_name() ] = $conflicting->ID();
547
+                            $old_db_to_new_db_mapping[$model_name][$id_in_csv] = $conflicting->ID();
548
+                            $model_object_data[$model->primary_key_name()] = $conflicting->ID();
549 549
                         } else {
550 550
                             // we want to update this conflicting item, instead of inserting a conflicting item
551 551
                             // so we need to make sure they match entirely (its possible that they only conflicted on one field, but we need them to match on other fields
552 552
                             // for the WHERE conditions in the update). At the time of this comment, there were no models like this
553 553
                             foreach ($model->get_combined_primary_key_fields() as $key_field) {
554
-                                $model_object_data[ $key_field->get_name() ] = $conflicting->get(
554
+                                $model_object_data[$key_field->get_name()] = $conflicting->get(
555 555
                                     $key_field->get_name()
556 556
                                 );
557 557
                             }
@@ -611,7 +611,7 @@  discard block
 block discarded – undo
611 611
         $model_name = $model->get_this_model_name();
612 612
         // if it's a site-to-site export-and-import, see if this modelobject's id
613 613
         // in the old data that we know of
614
-        if (isset($old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ])) {
614
+        if (isset($old_db_to_new_db_mapping[$model_name][$id_in_csv])) {
615 615
             return self::do_update;
616 616
         } else {
617 617
             return self::do_insert;
@@ -666,12 +666,12 @@  discard block
 block discarded – undo
666 666
         // if this model object's primary key is in the mapping, replace it
667 667
         if ($model->has_primary_key_field() &&
668 668
             $model->get_primary_key_field()->is_auto_increment() &&
669
-            isset($old_db_to_new_db_mapping[ $model->get_this_model_name() ]) &&
669
+            isset($old_db_to_new_db_mapping[$model->get_this_model_name()]) &&
670 670
             isset(
671
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ]
671
+                $old_db_to_new_db_mapping[$model->get_this_model_name()][$model_object_data[$model->primary_key_name()]]
672 672
             )) {
673
-            $model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name(
674
-            ) ][ $model_object_data[ $model->primary_key_name() ] ];
673
+            $model_object_data[$model->primary_key_name()] = $old_db_to_new_db_mapping[$model->get_this_model_name(
674
+            )][$model_object_data[$model->primary_key_name()]];
675 675
         }
676 676
 
677 677
         try {
@@ -687,10 +687,10 @@  discard block
 block discarded – undo
687 687
                 $found_a_mapping = false;
688 688
                 foreach ($models_pointed_to as $model_pointed_to_by_fk) {
689 689
                     if ($model_name_field) {
690
-                        $value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ];
690
+                        $value_of_model_name_field = $model_object_data[$model_name_field->get_name()];
691 691
                         if ($value_of_model_name_field == $model_pointed_to_by_fk) {
692
-                            $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
693
-                                $model_object_data[ $field_obj->get_name() ],
692
+                            $model_object_data[$field_obj->get_name()] = $this->_find_mapping_in(
693
+                                $model_object_data[$field_obj->get_name()],
694 694
                                 $model_pointed_to_by_fk,
695 695
                                 $old_db_to_new_db_mapping,
696 696
                                 $export_from_site_a_to_b
@@ -699,8 +699,8 @@  discard block
 block discarded – undo
699 699
                             break;
700 700
                         }
701 701
                     } else {
702
-                        $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
703
-                            $model_object_data[ $field_obj->get_name() ],
702
+                        $model_object_data[$field_obj->get_name()] = $this->_find_mapping_in(
703
+                            $model_object_data[$field_obj->get_name()],
704 704
                             $model_pointed_to_by_fk,
705 705
                             $old_db_to_new_db_mapping,
706 706
                             $export_from_site_a_to_b
@@ -763,8 +763,8 @@  discard block
 block discarded – undo
763 763
      */
764 764
     protected function _find_mapping_in($object_id, $model_name, $old_db_to_new_db_mapping, $export_from_site_a_to_b)
765 765
     {
766
-        if (isset($old_db_to_new_db_mapping[ $model_name ][ $object_id ])) {
767
-            return $old_db_to_new_db_mapping[ $model_name ][ $object_id ];
766
+        if (isset($old_db_to_new_db_mapping[$model_name][$object_id])) {
767
+            return $old_db_to_new_db_mapping[$model_name][$object_id];
768 768
         } elseif ($object_id == '0' || $object_id == '') {
769 769
             // leave as-is
770 770
             return $object_id;
@@ -772,7 +772,7 @@  discard block
 block discarded – undo
772 772
             // we couldn't find a mapping for this, and it's from a different site,
773 773
             // so blank it out
774 774
             return null;
775
-        } elseif (! $export_from_site_a_to_b) {
775
+        } elseif ( ! $export_from_site_a_to_b) {
776 776
             // we coudln't find a mapping for this, but it's from thsi DB anyway
777 777
             // so let's just leave it as-is
778 778
             return $object_id;
@@ -792,8 +792,8 @@  discard block
 block discarded – undo
792 792
         // remove the primary key, if there is one (we don't want it for inserts OR updates)
793 793
         // we'll put it back in if we need it
794 794
         if ($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()) {
795
-            $effective_id = $model_object_data[ $model->primary_key_name() ];
796
-            unset($model_object_data[ $model->primary_key_name() ]);
795
+            $effective_id = $model_object_data[$model->primary_key_name()];
796
+            unset($model_object_data[$model->primary_key_name()]);
797 797
         } else {
798 798
             $effective_id = $model->get_index_primary_key_string($model_object_data);
799 799
         }
@@ -801,7 +801,7 @@  discard block
 block discarded – undo
801 801
         try {
802 802
             $new_id = $model->insert($model_object_data);
803 803
             if ($new_id) {
804
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_id;
804
+                $old_db_to_new_db_mapping[$model->get_this_model_name()][$id_in_csv] = $new_id;
805 805
                 $this->_total_inserts++;
806 806
                 EE_Error::add_success(
807 807
                     sprintf(
@@ -815,7 +815,7 @@  discard block
 block discarded – undo
815 815
                 $this->_total_insert_errors++;
816 816
                 // put the ID used back in there for the error message
817 817
                 if ($model->has_primary_key_field()) {
818
-                    $model_object_data[ $model->primary_key_name() ] = $effective_id;
818
+                    $model_object_data[$model->primary_key_name()] = $effective_id;
819 819
                 }
820 820
                 EE_Error::add_error(
821 821
                     sprintf(
@@ -831,7 +831,7 @@  discard block
 block discarded – undo
831 831
         } catch (EE_Error $e) {
832 832
             $this->_total_insert_errors++;
833 833
             if ($model->has_primary_key_field()) {
834
-                $model_object_data[ $model->primary_key_name() ] = $effective_id;
834
+                $model_object_data[$model->primary_key_name()] = $effective_id;
835 835
             }
836 836
             EE_Error::add_error(
837 837
                 sprintf(
@@ -864,17 +864,17 @@  discard block
 block discarded – undo
864 864
             // one for performing an update, one for everthing else
865 865
             $model_object_data_for_update = $model_object_data;
866 866
             if ($model->has_primary_key_field()) {
867
-                $conditions = array($model->primary_key_name() => $model_object_data[ $model->primary_key_name() ]);
867
+                $conditions = array($model->primary_key_name() => $model_object_data[$model->primary_key_name()]);
868 868
                 // remove the primary key because we shouldn't use it for updating
869
-                unset($model_object_data_for_update[ $model->primary_key_name() ]);
869
+                unset($model_object_data_for_update[$model->primary_key_name()]);
870 870
             } elseif ($model->get_combined_primary_key_fields() > 1) {
871 871
                 $conditions = array();
872 872
                 foreach ($model->get_combined_primary_key_fields() as $key_field) {
873
-                    $conditions[ $key_field->get_name() ] = $model_object_data[ $key_field->get_name() ];
873
+                    $conditions[$key_field->get_name()] = $model_object_data[$key_field->get_name()];
874 874
                 }
875 875
             } else {
876 876
                 $model->primary_key_name(
877
-                );// this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
877
+                ); // this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
878 878
             }
879 879
 
880 880
             $success = $model->update($model_object_data_for_update, array($conditions));
@@ -892,15 +892,15 @@  discard block
 block discarded – undo
892 892
                 // we would have last-minute decided to update. So we'd like to know what we updated
893 893
                 // and so we record what record ended up being updated using the mapping
894 894
                 if ($model->has_primary_key_field()) {
895
-                    $new_key_for_mapping = $model_object_data[ $model->primary_key_name() ];
895
+                    $new_key_for_mapping = $model_object_data[$model->primary_key_name()];
896 896
                 } else {
897 897
                     // no primary key just a combined key
898 898
                     $new_key_for_mapping = $model->get_index_primary_key_string($model_object_data);
899 899
                 }
900
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping;
900
+                $old_db_to_new_db_mapping[$model->get_this_model_name()][$id_in_csv] = $new_key_for_mapping;
901 901
             } else {
902 902
                 $matched_items = $model->get_all(array($conditions));
903
-                if (! $matched_items) {
903
+                if ( ! $matched_items) {
904 904
                     // no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck?
905 905
                     $this->_total_update_errors++;
906 906
                     EE_Error::add_error(
@@ -939,7 +939,7 @@  discard block
 block discarded – undo
939 939
                 implode(",", $model_object_data),
940 940
                 $e->getMessage()
941 941
             );
942
-            $debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString();
942
+            $debug_message = $basic_message.' Stack trace: '.$e->getTraceAsString();
943 943
             EE_Error::add_error("$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__);
944 944
         }
945 945
         return $old_db_to_new_db_mapping;
Please login to merge, or discard this patch.
core/db_classes/EE_Event.class.php 2 patches
Indentation   +1343 added lines, -1343 removed lines patch added patch discarded remove patch
@@ -13,1347 +13,1347 @@
 block discarded – undo
13 13
 class EE_Event extends EE_CPT_Base implements EEI_Line_Item_Object, EEI_Admin_Links, EEI_Has_Icon, EEI_Event
14 14
 {
15 15
 
16
-    /**
17
-     * cached value for the the logical active status for the event
18
-     *
19
-     * @see get_active_status()
20
-     * @var string
21
-     */
22
-    protected $_active_status = '';
23
-
24
-    /**
25
-     * This is just used for caching the Primary Datetime for the Event on initial retrieval
26
-     *
27
-     * @var EE_Datetime
28
-     */
29
-    protected $_Primary_Datetime;
30
-
31
-    /**
32
-     * @var EventSpacesCalculator $available_spaces_calculator
33
-     */
34
-    protected $available_spaces_calculator;
35
-
36
-
37
-    /**
38
-     * @param array  $props_n_values          incoming values
39
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
40
-     *                                        used.)
41
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
42
-     *                                        date_format and the second value is the time format
43
-     * @return EE_Event
44
-     * @throws EE_Error
45
-     */
46
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
47
-    {
48
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
49
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
50
-    }
51
-
52
-
53
-    /**
54
-     * @param array  $props_n_values  incoming values from the database
55
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
56
-     *                                the website will be used.
57
-     * @return EE_Event
58
-     * @throws EE_Error
59
-     */
60
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
61
-    {
62
-        return new self($props_n_values, true, $timezone);
63
-    }
64
-
65
-
66
-    /**
67
-     * @return EventSpacesCalculator
68
-     * @throws \EE_Error
69
-     */
70
-    public function getAvailableSpacesCalculator()
71
-    {
72
-        if (! $this->available_spaces_calculator instanceof EventSpacesCalculator) {
73
-            $this->available_spaces_calculator = new EventSpacesCalculator($this);
74
-        }
75
-        return $this->available_spaces_calculator;
76
-    }
77
-
78
-
79
-    /**
80
-     * Overrides parent set() method so that all calls to set( 'status', $status ) can be routed to internal methods
81
-     *
82
-     * @param string $field_name
83
-     * @param mixed  $field_value
84
-     * @param bool   $use_default
85
-     * @throws EE_Error
86
-     */
87
-    public function set($field_name, $field_value, $use_default = false)
88
-    {
89
-        switch ($field_name) {
90
-            case 'status':
91
-                $this->set_status($field_value, $use_default);
92
-                break;
93
-            default:
94
-                parent::set($field_name, $field_value, $use_default);
95
-        }
96
-    }
97
-
98
-
99
-    /**
100
-     *    set_status
101
-     * Checks if event status is being changed to SOLD OUT
102
-     * and updates event meta data with previous event status
103
-     * so that we can revert things if/when the event is no longer sold out
104
-     *
105
-     * @access public
106
-     * @param string $new_status
107
-     * @param bool   $use_default
108
-     * @return void
109
-     * @throws EE_Error
110
-     */
111
-    public function set_status($new_status = null, $use_default = false)
112
-    {
113
-        // if nothing is set, and we aren't explicitly wanting to reset the status, then just leave
114
-        if (empty($new_status) && ! $use_default) {
115
-            return;
116
-        }
117
-        // get current Event status
118
-        $old_status = $this->status();
119
-        // if status has changed
120
-        if ($old_status !== $new_status) {
121
-            // TO sold_out
122
-            if ($new_status === EEM_Event::sold_out) {
123
-                // save the previous event status so that we can revert if the event is no longer sold out
124
-                $this->add_post_meta('_previous_event_status', $old_status);
125
-                do_action('AHEE__EE_Event__set_status__to_sold_out', $this, $old_status, $new_status);
126
-                // OR FROM  sold_out
127
-            } elseif ($old_status === EEM_Event::sold_out) {
128
-                $this->delete_post_meta('_previous_event_status');
129
-                do_action('AHEE__EE_Event__set_status__from_sold_out', $this, $old_status, $new_status);
130
-            }
131
-            // clear out the active status so that it gets reset the next time it is requested
132
-            $this->_active_status = null;
133
-            // update status
134
-            parent::set('status', $new_status, $use_default);
135
-            do_action('AHEE__EE_Event__set_status__after_update', $this);
136
-            return;
137
-        }
138
-        // even though the old value matches the new value, it's still good to
139
-        // allow the parent set method to have a say
140
-        parent::set('status', $new_status, $use_default);
141
-    }
142
-
143
-
144
-    /**
145
-     * Gets all the datetimes for this event
146
-     *
147
-     * @param array $query_params like EEM_Base::get_all
148
-     * @return EE_Base_Class[]|EE_Datetime[]
149
-     * @throws EE_Error
150
-     */
151
-    public function datetimes($query_params = array())
152
-    {
153
-        return $this->get_many_related('Datetime', $query_params);
154
-    }
155
-
156
-
157
-    /**
158
-     * Gets all the datetimes for this event, ordered by DTT_EVT_start in ascending order
159
-     *
160
-     * @return EE_Base_Class[]|EE_Datetime[]
161
-     * @throws EE_Error
162
-     */
163
-    public function datetimes_in_chronological_order()
164
-    {
165
-        return $this->get_many_related('Datetime', array('order_by' => array('DTT_EVT_start' => 'ASC')));
166
-    }
167
-
168
-
169
-    /**
170
-     * Gets all the datetimes for this event, ordered by the DTT_order on the datetime.
171
-     * @darren, we should probably UNSET timezone on the EEM_Datetime model
172
-     * after running our query, so that this timezone isn't set for EVERY query
173
-     * on EEM_Datetime for the rest of the request, no?
174
-     *
175
-     * @param boolean $show_expired whether or not to include expired events
176
-     * @param boolean $show_deleted whether or not to include deleted events
177
-     * @param null    $limit
178
-     * @return EE_Datetime[]
179
-     * @throws EE_Error
180
-     */
181
-    public function datetimes_ordered($show_expired = true, $show_deleted = false, $limit = null)
182
-    {
183
-        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_event_ordered_by_DTT_order(
184
-            $this->ID(),
185
-            $show_expired,
186
-            $show_deleted,
187
-            $limit
188
-        );
189
-    }
190
-
191
-
192
-    /**
193
-     * Returns one related datetime. Mostly only used by some legacy code.
194
-     *
195
-     * @return EE_Base_Class|EE_Datetime
196
-     * @throws EE_Error
197
-     */
198
-    public function first_datetime()
199
-    {
200
-        return $this->get_first_related('Datetime');
201
-    }
202
-
203
-
204
-    /**
205
-     * Returns the 'primary' datetime for the event
206
-     *
207
-     * @param bool $try_to_exclude_expired
208
-     * @param bool $try_to_exclude_deleted
209
-     * @return EE_Datetime
210
-     * @throws EE_Error
211
-     */
212
-    public function primary_datetime($try_to_exclude_expired = true, $try_to_exclude_deleted = true)
213
-    {
214
-        if (! empty($this->_Primary_Datetime)) {
215
-            return $this->_Primary_Datetime;
216
-        }
217
-        $this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)->get_primary_datetime_for_event(
218
-            $this->ID(),
219
-            $try_to_exclude_expired,
220
-            $try_to_exclude_deleted
221
-        );
222
-        return $this->_Primary_Datetime;
223
-    }
224
-
225
-
226
-    /**
227
-     * Gets all the tickets available for purchase of this event
228
-     *
229
-     * @param array $query_params like EEM_Base::get_all
230
-     * @return EE_Base_Class[]|EE_Ticket[]
231
-     * @throws EE_Error
232
-     */
233
-    public function tickets($query_params = array())
234
-    {
235
-        // first get all datetimes
236
-        $datetimes = $this->datetimes_ordered();
237
-        if (! $datetimes) {
238
-            return array();
239
-        }
240
-        $datetime_ids = array();
241
-        foreach ($datetimes as $datetime) {
242
-            $datetime_ids[] = $datetime->ID();
243
-        }
244
-        $where_params = array('Datetime.DTT_ID' => array('IN', $datetime_ids));
245
-        // if incoming $query_params has where conditions let's merge but not override existing.
246
-        if (is_array($query_params) && isset($query_params[0])) {
247
-            $where_params = array_merge($query_params[0], $where_params);
248
-            unset($query_params[0]);
249
-        }
250
-        // now add $where_params to $query_params
251
-        $query_params[0] = $where_params;
252
-        return EEM_Ticket::instance()->get_all($query_params);
253
-    }
254
-
255
-
256
-    /**
257
-     * get all unexpired untrashed tickets
258
-     *
259
-     * @return EE_Ticket[]
260
-     * @throws EE_Error
261
-     */
262
-    public function active_tickets()
263
-    {
264
-        return $this->tickets(
265
-            array(
266
-                array(
267
-                    'TKT_end_date' => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
268
-                    'TKT_deleted'  => false,
269
-                ),
270
-            )
271
-        );
272
-    }
273
-
274
-
275
-    /**
276
-     * @return bool
277
-     * @throws EE_Error
278
-     */
279
-    public function additional_limit()
280
-    {
281
-        return $this->get('EVT_additional_limit');
282
-    }
283
-
284
-
285
-    /**
286
-     * @return bool
287
-     * @throws EE_Error
288
-     */
289
-    public function allow_overflow()
290
-    {
291
-        return $this->get('EVT_allow_overflow');
292
-    }
293
-
294
-
295
-    /**
296
-     * @return bool
297
-     * @throws EE_Error
298
-     */
299
-    public function created()
300
-    {
301
-        return $this->get('EVT_created');
302
-    }
303
-
304
-
305
-    /**
306
-     * @return bool
307
-     * @throws EE_Error
308
-     */
309
-    public function description()
310
-    {
311
-        return $this->get('EVT_desc');
312
-    }
313
-
314
-
315
-    /**
316
-     * Runs do_shortcode and wpautop on the description
317
-     *
318
-     * @return string of html
319
-     * @throws EE_Error
320
-     */
321
-    public function description_filtered()
322
-    {
323
-        return $this->get_pretty('EVT_desc');
324
-    }
325
-
326
-
327
-    /**
328
-     * @return bool
329
-     * @throws EE_Error
330
-     */
331
-    public function display_description()
332
-    {
333
-        return $this->get('EVT_display_desc');
334
-    }
335
-
336
-
337
-    /**
338
-     * @return bool
339
-     * @throws EE_Error
340
-     */
341
-    public function display_ticket_selector()
342
-    {
343
-        return (bool) $this->get('EVT_display_ticket_selector');
344
-    }
345
-
346
-
347
-    /**
348
-     * @return bool
349
-     * @throws EE_Error
350
-     */
351
-    public function external_url()
352
-    {
353
-        return $this->get('EVT_external_URL');
354
-    }
355
-
356
-
357
-    /**
358
-     * @return bool
359
-     * @throws EE_Error
360
-     */
361
-    public function member_only()
362
-    {
363
-        return $this->get('EVT_member_only');
364
-    }
365
-
366
-
367
-    /**
368
-     * @return bool
369
-     * @throws EE_Error
370
-     */
371
-    public function phone()
372
-    {
373
-        return $this->get('EVT_phone');
374
-    }
375
-
376
-
377
-    /**
378
-     * @return bool
379
-     * @throws EE_Error
380
-     */
381
-    public function modified()
382
-    {
383
-        return $this->get('EVT_modified');
384
-    }
385
-
386
-
387
-    /**
388
-     * @return bool
389
-     * @throws EE_Error
390
-     */
391
-    public function name()
392
-    {
393
-        return $this->get('EVT_name');
394
-    }
395
-
396
-
397
-    /**
398
-     * @return bool
399
-     * @throws EE_Error
400
-     */
401
-    public function order()
402
-    {
403
-        return $this->get('EVT_order');
404
-    }
405
-
406
-
407
-    /**
408
-     * @return bool|string
409
-     * @throws EE_Error
410
-     */
411
-    public function default_registration_status()
412
-    {
413
-        $event_default_registration_status = $this->get('EVT_default_registration_status');
414
-        return ! empty($event_default_registration_status)
415
-            ? $event_default_registration_status
416
-            : EE_Registry::instance()->CFG->registration->default_STS_ID;
417
-    }
418
-
419
-
420
-    /**
421
-     * @param int  $num_words
422
-     * @param null $more
423
-     * @param bool $not_full_desc
424
-     * @return bool|string
425
-     * @throws EE_Error
426
-     */
427
-    public function short_description($num_words = 55, $more = null, $not_full_desc = false)
428
-    {
429
-        $short_desc = $this->get('EVT_short_desc');
430
-        if (! empty($short_desc) || $not_full_desc) {
431
-            return $short_desc;
432
-        }
433
-        $full_desc = $this->get('EVT_desc');
434
-        return wp_trim_words($full_desc, $num_words, $more);
435
-    }
436
-
437
-
438
-    /**
439
-     * @return bool
440
-     * @throws EE_Error
441
-     */
442
-    public function slug()
443
-    {
444
-        return $this->get('EVT_slug');
445
-    }
446
-
447
-
448
-    /**
449
-     * @return bool
450
-     * @throws EE_Error
451
-     */
452
-    public function timezone_string()
453
-    {
454
-        return $this->get('EVT_timezone_string');
455
-    }
456
-
457
-
458
-    /**
459
-     * @return bool
460
-     * @throws EE_Error
461
-     */
462
-    public function visible_on()
463
-    {
464
-        return $this->get('EVT_visible_on');
465
-    }
466
-
467
-
468
-    /**
469
-     * @return int
470
-     * @throws EE_Error
471
-     */
472
-    public function wp_user()
473
-    {
474
-        return $this->get('EVT_wp_user');
475
-    }
476
-
477
-
478
-    /**
479
-     * @return bool
480
-     * @throws EE_Error
481
-     */
482
-    public function donations()
483
-    {
484
-        return $this->get('EVT_donations');
485
-    }
486
-
487
-
488
-    /**
489
-     * @param $limit
490
-     * @throws EE_Error
491
-     */
492
-    public function set_additional_limit($limit)
493
-    {
494
-        $this->set('EVT_additional_limit', $limit);
495
-    }
496
-
497
-
498
-    /**
499
-     * @param $created
500
-     * @throws EE_Error
501
-     */
502
-    public function set_created($created)
503
-    {
504
-        $this->set('EVT_created', $created);
505
-    }
506
-
507
-
508
-    /**
509
-     * @param $desc
510
-     * @throws EE_Error
511
-     */
512
-    public function set_description($desc)
513
-    {
514
-        $this->set('EVT_desc', $desc);
515
-    }
516
-
517
-
518
-    /**
519
-     * @param $display_desc
520
-     * @throws EE_Error
521
-     */
522
-    public function set_display_description($display_desc)
523
-    {
524
-        $this->set('EVT_display_desc', $display_desc);
525
-    }
526
-
527
-
528
-    /**
529
-     * @param $display_ticket_selector
530
-     * @throws EE_Error
531
-     */
532
-    public function set_display_ticket_selector($display_ticket_selector)
533
-    {
534
-        $this->set('EVT_display_ticket_selector', $display_ticket_selector);
535
-    }
536
-
537
-
538
-    /**
539
-     * @param $external_url
540
-     * @throws EE_Error
541
-     */
542
-    public function set_external_url($external_url)
543
-    {
544
-        $this->set('EVT_external_URL', $external_url);
545
-    }
546
-
547
-
548
-    /**
549
-     * @param $member_only
550
-     * @throws EE_Error
551
-     */
552
-    public function set_member_only($member_only)
553
-    {
554
-        $this->set('EVT_member_only', $member_only);
555
-    }
556
-
557
-
558
-    /**
559
-     * @param $event_phone
560
-     * @throws EE_Error
561
-     */
562
-    public function set_event_phone($event_phone)
563
-    {
564
-        $this->set('EVT_phone', $event_phone);
565
-    }
566
-
567
-
568
-    /**
569
-     * @param $modified
570
-     * @throws EE_Error
571
-     */
572
-    public function set_modified($modified)
573
-    {
574
-        $this->set('EVT_modified', $modified);
575
-    }
576
-
577
-
578
-    /**
579
-     * @param $name
580
-     * @throws EE_Error
581
-     */
582
-    public function set_name($name)
583
-    {
584
-        $this->set('EVT_name', $name);
585
-    }
586
-
587
-
588
-    /**
589
-     * @param $order
590
-     * @throws EE_Error
591
-     */
592
-    public function set_order($order)
593
-    {
594
-        $this->set('EVT_order', $order);
595
-    }
596
-
597
-
598
-    /**
599
-     * @param $short_desc
600
-     * @throws EE_Error
601
-     */
602
-    public function set_short_description($short_desc)
603
-    {
604
-        $this->set('EVT_short_desc', $short_desc);
605
-    }
606
-
607
-
608
-    /**
609
-     * @param $slug
610
-     * @throws EE_Error
611
-     */
612
-    public function set_slug($slug)
613
-    {
614
-        $this->set('EVT_slug', $slug);
615
-    }
616
-
617
-
618
-    /**
619
-     * @param $timezone_string
620
-     * @throws EE_Error
621
-     */
622
-    public function set_timezone_string($timezone_string)
623
-    {
624
-        $this->set('EVT_timezone_string', $timezone_string);
625
-    }
626
-
627
-
628
-    /**
629
-     * @param $visible_on
630
-     * @throws EE_Error
631
-     */
632
-    public function set_visible_on($visible_on)
633
-    {
634
-        $this->set('EVT_visible_on', $visible_on);
635
-    }
636
-
637
-
638
-    /**
639
-     * @param $wp_user
640
-     * @throws EE_Error
641
-     */
642
-    public function set_wp_user($wp_user)
643
-    {
644
-        $this->set('EVT_wp_user', $wp_user);
645
-    }
646
-
647
-
648
-    /**
649
-     * @param $default_registration_status
650
-     * @throws EE_Error
651
-     */
652
-    public function set_default_registration_status($default_registration_status)
653
-    {
654
-        $this->set('EVT_default_registration_status', $default_registration_status);
655
-    }
656
-
657
-
658
-    /**
659
-     * @param $donations
660
-     * @throws EE_Error
661
-     */
662
-    public function set_donations($donations)
663
-    {
664
-        $this->set('EVT_donations', $donations);
665
-    }
666
-
667
-
668
-    /**
669
-     * Adds a venue to this event
670
-     *
671
-     * @param EE_Venue /int $venue_id_or_obj
672
-     * @return EE_Base_Class|EE_Venue
673
-     * @throws EE_Error
674
-     */
675
-    public function add_venue($venue_id_or_obj)
676
-    {
677
-        return $this->_add_relation_to($venue_id_or_obj, 'Venue');
678
-    }
679
-
680
-
681
-    /**
682
-     * Removes a venue from the event
683
-     *
684
-     * @param EE_Venue /int $venue_id_or_obj
685
-     * @return EE_Base_Class|EE_Venue
686
-     * @throws EE_Error
687
-     */
688
-    public function remove_venue($venue_id_or_obj)
689
-    {
690
-        return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
691
-    }
692
-
693
-
694
-    /**
695
-     * Gets all the venues related ot the event. May provide additional $query_params if desired
696
-     *
697
-     * @param array $query_params like EEM_Base::get_all's $query_params
698
-     * @return EE_Base_Class[]|EE_Venue[]
699
-     * @throws EE_Error
700
-     */
701
-    public function venues($query_params = array())
702
-    {
703
-        return $this->get_many_related('Venue', $query_params);
704
-    }
705
-
706
-
707
-    /**
708
-     * check if event id is present and if event is published
709
-     *
710
-     * @access public
711
-     * @return boolean true yes, false no
712
-     * @throws EE_Error
713
-     */
714
-    private function _has_ID_and_is_published()
715
-    {
716
-        // first check if event id is present and not NULL,
717
-        // then check if this event is published (or any of the equivalent "published" statuses)
718
-        return
719
-            $this->ID() && $this->ID() !== null
720
-            && (
721
-                $this->status() === 'publish'
722
-                || $this->status() === EEM_Event::sold_out
723
-                || $this->status() === EEM_Event::postponed
724
-                || $this->status() === EEM_Event::cancelled
725
-            );
726
-    }
727
-
728
-
729
-    /**
730
-     * This simply compares the internal dates with NOW and determines if the event is upcoming or not.
731
-     *
732
-     * @access public
733
-     * @return boolean true yes, false no
734
-     * @throws EE_Error
735
-     */
736
-    public function is_upcoming()
737
-    {
738
-        // check if event id is present and if this event is published
739
-        if ($this->is_inactive()) {
740
-            return false;
741
-        }
742
-        // set initial value
743
-        $upcoming = false;
744
-        // next let's get all datetimes and loop through them
745
-        $datetimes = $this->datetimes_in_chronological_order();
746
-        foreach ($datetimes as $datetime) {
747
-            if ($datetime instanceof EE_Datetime) {
748
-                // if this dtt is expired then we continue cause one of the other datetimes might be upcoming.
749
-                if ($datetime->is_expired()) {
750
-                    continue;
751
-                }
752
-                // if this dtt is active then we return false.
753
-                if ($datetime->is_active()) {
754
-                    return false;
755
-                }
756
-                // otherwise let's check upcoming status
757
-                $upcoming = $datetime->is_upcoming();
758
-            }
759
-        }
760
-        return $upcoming;
761
-    }
762
-
763
-
764
-    /**
765
-     * @return bool
766
-     * @throws EE_Error
767
-     */
768
-    public function is_active()
769
-    {
770
-        // check if event id is present and if this event is published
771
-        if ($this->is_inactive()) {
772
-            return false;
773
-        }
774
-        // set initial value
775
-        $active = false;
776
-        // next let's get all datetimes and loop through them
777
-        $datetimes = $this->datetimes_in_chronological_order();
778
-        foreach ($datetimes as $datetime) {
779
-            if ($datetime instanceof EE_Datetime) {
780
-                // if this dtt is expired then we continue cause one of the other datetimes might be active.
781
-                if ($datetime->is_expired()) {
782
-                    continue;
783
-                }
784
-                // if this dtt is upcoming then we return false.
785
-                if ($datetime->is_upcoming()) {
786
-                    return false;
787
-                }
788
-                // otherwise let's check active status
789
-                $active = $datetime->is_active();
790
-            }
791
-        }
792
-        return $active;
793
-    }
794
-
795
-
796
-    /**
797
-     * @return bool
798
-     * @throws EE_Error
799
-     */
800
-    public function is_expired()
801
-    {
802
-        // check if event id is present and if this event is published
803
-        if ($this->is_inactive()) {
804
-            return false;
805
-        }
806
-        // set initial value
807
-        $expired = false;
808
-        // first let's get all datetimes and loop through them
809
-        $datetimes = $this->datetimes_in_chronological_order();
810
-        foreach ($datetimes as $datetime) {
811
-            if ($datetime instanceof EE_Datetime) {
812
-                // if this dtt is upcoming or active then we return false.
813
-                if ($datetime->is_upcoming() || $datetime->is_active()) {
814
-                    return false;
815
-                }
816
-                // otherwise let's check active status
817
-                $expired = $datetime->is_expired();
818
-            }
819
-        }
820
-        return $expired;
821
-    }
822
-
823
-
824
-    /**
825
-     * @return bool
826
-     * @throws EE_Error
827
-     */
828
-    public function is_inactive()
829
-    {
830
-        // check if event id is present and if this event is published
831
-        if ($this->_has_ID_and_is_published()) {
832
-            return false;
833
-        }
834
-        return true;
835
-    }
836
-
837
-
838
-    /**
839
-     * calculate spaces remaining based on "saleable" tickets
840
-     *
841
-     * @param array $tickets
842
-     * @param bool  $filtered
843
-     * @return int|float
844
-     * @throws EE_Error
845
-     * @throws DomainException
846
-     * @throws UnexpectedEntityException
847
-     */
848
-    public function spaces_remaining($tickets = array(), $filtered = true)
849
-    {
850
-        $this->getAvailableSpacesCalculator()->setActiveTickets($tickets);
851
-        $spaces_remaining = $this->getAvailableSpacesCalculator()->spacesRemaining();
852
-        return $filtered
853
-            ? apply_filters(
854
-                'FHEE_EE_Event__spaces_remaining',
855
-                $spaces_remaining,
856
-                $this,
857
-                $tickets
858
-            )
859
-            : $spaces_remaining;
860
-    }
861
-
862
-
863
-    /**
864
-     *    perform_sold_out_status_check
865
-     *    checks all of this events's datetime  reg_limit - sold values to determine if ANY datetimes have spaces
866
-     *    available... if NOT, then the event status will get toggled to 'sold_out'
867
-     *
868
-     * @return bool    return the ACTUAL sold out state.
869
-     * @throws EE_Error
870
-     * @throws DomainException
871
-     * @throws UnexpectedEntityException
872
-     */
873
-    public function perform_sold_out_status_check()
874
-    {
875
-        // get all unexpired untrashed tickets
876
-        $tickets = $this->tickets(
877
-            array(
878
-                array('TKT_deleted' => false),
879
-                'order_by' => array('TKT_qty' => 'ASC'),
880
-            )
881
-        );
882
-        $all_expired = true;
883
-        foreach ($tickets as $ticket) {
884
-            if (! $ticket->is_expired()) {
885
-                $all_expired = false;
886
-                break;
887
-            }
888
-        }
889
-        // if all the tickets are just expired, then don't update the event status to sold out
890
-        if ($all_expired) {
891
-            return true;
892
-        }
893
-        $spaces_remaining = $this->spaces_remaining($tickets);
894
-        if ($spaces_remaining < 1) {
895
-            $this->set_status(EEM_Event::sold_out);
896
-            $this->save();
897
-            $sold_out = true;
898
-        } else {
899
-            $sold_out = false;
900
-            // was event previously marked as sold out ?
901
-            if ($this->status() === EEM_Event::sold_out) {
902
-                // revert status to previous value, if it was set
903
-                $previous_event_status = $this->get_post_meta('_previous_event_status', true);
904
-                if ($previous_event_status) {
905
-                    $this->set_status($previous_event_status);
906
-                    $this->save();
907
-                }
908
-            }
909
-        }
910
-        do_action('AHEE__EE_Event__perform_sold_out_status_check__end', $this, $sold_out, $spaces_remaining, $tickets);
911
-        return $sold_out;
912
-    }
913
-
914
-
915
-    /**
916
-     * This returns the total remaining spaces for sale on this event.
917
-     *
918
-     * @uses EE_Event::total_available_spaces()
919
-     * @return float|int
920
-     * @throws EE_Error
921
-     * @throws DomainException
922
-     * @throws UnexpectedEntityException
923
-     */
924
-    public function spaces_remaining_for_sale()
925
-    {
926
-        return $this->total_available_spaces(true);
927
-    }
928
-
929
-
930
-    /**
931
-     * This returns the total spaces available for an event
932
-     * while considering all the qtys on the tickets and the reg limits
933
-     * on the datetimes attached to this event.
934
-     *
935
-     * @param   bool $consider_sold Whether to consider any tickets that have already sold in our calculation.
936
-     *                              If this is false, then we return the most tickets that could ever be sold
937
-     *                              for this event with the datetime and tickets setup on the event under optimal
938
-     *                              selling conditions.  Otherwise we return a live calculation of spaces available
939
-     *                              based on tickets sold.  Depending on setup and stage of sales, this
940
-     *                              may appear to equal remaining tickets.  However, the more tickets are
941
-     *                              sold out, the more accurate the "live" total is.
942
-     * @return float|int
943
-     * @throws EE_Error
944
-     * @throws DomainException
945
-     * @throws UnexpectedEntityException
946
-     */
947
-    public function total_available_spaces($consider_sold = false)
948
-    {
949
-        $spaces_available = $consider_sold
950
-            ? $this->getAvailableSpacesCalculator()->spacesRemaining()
951
-            : $this->getAvailableSpacesCalculator()->totalSpacesAvailable();
952
-        return apply_filters(
953
-            'FHEE_EE_Event__total_available_spaces__spaces_available',
954
-            $spaces_available,
955
-            $this,
956
-            $this->getAvailableSpacesCalculator()->getDatetimes(),
957
-            $this->getAvailableSpacesCalculator()->getActiveTickets()
958
-        );
959
-    }
960
-
961
-
962
-    /**
963
-     * Checks if the event is set to sold out
964
-     *
965
-     * @param  bool $actual whether or not to perform calculations to not only figure the
966
-     *                      actual status but also to flip the status if necessary to sold
967
-     *                      out If false, we just check the existing status of the event
968
-     * @return boolean
969
-     * @throws EE_Error
970
-     */
971
-    public function is_sold_out($actual = false)
972
-    {
973
-        if (! $actual) {
974
-            return $this->status() === EEM_Event::sold_out;
975
-        }
976
-        return $this->perform_sold_out_status_check();
977
-    }
978
-
979
-
980
-    /**
981
-     * Checks if the event is marked as postponed
982
-     *
983
-     * @return boolean
984
-     */
985
-    public function is_postponed()
986
-    {
987
-        return $this->status() === EEM_Event::postponed;
988
-    }
989
-
990
-
991
-    /**
992
-     * Checks if the event is marked as cancelled
993
-     *
994
-     * @return boolean
995
-     */
996
-    public function is_cancelled()
997
-    {
998
-        return $this->status() === EEM_Event::cancelled;
999
-    }
1000
-
1001
-
1002
-    /**
1003
-     * Get the logical active status in a hierarchical order for all the datetimes.  Note
1004
-     * Basically, we order the datetimes by EVT_start_date.  Then first test on whether the event is published.  If its
1005
-     * NOT published then we test for whether its expired or not.  IF it IS published then we test first on whether an
1006
-     * event has any active dates.  If no active dates then we check for any upcoming dates.  If no upcoming dates then
1007
-     * the event is considered expired.
1008
-     * NOTE: this method does NOT calculate whether the datetimes are sold out when event is published.  Sold Out is a
1009
-     * status set on the EVENT when it is not published and thus is done
1010
-     *
1011
-     * @param bool $reset
1012
-     * @return bool | string - based on EE_Datetime active constants or FALSE if error.
1013
-     * @throws EE_Error
1014
-     */
1015
-    public function get_active_status($reset = false)
1016
-    {
1017
-        // if the active status has already been set, then just use that value (unless we are resetting it)
1018
-        if (! empty($this->_active_status) && ! $reset) {
1019
-            return $this->_active_status;
1020
-        }
1021
-        // first check if event id is present on this object
1022
-        if (! $this->ID()) {
1023
-            return false;
1024
-        }
1025
-        $where_params_for_event = array(array('EVT_ID' => $this->ID()));
1026
-        // if event is published:
1027
-        if ($this->status() === 'publish') {
1028
-            // active?
1029
-            if (EEM_Datetime::instance()->get_datetime_count_for_status(
1030
-                EE_Datetime::active,
1031
-                $where_params_for_event
1032
-            ) > 0) {
1033
-                $this->_active_status = EE_Datetime::active;
1034
-            } else {
1035
-                // upcoming?
1036
-                if (EEM_Datetime::instance()->get_datetime_count_for_status(
1037
-                    EE_Datetime::upcoming,
1038
-                    $where_params_for_event
1039
-                ) > 0) {
1040
-                    $this->_active_status = EE_Datetime::upcoming;
1041
-                } else {
1042
-                    // expired?
1043
-                    if (EEM_Datetime::instance()->get_datetime_count_for_status(
1044
-                        EE_Datetime::expired,
1045
-                        $where_params_for_event
1046
-                    ) > 0
1047
-                    ) {
1048
-                        $this->_active_status = EE_Datetime::expired;
1049
-                    } else {
1050
-                        // it would be odd if things make it this far because it basically means there are no datetime's
1051
-                        // attached to the event.  So in this case it will just be considered inactive.
1052
-                        $this->_active_status = EE_Datetime::inactive;
1053
-                    }
1054
-                }
1055
-            }
1056
-        } else {
1057
-            // the event is not published, so let's just set it's active status according to its' post status
1058
-            switch ($this->status()) {
1059
-                case EEM_Event::sold_out:
1060
-                    $this->_active_status = EE_Datetime::sold_out;
1061
-                    break;
1062
-                case EEM_Event::cancelled:
1063
-                    $this->_active_status = EE_Datetime::cancelled;
1064
-                    break;
1065
-                case EEM_Event::postponed:
1066
-                    $this->_active_status = EE_Datetime::postponed;
1067
-                    break;
1068
-                default:
1069
-                    $this->_active_status = EE_Datetime::inactive;
1070
-            }
1071
-        }
1072
-        return $this->_active_status;
1073
-    }
1074
-
1075
-
1076
-    /**
1077
-     *    pretty_active_status
1078
-     *
1079
-     * @access public
1080
-     * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1081
-     * @return mixed void|string
1082
-     * @throws EE_Error
1083
-     */
1084
-    public function pretty_active_status($echo = true)
1085
-    {
1086
-        $active_status = $this->get_active_status();
1087
-        $status = '<span class="ee-status event-active-status-'
1088
-                  . $active_status
1089
-                  . '">'
1090
-                  . EEH_Template::pretty_status($active_status, false, 'sentence')
1091
-                  . '</span>';
1092
-        if ($echo) {
1093
-            echo $status;
1094
-            return '';
1095
-        }
1096
-        return $status;
1097
-    }
1098
-
1099
-
1100
-    /**
1101
-     * @return bool|int
1102
-     * @throws EE_Error
1103
-     */
1104
-    public function get_number_of_tickets_sold()
1105
-    {
1106
-        $tkt_sold = 0;
1107
-        if (! $this->ID()) {
1108
-            return 0;
1109
-        }
1110
-        $datetimes = $this->datetimes();
1111
-        foreach ($datetimes as $datetime) {
1112
-            if ($datetime instanceof EE_Datetime) {
1113
-                $tkt_sold += $datetime->sold();
1114
-            }
1115
-        }
1116
-        return $tkt_sold;
1117
-    }
1118
-
1119
-
1120
-    /**
1121
-     * This just returns a count of all the registrations for this event
1122
-     *
1123
-     * @access  public
1124
-     * @return int
1125
-     * @throws EE_Error
1126
-     */
1127
-    public function get_count_of_all_registrations()
1128
-    {
1129
-        return EEM_Event::instance()->count_related($this, 'Registration');
1130
-    }
1131
-
1132
-
1133
-    /**
1134
-     * This returns the ticket with the earliest start time that is
1135
-     * available for this event (across all datetimes attached to the event)
1136
-     *
1137
-     * @return EE_Base_Class|EE_Ticket|null
1138
-     * @throws EE_Error
1139
-     */
1140
-    public function get_ticket_with_earliest_start_time()
1141
-    {
1142
-        $where['Datetime.EVT_ID'] = $this->ID();
1143
-        $query_params = array($where, 'order_by' => array('TKT_start_date' => 'ASC'));
1144
-        return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1145
-    }
1146
-
1147
-
1148
-    /**
1149
-     * This returns the ticket with the latest end time that is available
1150
-     * for this event (across all datetimes attached to the event)
1151
-     *
1152
-     * @return EE_Base_Class|EE_Ticket|null
1153
-     * @throws EE_Error
1154
-     */
1155
-    public function get_ticket_with_latest_end_time()
1156
-    {
1157
-        $where['Datetime.EVT_ID'] = $this->ID();
1158
-        $query_params = array($where, 'order_by' => array('TKT_end_date' => 'DESC'));
1159
-        return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1160
-    }
1161
-
1162
-
1163
-    /**
1164
-     * This returns whether there are any tickets on sale for this event.
1165
-     *
1166
-     * @return bool true = YES tickets on sale.
1167
-     * @throws EE_Error
1168
-     */
1169
-    public function tickets_on_sale()
1170
-    {
1171
-        $earliest_ticket = $this->get_ticket_with_earliest_start_time();
1172
-        $latest_ticket = $this->get_ticket_with_latest_end_time();
1173
-        if (! $latest_ticket instanceof EE_Ticket && ! $earliest_ticket instanceof EE_Ticket) {
1174
-            return false;
1175
-        }
1176
-        // check on sale for these two tickets.
1177
-        if ($latest_ticket->is_on_sale() || $earliest_ticket->is_on_sale()) {
1178
-            return true;
1179
-        }
1180
-        return false;
1181
-    }
1182
-
1183
-
1184
-    /**
1185
-     * Gets the URL for viewing this event on the front-end. Overrides parent
1186
-     * to check for an external URL first
1187
-     *
1188
-     * @return string
1189
-     * @throws EE_Error
1190
-     */
1191
-    public function get_permalink()
1192
-    {
1193
-        if ($this->external_url()) {
1194
-            return $this->external_url();
1195
-        }
1196
-        return parent::get_permalink();
1197
-    }
1198
-
1199
-
1200
-    /**
1201
-     * Gets the first term for 'espresso_event_categories' we can find
1202
-     *
1203
-     * @param array $query_params like EEM_Base::get_all
1204
-     * @return EE_Base_Class|EE_Term|null
1205
-     * @throws EE_Error
1206
-     */
1207
-    public function first_event_category($query_params = array())
1208
-    {
1209
-        $query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1210
-        $query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1211
-        return EEM_Term::instance()->get_one($query_params);
1212
-    }
1213
-
1214
-
1215
-    /**
1216
-     * Gets all terms for 'espresso_event_categories' we can find
1217
-     *
1218
-     * @param array $query_params
1219
-     * @return EE_Base_Class[]|EE_Term[]
1220
-     * @throws EE_Error
1221
-     */
1222
-    public function get_all_event_categories($query_params = array())
1223
-    {
1224
-        $query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1225
-        $query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1226
-        return EEM_Term::instance()->get_all($query_params);
1227
-    }
1228
-
1229
-
1230
-    /**
1231
-     * Adds a question group to this event
1232
-     *
1233
-     * @param EE_Question_Group|int $question_group_id_or_obj
1234
-     * @param bool                  $for_primary if true, the question group will be added for the primary
1235
-     *                                           registrant, if false will be added for others. default: false
1236
-     * @return EE_Base_Class|EE_Question_Group
1237
-     * @throws EE_Error
1238
-     */
1239
-    public function add_question_group($question_group_id_or_obj, $for_primary = false)
1240
-    {
1241
-        $extra = $for_primary
1242
-            ? array('EQG_primary' => 1)
1243
-            : array();
1244
-        return $this->_add_relation_to($question_group_id_or_obj, 'Question_Group', $extra);
1245
-    }
1246
-
1247
-
1248
-    /**
1249
-     * Removes a question group from the event
1250
-     *
1251
-     * @param EE_Question_Group|int $question_group_id_or_obj
1252
-     * @param bool                  $for_primary if true, the question group will be removed from the primary
1253
-     *                                           registrant, if false will be removed from others. default: false
1254
-     * @return EE_Base_Class|EE_Question_Group
1255
-     * @throws EE_Error
1256
-     */
1257
-    public function remove_question_group($question_group_id_or_obj, $for_primary = false)
1258
-    {
1259
-        $where = $for_primary
1260
-            ? array('EQG_primary' => 1)
1261
-            : array();
1262
-        return $this->_remove_relation_to($question_group_id_or_obj, 'Question_Group', $where);
1263
-    }
1264
-
1265
-
1266
-    /**
1267
-     * Gets all the question groups, ordering them by QSG_order ascending
1268
-     *
1269
-     * @param array $query_params @see EEM_Base::get_all
1270
-     * @return EE_Base_Class[]|EE_Question_Group[]
1271
-     * @throws EE_Error
1272
-     */
1273
-    public function question_groups($query_params = array())
1274
-    {
1275
-        $query_params = ! empty($query_params) ? $query_params : array('order_by' => array('QSG_order' => 'ASC'));
1276
-        return $this->get_many_related('Question_Group', $query_params);
1277
-    }
1278
-
1279
-
1280
-    /**
1281
-     * Implementation for EEI_Has_Icon interface method.
1282
-     *
1283
-     * @see EEI_Visual_Representation for comments
1284
-     * @return string
1285
-     */
1286
-    public function get_icon()
1287
-    {
1288
-        return '<span class="dashicons dashicons-flag"></span>';
1289
-    }
1290
-
1291
-
1292
-    /**
1293
-     * Implementation for EEI_Admin_Links interface method.
1294
-     *
1295
-     * @see EEI_Admin_Links for comments
1296
-     * @return string
1297
-     * @throws EE_Error
1298
-     */
1299
-    public function get_admin_details_link()
1300
-    {
1301
-        return $this->get_admin_edit_link();
1302
-    }
1303
-
1304
-
1305
-    /**
1306
-     * Implementation for EEI_Admin_Links interface method.
1307
-     *
1308
-     * @see EEI_Admin_Links for comments
1309
-     * @return string
1310
-     * @throws EE_Error
1311
-     */
1312
-    public function get_admin_edit_link()
1313
-    {
1314
-        return EEH_URL::add_query_args_and_nonce(
1315
-            array(
1316
-                'page'   => 'espresso_events',
1317
-                'action' => 'edit',
1318
-                'post'   => $this->ID(),
1319
-            ),
1320
-            admin_url('admin.php')
1321
-        );
1322
-    }
1323
-
1324
-
1325
-    /**
1326
-     * Implementation for EEI_Admin_Links interface method.
1327
-     *
1328
-     * @see EEI_Admin_Links for comments
1329
-     * @return string
1330
-     */
1331
-    public function get_admin_settings_link()
1332
-    {
1333
-        return EEH_URL::add_query_args_and_nonce(
1334
-            array(
1335
-                'page'   => 'espresso_events',
1336
-                'action' => 'default_event_settings',
1337
-            ),
1338
-            admin_url('admin.php')
1339
-        );
1340
-    }
1341
-
1342
-
1343
-    /**
1344
-     * Implementation for EEI_Admin_Links interface method.
1345
-     *
1346
-     * @see EEI_Admin_Links for comments
1347
-     * @return string
1348
-     */
1349
-    public function get_admin_overview_link()
1350
-    {
1351
-        return EEH_URL::add_query_args_and_nonce(
1352
-            array(
1353
-                'page'   => 'espresso_events',
1354
-                'action' => 'default',
1355
-            ),
1356
-            admin_url('admin.php')
1357
-        );
1358
-    }
16
+	/**
17
+	 * cached value for the the logical active status for the event
18
+	 *
19
+	 * @see get_active_status()
20
+	 * @var string
21
+	 */
22
+	protected $_active_status = '';
23
+
24
+	/**
25
+	 * This is just used for caching the Primary Datetime for the Event on initial retrieval
26
+	 *
27
+	 * @var EE_Datetime
28
+	 */
29
+	protected $_Primary_Datetime;
30
+
31
+	/**
32
+	 * @var EventSpacesCalculator $available_spaces_calculator
33
+	 */
34
+	protected $available_spaces_calculator;
35
+
36
+
37
+	/**
38
+	 * @param array  $props_n_values          incoming values
39
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
40
+	 *                                        used.)
41
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
42
+	 *                                        date_format and the second value is the time format
43
+	 * @return EE_Event
44
+	 * @throws EE_Error
45
+	 */
46
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
47
+	{
48
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
49
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
50
+	}
51
+
52
+
53
+	/**
54
+	 * @param array  $props_n_values  incoming values from the database
55
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
56
+	 *                                the website will be used.
57
+	 * @return EE_Event
58
+	 * @throws EE_Error
59
+	 */
60
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
61
+	{
62
+		return new self($props_n_values, true, $timezone);
63
+	}
64
+
65
+
66
+	/**
67
+	 * @return EventSpacesCalculator
68
+	 * @throws \EE_Error
69
+	 */
70
+	public function getAvailableSpacesCalculator()
71
+	{
72
+		if (! $this->available_spaces_calculator instanceof EventSpacesCalculator) {
73
+			$this->available_spaces_calculator = new EventSpacesCalculator($this);
74
+		}
75
+		return $this->available_spaces_calculator;
76
+	}
77
+
78
+
79
+	/**
80
+	 * Overrides parent set() method so that all calls to set( 'status', $status ) can be routed to internal methods
81
+	 *
82
+	 * @param string $field_name
83
+	 * @param mixed  $field_value
84
+	 * @param bool   $use_default
85
+	 * @throws EE_Error
86
+	 */
87
+	public function set($field_name, $field_value, $use_default = false)
88
+	{
89
+		switch ($field_name) {
90
+			case 'status':
91
+				$this->set_status($field_value, $use_default);
92
+				break;
93
+			default:
94
+				parent::set($field_name, $field_value, $use_default);
95
+		}
96
+	}
97
+
98
+
99
+	/**
100
+	 *    set_status
101
+	 * Checks if event status is being changed to SOLD OUT
102
+	 * and updates event meta data with previous event status
103
+	 * so that we can revert things if/when the event is no longer sold out
104
+	 *
105
+	 * @access public
106
+	 * @param string $new_status
107
+	 * @param bool   $use_default
108
+	 * @return void
109
+	 * @throws EE_Error
110
+	 */
111
+	public function set_status($new_status = null, $use_default = false)
112
+	{
113
+		// if nothing is set, and we aren't explicitly wanting to reset the status, then just leave
114
+		if (empty($new_status) && ! $use_default) {
115
+			return;
116
+		}
117
+		// get current Event status
118
+		$old_status = $this->status();
119
+		// if status has changed
120
+		if ($old_status !== $new_status) {
121
+			// TO sold_out
122
+			if ($new_status === EEM_Event::sold_out) {
123
+				// save the previous event status so that we can revert if the event is no longer sold out
124
+				$this->add_post_meta('_previous_event_status', $old_status);
125
+				do_action('AHEE__EE_Event__set_status__to_sold_out', $this, $old_status, $new_status);
126
+				// OR FROM  sold_out
127
+			} elseif ($old_status === EEM_Event::sold_out) {
128
+				$this->delete_post_meta('_previous_event_status');
129
+				do_action('AHEE__EE_Event__set_status__from_sold_out', $this, $old_status, $new_status);
130
+			}
131
+			// clear out the active status so that it gets reset the next time it is requested
132
+			$this->_active_status = null;
133
+			// update status
134
+			parent::set('status', $new_status, $use_default);
135
+			do_action('AHEE__EE_Event__set_status__after_update', $this);
136
+			return;
137
+		}
138
+		// even though the old value matches the new value, it's still good to
139
+		// allow the parent set method to have a say
140
+		parent::set('status', $new_status, $use_default);
141
+	}
142
+
143
+
144
+	/**
145
+	 * Gets all the datetimes for this event
146
+	 *
147
+	 * @param array $query_params like EEM_Base::get_all
148
+	 * @return EE_Base_Class[]|EE_Datetime[]
149
+	 * @throws EE_Error
150
+	 */
151
+	public function datetimes($query_params = array())
152
+	{
153
+		return $this->get_many_related('Datetime', $query_params);
154
+	}
155
+
156
+
157
+	/**
158
+	 * Gets all the datetimes for this event, ordered by DTT_EVT_start in ascending order
159
+	 *
160
+	 * @return EE_Base_Class[]|EE_Datetime[]
161
+	 * @throws EE_Error
162
+	 */
163
+	public function datetimes_in_chronological_order()
164
+	{
165
+		return $this->get_many_related('Datetime', array('order_by' => array('DTT_EVT_start' => 'ASC')));
166
+	}
167
+
168
+
169
+	/**
170
+	 * Gets all the datetimes for this event, ordered by the DTT_order on the datetime.
171
+	 * @darren, we should probably UNSET timezone on the EEM_Datetime model
172
+	 * after running our query, so that this timezone isn't set for EVERY query
173
+	 * on EEM_Datetime for the rest of the request, no?
174
+	 *
175
+	 * @param boolean $show_expired whether or not to include expired events
176
+	 * @param boolean $show_deleted whether or not to include deleted events
177
+	 * @param null    $limit
178
+	 * @return EE_Datetime[]
179
+	 * @throws EE_Error
180
+	 */
181
+	public function datetimes_ordered($show_expired = true, $show_deleted = false, $limit = null)
182
+	{
183
+		return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_event_ordered_by_DTT_order(
184
+			$this->ID(),
185
+			$show_expired,
186
+			$show_deleted,
187
+			$limit
188
+		);
189
+	}
190
+
191
+
192
+	/**
193
+	 * Returns one related datetime. Mostly only used by some legacy code.
194
+	 *
195
+	 * @return EE_Base_Class|EE_Datetime
196
+	 * @throws EE_Error
197
+	 */
198
+	public function first_datetime()
199
+	{
200
+		return $this->get_first_related('Datetime');
201
+	}
202
+
203
+
204
+	/**
205
+	 * Returns the 'primary' datetime for the event
206
+	 *
207
+	 * @param bool $try_to_exclude_expired
208
+	 * @param bool $try_to_exclude_deleted
209
+	 * @return EE_Datetime
210
+	 * @throws EE_Error
211
+	 */
212
+	public function primary_datetime($try_to_exclude_expired = true, $try_to_exclude_deleted = true)
213
+	{
214
+		if (! empty($this->_Primary_Datetime)) {
215
+			return $this->_Primary_Datetime;
216
+		}
217
+		$this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)->get_primary_datetime_for_event(
218
+			$this->ID(),
219
+			$try_to_exclude_expired,
220
+			$try_to_exclude_deleted
221
+		);
222
+		return $this->_Primary_Datetime;
223
+	}
224
+
225
+
226
+	/**
227
+	 * Gets all the tickets available for purchase of this event
228
+	 *
229
+	 * @param array $query_params like EEM_Base::get_all
230
+	 * @return EE_Base_Class[]|EE_Ticket[]
231
+	 * @throws EE_Error
232
+	 */
233
+	public function tickets($query_params = array())
234
+	{
235
+		// first get all datetimes
236
+		$datetimes = $this->datetimes_ordered();
237
+		if (! $datetimes) {
238
+			return array();
239
+		}
240
+		$datetime_ids = array();
241
+		foreach ($datetimes as $datetime) {
242
+			$datetime_ids[] = $datetime->ID();
243
+		}
244
+		$where_params = array('Datetime.DTT_ID' => array('IN', $datetime_ids));
245
+		// if incoming $query_params has where conditions let's merge but not override existing.
246
+		if (is_array($query_params) && isset($query_params[0])) {
247
+			$where_params = array_merge($query_params[0], $where_params);
248
+			unset($query_params[0]);
249
+		}
250
+		// now add $where_params to $query_params
251
+		$query_params[0] = $where_params;
252
+		return EEM_Ticket::instance()->get_all($query_params);
253
+	}
254
+
255
+
256
+	/**
257
+	 * get all unexpired untrashed tickets
258
+	 *
259
+	 * @return EE_Ticket[]
260
+	 * @throws EE_Error
261
+	 */
262
+	public function active_tickets()
263
+	{
264
+		return $this->tickets(
265
+			array(
266
+				array(
267
+					'TKT_end_date' => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
268
+					'TKT_deleted'  => false,
269
+				),
270
+			)
271
+		);
272
+	}
273
+
274
+
275
+	/**
276
+	 * @return bool
277
+	 * @throws EE_Error
278
+	 */
279
+	public function additional_limit()
280
+	{
281
+		return $this->get('EVT_additional_limit');
282
+	}
283
+
284
+
285
+	/**
286
+	 * @return bool
287
+	 * @throws EE_Error
288
+	 */
289
+	public function allow_overflow()
290
+	{
291
+		return $this->get('EVT_allow_overflow');
292
+	}
293
+
294
+
295
+	/**
296
+	 * @return bool
297
+	 * @throws EE_Error
298
+	 */
299
+	public function created()
300
+	{
301
+		return $this->get('EVT_created');
302
+	}
303
+
304
+
305
+	/**
306
+	 * @return bool
307
+	 * @throws EE_Error
308
+	 */
309
+	public function description()
310
+	{
311
+		return $this->get('EVT_desc');
312
+	}
313
+
314
+
315
+	/**
316
+	 * Runs do_shortcode and wpautop on the description
317
+	 *
318
+	 * @return string of html
319
+	 * @throws EE_Error
320
+	 */
321
+	public function description_filtered()
322
+	{
323
+		return $this->get_pretty('EVT_desc');
324
+	}
325
+
326
+
327
+	/**
328
+	 * @return bool
329
+	 * @throws EE_Error
330
+	 */
331
+	public function display_description()
332
+	{
333
+		return $this->get('EVT_display_desc');
334
+	}
335
+
336
+
337
+	/**
338
+	 * @return bool
339
+	 * @throws EE_Error
340
+	 */
341
+	public function display_ticket_selector()
342
+	{
343
+		return (bool) $this->get('EVT_display_ticket_selector');
344
+	}
345
+
346
+
347
+	/**
348
+	 * @return bool
349
+	 * @throws EE_Error
350
+	 */
351
+	public function external_url()
352
+	{
353
+		return $this->get('EVT_external_URL');
354
+	}
355
+
356
+
357
+	/**
358
+	 * @return bool
359
+	 * @throws EE_Error
360
+	 */
361
+	public function member_only()
362
+	{
363
+		return $this->get('EVT_member_only');
364
+	}
365
+
366
+
367
+	/**
368
+	 * @return bool
369
+	 * @throws EE_Error
370
+	 */
371
+	public function phone()
372
+	{
373
+		return $this->get('EVT_phone');
374
+	}
375
+
376
+
377
+	/**
378
+	 * @return bool
379
+	 * @throws EE_Error
380
+	 */
381
+	public function modified()
382
+	{
383
+		return $this->get('EVT_modified');
384
+	}
385
+
386
+
387
+	/**
388
+	 * @return bool
389
+	 * @throws EE_Error
390
+	 */
391
+	public function name()
392
+	{
393
+		return $this->get('EVT_name');
394
+	}
395
+
396
+
397
+	/**
398
+	 * @return bool
399
+	 * @throws EE_Error
400
+	 */
401
+	public function order()
402
+	{
403
+		return $this->get('EVT_order');
404
+	}
405
+
406
+
407
+	/**
408
+	 * @return bool|string
409
+	 * @throws EE_Error
410
+	 */
411
+	public function default_registration_status()
412
+	{
413
+		$event_default_registration_status = $this->get('EVT_default_registration_status');
414
+		return ! empty($event_default_registration_status)
415
+			? $event_default_registration_status
416
+			: EE_Registry::instance()->CFG->registration->default_STS_ID;
417
+	}
418
+
419
+
420
+	/**
421
+	 * @param int  $num_words
422
+	 * @param null $more
423
+	 * @param bool $not_full_desc
424
+	 * @return bool|string
425
+	 * @throws EE_Error
426
+	 */
427
+	public function short_description($num_words = 55, $more = null, $not_full_desc = false)
428
+	{
429
+		$short_desc = $this->get('EVT_short_desc');
430
+		if (! empty($short_desc) || $not_full_desc) {
431
+			return $short_desc;
432
+		}
433
+		$full_desc = $this->get('EVT_desc');
434
+		return wp_trim_words($full_desc, $num_words, $more);
435
+	}
436
+
437
+
438
+	/**
439
+	 * @return bool
440
+	 * @throws EE_Error
441
+	 */
442
+	public function slug()
443
+	{
444
+		return $this->get('EVT_slug');
445
+	}
446
+
447
+
448
+	/**
449
+	 * @return bool
450
+	 * @throws EE_Error
451
+	 */
452
+	public function timezone_string()
453
+	{
454
+		return $this->get('EVT_timezone_string');
455
+	}
456
+
457
+
458
+	/**
459
+	 * @return bool
460
+	 * @throws EE_Error
461
+	 */
462
+	public function visible_on()
463
+	{
464
+		return $this->get('EVT_visible_on');
465
+	}
466
+
467
+
468
+	/**
469
+	 * @return int
470
+	 * @throws EE_Error
471
+	 */
472
+	public function wp_user()
473
+	{
474
+		return $this->get('EVT_wp_user');
475
+	}
476
+
477
+
478
+	/**
479
+	 * @return bool
480
+	 * @throws EE_Error
481
+	 */
482
+	public function donations()
483
+	{
484
+		return $this->get('EVT_donations');
485
+	}
486
+
487
+
488
+	/**
489
+	 * @param $limit
490
+	 * @throws EE_Error
491
+	 */
492
+	public function set_additional_limit($limit)
493
+	{
494
+		$this->set('EVT_additional_limit', $limit);
495
+	}
496
+
497
+
498
+	/**
499
+	 * @param $created
500
+	 * @throws EE_Error
501
+	 */
502
+	public function set_created($created)
503
+	{
504
+		$this->set('EVT_created', $created);
505
+	}
506
+
507
+
508
+	/**
509
+	 * @param $desc
510
+	 * @throws EE_Error
511
+	 */
512
+	public function set_description($desc)
513
+	{
514
+		$this->set('EVT_desc', $desc);
515
+	}
516
+
517
+
518
+	/**
519
+	 * @param $display_desc
520
+	 * @throws EE_Error
521
+	 */
522
+	public function set_display_description($display_desc)
523
+	{
524
+		$this->set('EVT_display_desc', $display_desc);
525
+	}
526
+
527
+
528
+	/**
529
+	 * @param $display_ticket_selector
530
+	 * @throws EE_Error
531
+	 */
532
+	public function set_display_ticket_selector($display_ticket_selector)
533
+	{
534
+		$this->set('EVT_display_ticket_selector', $display_ticket_selector);
535
+	}
536
+
537
+
538
+	/**
539
+	 * @param $external_url
540
+	 * @throws EE_Error
541
+	 */
542
+	public function set_external_url($external_url)
543
+	{
544
+		$this->set('EVT_external_URL', $external_url);
545
+	}
546
+
547
+
548
+	/**
549
+	 * @param $member_only
550
+	 * @throws EE_Error
551
+	 */
552
+	public function set_member_only($member_only)
553
+	{
554
+		$this->set('EVT_member_only', $member_only);
555
+	}
556
+
557
+
558
+	/**
559
+	 * @param $event_phone
560
+	 * @throws EE_Error
561
+	 */
562
+	public function set_event_phone($event_phone)
563
+	{
564
+		$this->set('EVT_phone', $event_phone);
565
+	}
566
+
567
+
568
+	/**
569
+	 * @param $modified
570
+	 * @throws EE_Error
571
+	 */
572
+	public function set_modified($modified)
573
+	{
574
+		$this->set('EVT_modified', $modified);
575
+	}
576
+
577
+
578
+	/**
579
+	 * @param $name
580
+	 * @throws EE_Error
581
+	 */
582
+	public function set_name($name)
583
+	{
584
+		$this->set('EVT_name', $name);
585
+	}
586
+
587
+
588
+	/**
589
+	 * @param $order
590
+	 * @throws EE_Error
591
+	 */
592
+	public function set_order($order)
593
+	{
594
+		$this->set('EVT_order', $order);
595
+	}
596
+
597
+
598
+	/**
599
+	 * @param $short_desc
600
+	 * @throws EE_Error
601
+	 */
602
+	public function set_short_description($short_desc)
603
+	{
604
+		$this->set('EVT_short_desc', $short_desc);
605
+	}
606
+
607
+
608
+	/**
609
+	 * @param $slug
610
+	 * @throws EE_Error
611
+	 */
612
+	public function set_slug($slug)
613
+	{
614
+		$this->set('EVT_slug', $slug);
615
+	}
616
+
617
+
618
+	/**
619
+	 * @param $timezone_string
620
+	 * @throws EE_Error
621
+	 */
622
+	public function set_timezone_string($timezone_string)
623
+	{
624
+		$this->set('EVT_timezone_string', $timezone_string);
625
+	}
626
+
627
+
628
+	/**
629
+	 * @param $visible_on
630
+	 * @throws EE_Error
631
+	 */
632
+	public function set_visible_on($visible_on)
633
+	{
634
+		$this->set('EVT_visible_on', $visible_on);
635
+	}
636
+
637
+
638
+	/**
639
+	 * @param $wp_user
640
+	 * @throws EE_Error
641
+	 */
642
+	public function set_wp_user($wp_user)
643
+	{
644
+		$this->set('EVT_wp_user', $wp_user);
645
+	}
646
+
647
+
648
+	/**
649
+	 * @param $default_registration_status
650
+	 * @throws EE_Error
651
+	 */
652
+	public function set_default_registration_status($default_registration_status)
653
+	{
654
+		$this->set('EVT_default_registration_status', $default_registration_status);
655
+	}
656
+
657
+
658
+	/**
659
+	 * @param $donations
660
+	 * @throws EE_Error
661
+	 */
662
+	public function set_donations($donations)
663
+	{
664
+		$this->set('EVT_donations', $donations);
665
+	}
666
+
667
+
668
+	/**
669
+	 * Adds a venue to this event
670
+	 *
671
+	 * @param EE_Venue /int $venue_id_or_obj
672
+	 * @return EE_Base_Class|EE_Venue
673
+	 * @throws EE_Error
674
+	 */
675
+	public function add_venue($venue_id_or_obj)
676
+	{
677
+		return $this->_add_relation_to($venue_id_or_obj, 'Venue');
678
+	}
679
+
680
+
681
+	/**
682
+	 * Removes a venue from the event
683
+	 *
684
+	 * @param EE_Venue /int $venue_id_or_obj
685
+	 * @return EE_Base_Class|EE_Venue
686
+	 * @throws EE_Error
687
+	 */
688
+	public function remove_venue($venue_id_or_obj)
689
+	{
690
+		return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
691
+	}
692
+
693
+
694
+	/**
695
+	 * Gets all the venues related ot the event. May provide additional $query_params if desired
696
+	 *
697
+	 * @param array $query_params like EEM_Base::get_all's $query_params
698
+	 * @return EE_Base_Class[]|EE_Venue[]
699
+	 * @throws EE_Error
700
+	 */
701
+	public function venues($query_params = array())
702
+	{
703
+		return $this->get_many_related('Venue', $query_params);
704
+	}
705
+
706
+
707
+	/**
708
+	 * check if event id is present and if event is published
709
+	 *
710
+	 * @access public
711
+	 * @return boolean true yes, false no
712
+	 * @throws EE_Error
713
+	 */
714
+	private function _has_ID_and_is_published()
715
+	{
716
+		// first check if event id is present and not NULL,
717
+		// then check if this event is published (or any of the equivalent "published" statuses)
718
+		return
719
+			$this->ID() && $this->ID() !== null
720
+			&& (
721
+				$this->status() === 'publish'
722
+				|| $this->status() === EEM_Event::sold_out
723
+				|| $this->status() === EEM_Event::postponed
724
+				|| $this->status() === EEM_Event::cancelled
725
+			);
726
+	}
727
+
728
+
729
+	/**
730
+	 * This simply compares the internal dates with NOW and determines if the event is upcoming or not.
731
+	 *
732
+	 * @access public
733
+	 * @return boolean true yes, false no
734
+	 * @throws EE_Error
735
+	 */
736
+	public function is_upcoming()
737
+	{
738
+		// check if event id is present and if this event is published
739
+		if ($this->is_inactive()) {
740
+			return false;
741
+		}
742
+		// set initial value
743
+		$upcoming = false;
744
+		// next let's get all datetimes and loop through them
745
+		$datetimes = $this->datetimes_in_chronological_order();
746
+		foreach ($datetimes as $datetime) {
747
+			if ($datetime instanceof EE_Datetime) {
748
+				// if this dtt is expired then we continue cause one of the other datetimes might be upcoming.
749
+				if ($datetime->is_expired()) {
750
+					continue;
751
+				}
752
+				// if this dtt is active then we return false.
753
+				if ($datetime->is_active()) {
754
+					return false;
755
+				}
756
+				// otherwise let's check upcoming status
757
+				$upcoming = $datetime->is_upcoming();
758
+			}
759
+		}
760
+		return $upcoming;
761
+	}
762
+
763
+
764
+	/**
765
+	 * @return bool
766
+	 * @throws EE_Error
767
+	 */
768
+	public function is_active()
769
+	{
770
+		// check if event id is present and if this event is published
771
+		if ($this->is_inactive()) {
772
+			return false;
773
+		}
774
+		// set initial value
775
+		$active = false;
776
+		// next let's get all datetimes and loop through them
777
+		$datetimes = $this->datetimes_in_chronological_order();
778
+		foreach ($datetimes as $datetime) {
779
+			if ($datetime instanceof EE_Datetime) {
780
+				// if this dtt is expired then we continue cause one of the other datetimes might be active.
781
+				if ($datetime->is_expired()) {
782
+					continue;
783
+				}
784
+				// if this dtt is upcoming then we return false.
785
+				if ($datetime->is_upcoming()) {
786
+					return false;
787
+				}
788
+				// otherwise let's check active status
789
+				$active = $datetime->is_active();
790
+			}
791
+		}
792
+		return $active;
793
+	}
794
+
795
+
796
+	/**
797
+	 * @return bool
798
+	 * @throws EE_Error
799
+	 */
800
+	public function is_expired()
801
+	{
802
+		// check if event id is present and if this event is published
803
+		if ($this->is_inactive()) {
804
+			return false;
805
+		}
806
+		// set initial value
807
+		$expired = false;
808
+		// first let's get all datetimes and loop through them
809
+		$datetimes = $this->datetimes_in_chronological_order();
810
+		foreach ($datetimes as $datetime) {
811
+			if ($datetime instanceof EE_Datetime) {
812
+				// if this dtt is upcoming or active then we return false.
813
+				if ($datetime->is_upcoming() || $datetime->is_active()) {
814
+					return false;
815
+				}
816
+				// otherwise let's check active status
817
+				$expired = $datetime->is_expired();
818
+			}
819
+		}
820
+		return $expired;
821
+	}
822
+
823
+
824
+	/**
825
+	 * @return bool
826
+	 * @throws EE_Error
827
+	 */
828
+	public function is_inactive()
829
+	{
830
+		// check if event id is present and if this event is published
831
+		if ($this->_has_ID_and_is_published()) {
832
+			return false;
833
+		}
834
+		return true;
835
+	}
836
+
837
+
838
+	/**
839
+	 * calculate spaces remaining based on "saleable" tickets
840
+	 *
841
+	 * @param array $tickets
842
+	 * @param bool  $filtered
843
+	 * @return int|float
844
+	 * @throws EE_Error
845
+	 * @throws DomainException
846
+	 * @throws UnexpectedEntityException
847
+	 */
848
+	public function spaces_remaining($tickets = array(), $filtered = true)
849
+	{
850
+		$this->getAvailableSpacesCalculator()->setActiveTickets($tickets);
851
+		$spaces_remaining = $this->getAvailableSpacesCalculator()->spacesRemaining();
852
+		return $filtered
853
+			? apply_filters(
854
+				'FHEE_EE_Event__spaces_remaining',
855
+				$spaces_remaining,
856
+				$this,
857
+				$tickets
858
+			)
859
+			: $spaces_remaining;
860
+	}
861
+
862
+
863
+	/**
864
+	 *    perform_sold_out_status_check
865
+	 *    checks all of this events's datetime  reg_limit - sold values to determine if ANY datetimes have spaces
866
+	 *    available... if NOT, then the event status will get toggled to 'sold_out'
867
+	 *
868
+	 * @return bool    return the ACTUAL sold out state.
869
+	 * @throws EE_Error
870
+	 * @throws DomainException
871
+	 * @throws UnexpectedEntityException
872
+	 */
873
+	public function perform_sold_out_status_check()
874
+	{
875
+		// get all unexpired untrashed tickets
876
+		$tickets = $this->tickets(
877
+			array(
878
+				array('TKT_deleted' => false),
879
+				'order_by' => array('TKT_qty' => 'ASC'),
880
+			)
881
+		);
882
+		$all_expired = true;
883
+		foreach ($tickets as $ticket) {
884
+			if (! $ticket->is_expired()) {
885
+				$all_expired = false;
886
+				break;
887
+			}
888
+		}
889
+		// if all the tickets are just expired, then don't update the event status to sold out
890
+		if ($all_expired) {
891
+			return true;
892
+		}
893
+		$spaces_remaining = $this->spaces_remaining($tickets);
894
+		if ($spaces_remaining < 1) {
895
+			$this->set_status(EEM_Event::sold_out);
896
+			$this->save();
897
+			$sold_out = true;
898
+		} else {
899
+			$sold_out = false;
900
+			// was event previously marked as sold out ?
901
+			if ($this->status() === EEM_Event::sold_out) {
902
+				// revert status to previous value, if it was set
903
+				$previous_event_status = $this->get_post_meta('_previous_event_status', true);
904
+				if ($previous_event_status) {
905
+					$this->set_status($previous_event_status);
906
+					$this->save();
907
+				}
908
+			}
909
+		}
910
+		do_action('AHEE__EE_Event__perform_sold_out_status_check__end', $this, $sold_out, $spaces_remaining, $tickets);
911
+		return $sold_out;
912
+	}
913
+
914
+
915
+	/**
916
+	 * This returns the total remaining spaces for sale on this event.
917
+	 *
918
+	 * @uses EE_Event::total_available_spaces()
919
+	 * @return float|int
920
+	 * @throws EE_Error
921
+	 * @throws DomainException
922
+	 * @throws UnexpectedEntityException
923
+	 */
924
+	public function spaces_remaining_for_sale()
925
+	{
926
+		return $this->total_available_spaces(true);
927
+	}
928
+
929
+
930
+	/**
931
+	 * This returns the total spaces available for an event
932
+	 * while considering all the qtys on the tickets and the reg limits
933
+	 * on the datetimes attached to this event.
934
+	 *
935
+	 * @param   bool $consider_sold Whether to consider any tickets that have already sold in our calculation.
936
+	 *                              If this is false, then we return the most tickets that could ever be sold
937
+	 *                              for this event with the datetime and tickets setup on the event under optimal
938
+	 *                              selling conditions.  Otherwise we return a live calculation of spaces available
939
+	 *                              based on tickets sold.  Depending on setup and stage of sales, this
940
+	 *                              may appear to equal remaining tickets.  However, the more tickets are
941
+	 *                              sold out, the more accurate the "live" total is.
942
+	 * @return float|int
943
+	 * @throws EE_Error
944
+	 * @throws DomainException
945
+	 * @throws UnexpectedEntityException
946
+	 */
947
+	public function total_available_spaces($consider_sold = false)
948
+	{
949
+		$spaces_available = $consider_sold
950
+			? $this->getAvailableSpacesCalculator()->spacesRemaining()
951
+			: $this->getAvailableSpacesCalculator()->totalSpacesAvailable();
952
+		return apply_filters(
953
+			'FHEE_EE_Event__total_available_spaces__spaces_available',
954
+			$spaces_available,
955
+			$this,
956
+			$this->getAvailableSpacesCalculator()->getDatetimes(),
957
+			$this->getAvailableSpacesCalculator()->getActiveTickets()
958
+		);
959
+	}
960
+
961
+
962
+	/**
963
+	 * Checks if the event is set to sold out
964
+	 *
965
+	 * @param  bool $actual whether or not to perform calculations to not only figure the
966
+	 *                      actual status but also to flip the status if necessary to sold
967
+	 *                      out If false, we just check the existing status of the event
968
+	 * @return boolean
969
+	 * @throws EE_Error
970
+	 */
971
+	public function is_sold_out($actual = false)
972
+	{
973
+		if (! $actual) {
974
+			return $this->status() === EEM_Event::sold_out;
975
+		}
976
+		return $this->perform_sold_out_status_check();
977
+	}
978
+
979
+
980
+	/**
981
+	 * Checks if the event is marked as postponed
982
+	 *
983
+	 * @return boolean
984
+	 */
985
+	public function is_postponed()
986
+	{
987
+		return $this->status() === EEM_Event::postponed;
988
+	}
989
+
990
+
991
+	/**
992
+	 * Checks if the event is marked as cancelled
993
+	 *
994
+	 * @return boolean
995
+	 */
996
+	public function is_cancelled()
997
+	{
998
+		return $this->status() === EEM_Event::cancelled;
999
+	}
1000
+
1001
+
1002
+	/**
1003
+	 * Get the logical active status in a hierarchical order for all the datetimes.  Note
1004
+	 * Basically, we order the datetimes by EVT_start_date.  Then first test on whether the event is published.  If its
1005
+	 * NOT published then we test for whether its expired or not.  IF it IS published then we test first on whether an
1006
+	 * event has any active dates.  If no active dates then we check for any upcoming dates.  If no upcoming dates then
1007
+	 * the event is considered expired.
1008
+	 * NOTE: this method does NOT calculate whether the datetimes are sold out when event is published.  Sold Out is a
1009
+	 * status set on the EVENT when it is not published and thus is done
1010
+	 *
1011
+	 * @param bool $reset
1012
+	 * @return bool | string - based on EE_Datetime active constants or FALSE if error.
1013
+	 * @throws EE_Error
1014
+	 */
1015
+	public function get_active_status($reset = false)
1016
+	{
1017
+		// if the active status has already been set, then just use that value (unless we are resetting it)
1018
+		if (! empty($this->_active_status) && ! $reset) {
1019
+			return $this->_active_status;
1020
+		}
1021
+		// first check if event id is present on this object
1022
+		if (! $this->ID()) {
1023
+			return false;
1024
+		}
1025
+		$where_params_for_event = array(array('EVT_ID' => $this->ID()));
1026
+		// if event is published:
1027
+		if ($this->status() === 'publish') {
1028
+			// active?
1029
+			if (EEM_Datetime::instance()->get_datetime_count_for_status(
1030
+				EE_Datetime::active,
1031
+				$where_params_for_event
1032
+			) > 0) {
1033
+				$this->_active_status = EE_Datetime::active;
1034
+			} else {
1035
+				// upcoming?
1036
+				if (EEM_Datetime::instance()->get_datetime_count_for_status(
1037
+					EE_Datetime::upcoming,
1038
+					$where_params_for_event
1039
+				) > 0) {
1040
+					$this->_active_status = EE_Datetime::upcoming;
1041
+				} else {
1042
+					// expired?
1043
+					if (EEM_Datetime::instance()->get_datetime_count_for_status(
1044
+						EE_Datetime::expired,
1045
+						$where_params_for_event
1046
+					) > 0
1047
+					) {
1048
+						$this->_active_status = EE_Datetime::expired;
1049
+					} else {
1050
+						// it would be odd if things make it this far because it basically means there are no datetime's
1051
+						// attached to the event.  So in this case it will just be considered inactive.
1052
+						$this->_active_status = EE_Datetime::inactive;
1053
+					}
1054
+				}
1055
+			}
1056
+		} else {
1057
+			// the event is not published, so let's just set it's active status according to its' post status
1058
+			switch ($this->status()) {
1059
+				case EEM_Event::sold_out:
1060
+					$this->_active_status = EE_Datetime::sold_out;
1061
+					break;
1062
+				case EEM_Event::cancelled:
1063
+					$this->_active_status = EE_Datetime::cancelled;
1064
+					break;
1065
+				case EEM_Event::postponed:
1066
+					$this->_active_status = EE_Datetime::postponed;
1067
+					break;
1068
+				default:
1069
+					$this->_active_status = EE_Datetime::inactive;
1070
+			}
1071
+		}
1072
+		return $this->_active_status;
1073
+	}
1074
+
1075
+
1076
+	/**
1077
+	 *    pretty_active_status
1078
+	 *
1079
+	 * @access public
1080
+	 * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1081
+	 * @return mixed void|string
1082
+	 * @throws EE_Error
1083
+	 */
1084
+	public function pretty_active_status($echo = true)
1085
+	{
1086
+		$active_status = $this->get_active_status();
1087
+		$status = '<span class="ee-status event-active-status-'
1088
+				  . $active_status
1089
+				  . '">'
1090
+				  . EEH_Template::pretty_status($active_status, false, 'sentence')
1091
+				  . '</span>';
1092
+		if ($echo) {
1093
+			echo $status;
1094
+			return '';
1095
+		}
1096
+		return $status;
1097
+	}
1098
+
1099
+
1100
+	/**
1101
+	 * @return bool|int
1102
+	 * @throws EE_Error
1103
+	 */
1104
+	public function get_number_of_tickets_sold()
1105
+	{
1106
+		$tkt_sold = 0;
1107
+		if (! $this->ID()) {
1108
+			return 0;
1109
+		}
1110
+		$datetimes = $this->datetimes();
1111
+		foreach ($datetimes as $datetime) {
1112
+			if ($datetime instanceof EE_Datetime) {
1113
+				$tkt_sold += $datetime->sold();
1114
+			}
1115
+		}
1116
+		return $tkt_sold;
1117
+	}
1118
+
1119
+
1120
+	/**
1121
+	 * This just returns a count of all the registrations for this event
1122
+	 *
1123
+	 * @access  public
1124
+	 * @return int
1125
+	 * @throws EE_Error
1126
+	 */
1127
+	public function get_count_of_all_registrations()
1128
+	{
1129
+		return EEM_Event::instance()->count_related($this, 'Registration');
1130
+	}
1131
+
1132
+
1133
+	/**
1134
+	 * This returns the ticket with the earliest start time that is
1135
+	 * available for this event (across all datetimes attached to the event)
1136
+	 *
1137
+	 * @return EE_Base_Class|EE_Ticket|null
1138
+	 * @throws EE_Error
1139
+	 */
1140
+	public function get_ticket_with_earliest_start_time()
1141
+	{
1142
+		$where['Datetime.EVT_ID'] = $this->ID();
1143
+		$query_params = array($where, 'order_by' => array('TKT_start_date' => 'ASC'));
1144
+		return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1145
+	}
1146
+
1147
+
1148
+	/**
1149
+	 * This returns the ticket with the latest end time that is available
1150
+	 * for this event (across all datetimes attached to the event)
1151
+	 *
1152
+	 * @return EE_Base_Class|EE_Ticket|null
1153
+	 * @throws EE_Error
1154
+	 */
1155
+	public function get_ticket_with_latest_end_time()
1156
+	{
1157
+		$where['Datetime.EVT_ID'] = $this->ID();
1158
+		$query_params = array($where, 'order_by' => array('TKT_end_date' => 'DESC'));
1159
+		return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1160
+	}
1161
+
1162
+
1163
+	/**
1164
+	 * This returns whether there are any tickets on sale for this event.
1165
+	 *
1166
+	 * @return bool true = YES tickets on sale.
1167
+	 * @throws EE_Error
1168
+	 */
1169
+	public function tickets_on_sale()
1170
+	{
1171
+		$earliest_ticket = $this->get_ticket_with_earliest_start_time();
1172
+		$latest_ticket = $this->get_ticket_with_latest_end_time();
1173
+		if (! $latest_ticket instanceof EE_Ticket && ! $earliest_ticket instanceof EE_Ticket) {
1174
+			return false;
1175
+		}
1176
+		// check on sale for these two tickets.
1177
+		if ($latest_ticket->is_on_sale() || $earliest_ticket->is_on_sale()) {
1178
+			return true;
1179
+		}
1180
+		return false;
1181
+	}
1182
+
1183
+
1184
+	/**
1185
+	 * Gets the URL for viewing this event on the front-end. Overrides parent
1186
+	 * to check for an external URL first
1187
+	 *
1188
+	 * @return string
1189
+	 * @throws EE_Error
1190
+	 */
1191
+	public function get_permalink()
1192
+	{
1193
+		if ($this->external_url()) {
1194
+			return $this->external_url();
1195
+		}
1196
+		return parent::get_permalink();
1197
+	}
1198
+
1199
+
1200
+	/**
1201
+	 * Gets the first term for 'espresso_event_categories' we can find
1202
+	 *
1203
+	 * @param array $query_params like EEM_Base::get_all
1204
+	 * @return EE_Base_Class|EE_Term|null
1205
+	 * @throws EE_Error
1206
+	 */
1207
+	public function first_event_category($query_params = array())
1208
+	{
1209
+		$query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1210
+		$query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1211
+		return EEM_Term::instance()->get_one($query_params);
1212
+	}
1213
+
1214
+
1215
+	/**
1216
+	 * Gets all terms for 'espresso_event_categories' we can find
1217
+	 *
1218
+	 * @param array $query_params
1219
+	 * @return EE_Base_Class[]|EE_Term[]
1220
+	 * @throws EE_Error
1221
+	 */
1222
+	public function get_all_event_categories($query_params = array())
1223
+	{
1224
+		$query_params[0]['Term_Taxonomy.taxonomy'] = 'espresso_event_categories';
1225
+		$query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1226
+		return EEM_Term::instance()->get_all($query_params);
1227
+	}
1228
+
1229
+
1230
+	/**
1231
+	 * Adds a question group to this event
1232
+	 *
1233
+	 * @param EE_Question_Group|int $question_group_id_or_obj
1234
+	 * @param bool                  $for_primary if true, the question group will be added for the primary
1235
+	 *                                           registrant, if false will be added for others. default: false
1236
+	 * @return EE_Base_Class|EE_Question_Group
1237
+	 * @throws EE_Error
1238
+	 */
1239
+	public function add_question_group($question_group_id_or_obj, $for_primary = false)
1240
+	{
1241
+		$extra = $for_primary
1242
+			? array('EQG_primary' => 1)
1243
+			: array();
1244
+		return $this->_add_relation_to($question_group_id_or_obj, 'Question_Group', $extra);
1245
+	}
1246
+
1247
+
1248
+	/**
1249
+	 * Removes a question group from the event
1250
+	 *
1251
+	 * @param EE_Question_Group|int $question_group_id_or_obj
1252
+	 * @param bool                  $for_primary if true, the question group will be removed from the primary
1253
+	 *                                           registrant, if false will be removed from others. default: false
1254
+	 * @return EE_Base_Class|EE_Question_Group
1255
+	 * @throws EE_Error
1256
+	 */
1257
+	public function remove_question_group($question_group_id_or_obj, $for_primary = false)
1258
+	{
1259
+		$where = $for_primary
1260
+			? array('EQG_primary' => 1)
1261
+			: array();
1262
+		return $this->_remove_relation_to($question_group_id_or_obj, 'Question_Group', $where);
1263
+	}
1264
+
1265
+
1266
+	/**
1267
+	 * Gets all the question groups, ordering them by QSG_order ascending
1268
+	 *
1269
+	 * @param array $query_params @see EEM_Base::get_all
1270
+	 * @return EE_Base_Class[]|EE_Question_Group[]
1271
+	 * @throws EE_Error
1272
+	 */
1273
+	public function question_groups($query_params = array())
1274
+	{
1275
+		$query_params = ! empty($query_params) ? $query_params : array('order_by' => array('QSG_order' => 'ASC'));
1276
+		return $this->get_many_related('Question_Group', $query_params);
1277
+	}
1278
+
1279
+
1280
+	/**
1281
+	 * Implementation for EEI_Has_Icon interface method.
1282
+	 *
1283
+	 * @see EEI_Visual_Representation for comments
1284
+	 * @return string
1285
+	 */
1286
+	public function get_icon()
1287
+	{
1288
+		return '<span class="dashicons dashicons-flag"></span>';
1289
+	}
1290
+
1291
+
1292
+	/**
1293
+	 * Implementation for EEI_Admin_Links interface method.
1294
+	 *
1295
+	 * @see EEI_Admin_Links for comments
1296
+	 * @return string
1297
+	 * @throws EE_Error
1298
+	 */
1299
+	public function get_admin_details_link()
1300
+	{
1301
+		return $this->get_admin_edit_link();
1302
+	}
1303
+
1304
+
1305
+	/**
1306
+	 * Implementation for EEI_Admin_Links interface method.
1307
+	 *
1308
+	 * @see EEI_Admin_Links for comments
1309
+	 * @return string
1310
+	 * @throws EE_Error
1311
+	 */
1312
+	public function get_admin_edit_link()
1313
+	{
1314
+		return EEH_URL::add_query_args_and_nonce(
1315
+			array(
1316
+				'page'   => 'espresso_events',
1317
+				'action' => 'edit',
1318
+				'post'   => $this->ID(),
1319
+			),
1320
+			admin_url('admin.php')
1321
+		);
1322
+	}
1323
+
1324
+
1325
+	/**
1326
+	 * Implementation for EEI_Admin_Links interface method.
1327
+	 *
1328
+	 * @see EEI_Admin_Links for comments
1329
+	 * @return string
1330
+	 */
1331
+	public function get_admin_settings_link()
1332
+	{
1333
+		return EEH_URL::add_query_args_and_nonce(
1334
+			array(
1335
+				'page'   => 'espresso_events',
1336
+				'action' => 'default_event_settings',
1337
+			),
1338
+			admin_url('admin.php')
1339
+		);
1340
+	}
1341
+
1342
+
1343
+	/**
1344
+	 * Implementation for EEI_Admin_Links interface method.
1345
+	 *
1346
+	 * @see EEI_Admin_Links for comments
1347
+	 * @return string
1348
+	 */
1349
+	public function get_admin_overview_link()
1350
+	{
1351
+		return EEH_URL::add_query_args_and_nonce(
1352
+			array(
1353
+				'page'   => 'espresso_events',
1354
+				'action' => 'default',
1355
+			),
1356
+			admin_url('admin.php')
1357
+		);
1358
+	}
1359 1359
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -69,7 +69,7 @@  discard block
 block discarded – undo
69 69
      */
70 70
     public function getAvailableSpacesCalculator()
71 71
     {
72
-        if (! $this->available_spaces_calculator instanceof EventSpacesCalculator) {
72
+        if ( ! $this->available_spaces_calculator instanceof EventSpacesCalculator) {
73 73
             $this->available_spaces_calculator = new EventSpacesCalculator($this);
74 74
         }
75 75
         return $this->available_spaces_calculator;
@@ -211,7 +211,7 @@  discard block
 block discarded – undo
211 211
      */
212 212
     public function primary_datetime($try_to_exclude_expired = true, $try_to_exclude_deleted = true)
213 213
     {
214
-        if (! empty($this->_Primary_Datetime)) {
214
+        if ( ! empty($this->_Primary_Datetime)) {
215 215
             return $this->_Primary_Datetime;
216 216
         }
217 217
         $this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)->get_primary_datetime_for_event(
@@ -234,7 +234,7 @@  discard block
 block discarded – undo
234 234
     {
235 235
         // first get all datetimes
236 236
         $datetimes = $this->datetimes_ordered();
237
-        if (! $datetimes) {
237
+        if ( ! $datetimes) {
238 238
             return array();
239 239
         }
240 240
         $datetime_ids = array();
@@ -427,7 +427,7 @@  discard block
 block discarded – undo
427 427
     public function short_description($num_words = 55, $more = null, $not_full_desc = false)
428 428
     {
429 429
         $short_desc = $this->get('EVT_short_desc');
430
-        if (! empty($short_desc) || $not_full_desc) {
430
+        if ( ! empty($short_desc) || $not_full_desc) {
431 431
             return $short_desc;
432 432
         }
433 433
         $full_desc = $this->get('EVT_desc');
@@ -881,7 +881,7 @@  discard block
 block discarded – undo
881 881
         );
882 882
         $all_expired = true;
883 883
         foreach ($tickets as $ticket) {
884
-            if (! $ticket->is_expired()) {
884
+            if ( ! $ticket->is_expired()) {
885 885
                 $all_expired = false;
886 886
                 break;
887 887
             }
@@ -970,7 +970,7 @@  discard block
 block discarded – undo
970 970
      */
971 971
     public function is_sold_out($actual = false)
972 972
     {
973
-        if (! $actual) {
973
+        if ( ! $actual) {
974 974
             return $this->status() === EEM_Event::sold_out;
975 975
         }
976 976
         return $this->perform_sold_out_status_check();
@@ -1015,11 +1015,11 @@  discard block
 block discarded – undo
1015 1015
     public function get_active_status($reset = false)
1016 1016
     {
1017 1017
         // if the active status has already been set, then just use that value (unless we are resetting it)
1018
-        if (! empty($this->_active_status) && ! $reset) {
1018
+        if ( ! empty($this->_active_status) && ! $reset) {
1019 1019
             return $this->_active_status;
1020 1020
         }
1021 1021
         // first check if event id is present on this object
1022
-        if (! $this->ID()) {
1022
+        if ( ! $this->ID()) {
1023 1023
             return false;
1024 1024
         }
1025 1025
         $where_params_for_event = array(array('EVT_ID' => $this->ID()));
@@ -1104,7 +1104,7 @@  discard block
 block discarded – undo
1104 1104
     public function get_number_of_tickets_sold()
1105 1105
     {
1106 1106
         $tkt_sold = 0;
1107
-        if (! $this->ID()) {
1107
+        if ( ! $this->ID()) {
1108 1108
             return 0;
1109 1109
         }
1110 1110
         $datetimes = $this->datetimes();
@@ -1170,7 +1170,7 @@  discard block
 block discarded – undo
1170 1170
     {
1171 1171
         $earliest_ticket = $this->get_ticket_with_earliest_start_time();
1172 1172
         $latest_ticket = $this->get_ticket_with_latest_end_time();
1173
-        if (! $latest_ticket instanceof EE_Ticket && ! $earliest_ticket instanceof EE_Ticket) {
1173
+        if ( ! $latest_ticket instanceof EE_Ticket && ! $earliest_ticket instanceof EE_Ticket) {
1174 1174
             return false;
1175 1175
         }
1176 1176
         // check on sale for these two tickets.
Please login to merge, or discard this patch.
core/db_classes/EE_Line_Item.class.php 2 patches
Indentation   +1426 added lines, -1426 removed lines patch added patch discarded remove patch
@@ -14,1430 +14,1430 @@
 block discarded – undo
14 14
 class EE_Line_Item extends EE_Base_Class implements EEI_Line_Item
15 15
 {
16 16
 
17
-    /**
18
-     * for children line items (currently not a normal relation)
19
-     *
20
-     * @type EE_Line_Item[]
21
-     */
22
-    protected $_children = array();
23
-
24
-    /**
25
-     * for the parent line item
26
-     *
27
-     * @var EE_Line_Item
28
-     */
29
-    protected $_parent;
30
-
31
-
32
-    /**
33
-     *
34
-     * @param array  $props_n_values          incoming values
35
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
36
-     *                                        used.)
37
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
38
-     *                                        date_format and the second value is the time format
39
-     * @return EE_Line_Item
40
-     * @throws EE_Error
41
-     */
42
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
43
-    {
44
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
45
-        return $has_object
46
-            ? $has_object
47
-            : new self($props_n_values, false, $timezone);
48
-    }
49
-
50
-
51
-    /**
52
-     * @param array  $props_n_values  incoming values from the database
53
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
54
-     *                                the website will be used.
55
-     * @return EE_Line_Item
56
-     * @throws EE_Error
57
-     */
58
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
59
-    {
60
-        return new self($props_n_values, true, $timezone);
61
-    }
62
-
63
-
64
-    /**
65
-     * Adds some defaults if they're not specified
66
-     *
67
-     * @param array  $fieldValues
68
-     * @param bool   $bydb
69
-     * @param string $timezone
70
-     * @throws EE_Error
71
-     */
72
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
73
-    {
74
-        parent::__construct($fieldValues, $bydb, $timezone);
75
-        if (! $this->get('LIN_code')) {
76
-            $this->set_code($this->generate_code());
77
-        }
78
-    }
79
-
80
-
81
-    /**
82
-     * Gets ID
83
-     *
84
-     * @return int
85
-     * @throws EE_Error
86
-     */
87
-    public function ID()
88
-    {
89
-        return $this->get('LIN_ID');
90
-    }
91
-
92
-
93
-    /**
94
-     * Gets TXN_ID
95
-     *
96
-     * @return int
97
-     * @throws EE_Error
98
-     */
99
-    public function TXN_ID()
100
-    {
101
-        return $this->get('TXN_ID');
102
-    }
103
-
104
-
105
-    /**
106
-     * Sets TXN_ID
107
-     *
108
-     * @param int $TXN_ID
109
-     * @throws EE_Error
110
-     */
111
-    public function set_TXN_ID($TXN_ID)
112
-    {
113
-        $this->set('TXN_ID', $TXN_ID);
114
-    }
115
-
116
-
117
-    /**
118
-     * Gets name
119
-     *
120
-     * @return string
121
-     * @throws EE_Error
122
-     */
123
-    public function name()
124
-    {
125
-        $name = $this->get('LIN_name');
126
-        if (! $name) {
127
-            $name = ucwords(str_replace('-', ' ', $this->type()));
128
-        }
129
-        return $name;
130
-    }
131
-
132
-
133
-    /**
134
-     * Sets name
135
-     *
136
-     * @param string $name
137
-     * @throws EE_Error
138
-     */
139
-    public function set_name($name)
140
-    {
141
-        $this->set('LIN_name', $name);
142
-    }
143
-
144
-
145
-    /**
146
-     * Gets desc
147
-     *
148
-     * @return string
149
-     * @throws EE_Error
150
-     */
151
-    public function desc()
152
-    {
153
-        return $this->get('LIN_desc');
154
-    }
155
-
156
-
157
-    /**
158
-     * Sets desc
159
-     *
160
-     * @param string $desc
161
-     * @throws EE_Error
162
-     */
163
-    public function set_desc($desc)
164
-    {
165
-        $this->set('LIN_desc', $desc);
166
-    }
167
-
168
-
169
-    /**
170
-     * Gets quantity
171
-     *
172
-     * @return int
173
-     * @throws EE_Error
174
-     */
175
-    public function quantity()
176
-    {
177
-        return $this->get('LIN_quantity');
178
-    }
179
-
180
-
181
-    /**
182
-     * Sets quantity
183
-     *
184
-     * @param int $quantity
185
-     * @throws EE_Error
186
-     */
187
-    public function set_quantity($quantity)
188
-    {
189
-        $this->set('LIN_quantity', max($quantity, 0));
190
-    }
191
-
192
-
193
-    /**
194
-     * Gets item_id
195
-     *
196
-     * @return string
197
-     * @throws EE_Error
198
-     */
199
-    public function OBJ_ID()
200
-    {
201
-        return $this->get('OBJ_ID');
202
-    }
203
-
204
-
205
-    /**
206
-     * Sets item_id
207
-     *
208
-     * @param string $item_id
209
-     * @throws EE_Error
210
-     */
211
-    public function set_OBJ_ID($item_id)
212
-    {
213
-        $this->set('OBJ_ID', $item_id);
214
-    }
215
-
216
-
217
-    /**
218
-     * Gets item_type
219
-     *
220
-     * @return string
221
-     * @throws EE_Error
222
-     */
223
-    public function OBJ_type()
224
-    {
225
-        return $this->get('OBJ_type');
226
-    }
227
-
228
-
229
-    /**
230
-     * Gets item_type
231
-     *
232
-     * @return string
233
-     * @throws EE_Error
234
-     */
235
-    public function OBJ_type_i18n()
236
-    {
237
-        $obj_type = $this->OBJ_type();
238
-        switch ($obj_type) {
239
-            case 'Event':
240
-                $obj_type = __('Event', 'event_espresso');
241
-                break;
242
-            case 'Price':
243
-                $obj_type = __('Price', 'event_espresso');
244
-                break;
245
-            case 'Promotion':
246
-                $obj_type = __('Promotion', 'event_espresso');
247
-                break;
248
-            case 'Ticket':
249
-                $obj_type = __('Ticket', 'event_espresso');
250
-                break;
251
-            case 'Transaction':
252
-                $obj_type = __('Transaction', 'event_espresso');
253
-                break;
254
-        }
255
-        return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
256
-    }
257
-
258
-
259
-    /**
260
-     * Sets item_type
261
-     *
262
-     * @param string $OBJ_type
263
-     * @throws EE_Error
264
-     */
265
-    public function set_OBJ_type($OBJ_type)
266
-    {
267
-        $this->set('OBJ_type', $OBJ_type);
268
-    }
269
-
270
-
271
-    /**
272
-     * Gets unit_price
273
-     *
274
-     * @return float
275
-     * @throws EE_Error
276
-     */
277
-    public function unit_price()
278
-    {
279
-        return $this->get('LIN_unit_price');
280
-    }
281
-
282
-
283
-    /**
284
-     * Sets unit_price
285
-     *
286
-     * @param float $unit_price
287
-     * @throws EE_Error
288
-     */
289
-    public function set_unit_price($unit_price)
290
-    {
291
-        $this->set('LIN_unit_price', $unit_price);
292
-    }
293
-
294
-
295
-    /**
296
-     * Checks if this item is a percentage modifier or not
297
-     *
298
-     * @return boolean
299
-     * @throws EE_Error
300
-     */
301
-    public function is_percent()
302
-    {
303
-        if ($this->is_tax_sub_total()) {
304
-            // tax subtotals HAVE a percent on them, that percentage only applies
305
-            // to taxable items, so its' an exception. Treat it like a flat line item
306
-            return false;
307
-        }
308
-        $unit_price = abs($this->get('LIN_unit_price'));
309
-        $percent = abs($this->get('LIN_percent'));
310
-        if ($unit_price < .001 && $percent) {
311
-            return true;
312
-        }
313
-        if ($unit_price >= .001 && ! $percent) {
314
-            return false;
315
-        }
316
-        if ($unit_price >= .001 && $percent) {
317
-            throw new EE_Error(
318
-                sprintf(
319
-                    esc_html__('A Line Item can not have a unit price of (%s) AND a percent (%s)!', 'event_espresso'),
320
-                    $unit_price,
321
-                    $percent
322
-                )
323
-            );
324
-        }
325
-        // if they're both 0, assume its not a percent item
326
-        return false;
327
-    }
328
-
329
-
330
-    /**
331
-     * Gets percent (between 100-.001)
332
-     *
333
-     * @return float
334
-     * @throws EE_Error
335
-     */
336
-    public function percent()
337
-    {
338
-        return $this->get('LIN_percent');
339
-    }
340
-
341
-
342
-    /**
343
-     * Sets percent (between 100-0.01)
344
-     *
345
-     * @param float $percent
346
-     * @throws EE_Error
347
-     */
348
-    public function set_percent($percent)
349
-    {
350
-        $this->set('LIN_percent', $percent);
351
-    }
352
-
353
-
354
-    /**
355
-     * Gets total
356
-     *
357
-     * @return float
358
-     * @throws EE_Error
359
-     */
360
-    public function total()
361
-    {
362
-        return $this->get('LIN_total');
363
-    }
364
-
365
-
366
-    /**
367
-     * Sets total
368
-     *
369
-     * @param float $total
370
-     * @throws EE_Error
371
-     */
372
-    public function set_total($total)
373
-    {
374
-        $this->set('LIN_total', $total);
375
-    }
376
-
377
-
378
-    /**
379
-     * Gets order
380
-     *
381
-     * @return int
382
-     * @throws EE_Error
383
-     */
384
-    public function order()
385
-    {
386
-        return $this->get('LIN_order');
387
-    }
388
-
389
-
390
-    /**
391
-     * Sets order
392
-     *
393
-     * @param int $order
394
-     * @throws EE_Error
395
-     */
396
-    public function set_order($order)
397
-    {
398
-        $this->set('LIN_order', $order);
399
-    }
400
-
401
-
402
-    /**
403
-     * Gets parent
404
-     *
405
-     * @return int
406
-     * @throws EE_Error
407
-     */
408
-    public function parent_ID()
409
-    {
410
-        return $this->get('LIN_parent');
411
-    }
412
-
413
-
414
-    /**
415
-     * Sets parent
416
-     *
417
-     * @param int $parent
418
-     * @throws EE_Error
419
-     */
420
-    public function set_parent_ID($parent)
421
-    {
422
-        $this->set('LIN_parent', $parent);
423
-    }
424
-
425
-
426
-    /**
427
-     * Gets type
428
-     *
429
-     * @return string
430
-     * @throws EE_Error
431
-     */
432
-    public function type()
433
-    {
434
-        return $this->get('LIN_type');
435
-    }
436
-
437
-
438
-    /**
439
-     * Sets type
440
-     *
441
-     * @param string $type
442
-     * @throws EE_Error
443
-     */
444
-    public function set_type($type)
445
-    {
446
-        $this->set('LIN_type', $type);
447
-    }
448
-
449
-
450
-    /**
451
-     * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
452
-     * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
453
-     * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
454
-     * or indirectly by `EE_Line_item::add_child_line_item()`)
455
-     *
456
-     * @return EE_Base_Class|EE_Line_Item
457
-     * @throws EE_Error
458
-     */
459
-    public function parent()
460
-    {
461
-        return $this->ID()
462
-            ? $this->get_model()->get_one_by_ID($this->parent_ID())
463
-            : $this->_parent;
464
-    }
465
-
466
-
467
-    /**
468
-     * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
469
-     *
470
-     * @return EE_Base_Class[]|EE_Line_Item[]
471
-     * @throws EE_Error
472
-     */
473
-    public function children()
474
-    {
475
-        if ($this->ID()) {
476
-            return $this->get_model()->get_all(
477
-                array(
478
-                    array('LIN_parent' => $this->ID()),
479
-                    'order_by' => array('LIN_order' => 'ASC'),
480
-                )
481
-            );
482
-        }
483
-        if (! is_array($this->_children)) {
484
-            $this->_children = array();
485
-        }
486
-        return $this->_children;
487
-    }
488
-
489
-
490
-    /**
491
-     * Gets code
492
-     *
493
-     * @return string
494
-     * @throws EE_Error
495
-     */
496
-    public function code()
497
-    {
498
-        return $this->get('LIN_code');
499
-    }
500
-
501
-
502
-    /**
503
-     * Sets code
504
-     *
505
-     * @param string $code
506
-     * @throws EE_Error
507
-     */
508
-    public function set_code($code)
509
-    {
510
-        $this->set('LIN_code', $code);
511
-    }
512
-
513
-
514
-    /**
515
-     * Gets is_taxable
516
-     *
517
-     * @return boolean
518
-     * @throws EE_Error
519
-     */
520
-    public function is_taxable()
521
-    {
522
-        return $this->get('LIN_is_taxable');
523
-    }
524
-
525
-
526
-    /**
527
-     * Sets is_taxable
528
-     *
529
-     * @param boolean $is_taxable
530
-     * @throws EE_Error
531
-     */
532
-    public function set_is_taxable($is_taxable)
533
-    {
534
-        $this->set('LIN_is_taxable', $is_taxable);
535
-    }
536
-
537
-
538
-    /**
539
-     * Gets the object that this model-joins-to.
540
-     * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
541
-     * EEM_Promotion_Object
542
-     *
543
-     *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
544
-     *
545
-     * @return EE_Base_Class | NULL
546
-     * @throws EE_Error
547
-     */
548
-    public function get_object()
549
-    {
550
-        $model_name_of_related_obj = $this->OBJ_type();
551
-        return $this->get_model()->has_relation($model_name_of_related_obj)
552
-            ? $this->get_first_related($model_name_of_related_obj)
553
-            : null;
554
-    }
555
-
556
-
557
-    /**
558
-     * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
559
-     * (IE, if this line item is for a price or something else, will return NULL)
560
-     *
561
-     * @param array $query_params
562
-     * @return EE_Base_Class|EE_Ticket
563
-     * @throws EE_Error
564
-     */
565
-    public function ticket($query_params = array())
566
-    {
567
-        // we're going to assume that when this method is called we always want to receive the attached ticket EVEN if that ticket is archived.  This can be overridden via the incoming $query_params argument
568
-        $remove_defaults = array('default_where_conditions' => 'none');
569
-        $query_params = array_merge($remove_defaults, $query_params);
570
-        return $this->get_first_related('Ticket', $query_params);
571
-    }
572
-
573
-
574
-    /**
575
-     * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
576
-     *
577
-     * @return EE_Datetime | NULL
578
-     * @throws EE_Error
579
-     */
580
-    public function get_ticket_datetime()
581
-    {
582
-        if ($this->OBJ_type() === 'Ticket') {
583
-            $ticket = $this->ticket();
584
-            if ($ticket instanceof EE_Ticket) {
585
-                $datetime = $ticket->first_datetime();
586
-                if ($datetime instanceof EE_Datetime) {
587
-                    return $datetime;
588
-                }
589
-            }
590
-        }
591
-        return null;
592
-    }
593
-
594
-
595
-    /**
596
-     * Gets the event's name that's related to the ticket, if this is for
597
-     * a ticket
598
-     *
599
-     * @return string
600
-     * @throws EE_Error
601
-     */
602
-    public function ticket_event_name()
603
-    {
604
-        $event_name = esc_html__('Unknown', 'event_espresso');
605
-        $event = $this->ticket_event();
606
-        if ($event instanceof EE_Event) {
607
-            $event_name = $event->name();
608
-        }
609
-        return $event_name;
610
-    }
611
-
612
-
613
-    /**
614
-     * Gets the event that's related to the ticket, if this line item represents a ticket.
615
-     *
616
-     * @return EE_Event|null
617
-     * @throws EE_Error
618
-     */
619
-    public function ticket_event()
620
-    {
621
-        $event = null;
622
-        $ticket = $this->ticket();
623
-        if ($ticket instanceof EE_Ticket) {
624
-            $datetime = $ticket->first_datetime();
625
-            if ($datetime instanceof EE_Datetime) {
626
-                $event = $datetime->event();
627
-            }
628
-        }
629
-        return $event;
630
-    }
631
-
632
-
633
-    /**
634
-     * Gets the first datetime for this lien item, assuming it's for a ticket
635
-     *
636
-     * @param string $date_format
637
-     * @param string $time_format
638
-     * @return string
639
-     * @throws EE_Error
640
-     */
641
-    public function ticket_datetime_start($date_format = '', $time_format = '')
642
-    {
643
-        $first_datetime_string = esc_html__('Unknown', 'event_espresso');
644
-        $datetime = $this->get_ticket_datetime();
645
-        if ($datetime) {
646
-            $first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
647
-        }
648
-        return $first_datetime_string;
649
-    }
650
-
651
-
652
-    /**
653
-     * Adds the line item as a child to this line item. If there is another child line
654
-     * item with the same LIN_code, it is overwritten by this new one
655
-     *
656
-     * @param EEI_Line_Item $line_item
657
-     * @param bool          $set_order
658
-     * @return bool success
659
-     * @throws EE_Error
660
-     */
661
-    public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
662
-    {
663
-        // should we calculate the LIN_order for this line item ?
664
-        if ($set_order || $line_item->order() === null) {
665
-            $line_item->set_order(count($this->children()));
666
-        }
667
-        if ($this->ID()) {
668
-            // check for any duplicate line items (with the same code), if so, this replaces it
669
-            $line_item_with_same_code = $this->get_child_line_item($line_item->code());
670
-            if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
671
-                $this->delete_child_line_item($line_item_with_same_code->code());
672
-            }
673
-            $line_item->set_parent_ID($this->ID());
674
-            if ($this->TXN_ID()) {
675
-                $line_item->set_TXN_ID($this->TXN_ID());
676
-            }
677
-            return $line_item->save();
678
-        }
679
-        $this->_children[ $line_item->code() ] = $line_item;
680
-        if ($line_item->parent() !== $this) {
681
-            $line_item->set_parent($this);
682
-        }
683
-        return true;
684
-    }
685
-
686
-
687
-    /**
688
-     * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
689
-     * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
690
-     * However, if this line item is NOT saved to the DB, this just caches the parent on
691
-     * the EE_Line_Item::_parent property.
692
-     *
693
-     * @param EE_Line_Item $line_item
694
-     * @throws EE_Error
695
-     */
696
-    public function set_parent($line_item)
697
-    {
698
-        if ($this->ID()) {
699
-            if (! $line_item->ID()) {
700
-                $line_item->save();
701
-            }
702
-            $this->set_parent_ID($line_item->ID());
703
-            $this->save();
704
-        } else {
705
-            $this->_parent = $line_item;
706
-            $this->set_parent_ID($line_item->ID());
707
-        }
708
-    }
709
-
710
-
711
-    /**
712
-     * Gets the child line item as specified by its code. Because this returns an object (by reference)
713
-     * you can modify this child line item and the parent (this object) can know about them
714
-     * because it also has a reference to that line item
715
-     *
716
-     * @param string $code
717
-     * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
718
-     * @throws EE_Error
719
-     */
720
-    public function get_child_line_item($code)
721
-    {
722
-        if ($this->ID()) {
723
-            return $this->get_model()->get_one(
724
-                array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
725
-            );
726
-        }
727
-        return isset($this->_children[ $code ])
728
-            ? $this->_children[ $code ]
729
-            : null;
730
-    }
731
-
732
-
733
-    /**
734
-     * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
735
-     * cached on it)
736
-     *
737
-     * @return int
738
-     * @throws EE_Error
739
-     */
740
-    public function delete_children_line_items()
741
-    {
742
-        if ($this->ID()) {
743
-            return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
744
-        }
745
-        $count = count($this->_children);
746
-        $this->_children = array();
747
-        return $count;
748
-    }
749
-
750
-
751
-    /**
752
-     * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
753
-     * HAS NOT been saved to the DB, removes the child line item with index $code.
754
-     * Also searches through the child's children for a matching line item. However, once a line item has been found
755
-     * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
756
-     * deleted)
757
-     *
758
-     * @param string $code
759
-     * @param bool   $stop_search_once_found
760
-     * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
761
-     *             the DB yet)
762
-     * @throws EE_Error
763
-     */
764
-    public function delete_child_line_item($code, $stop_search_once_found = true)
765
-    {
766
-        if ($this->ID()) {
767
-            $items_deleted = 0;
768
-            if ($this->code() === $code) {
769
-                $items_deleted += EEH_Line_Item::delete_all_child_items($this);
770
-                $items_deleted += (int) $this->delete();
771
-                if ($stop_search_once_found) {
772
-                    return $items_deleted;
773
-                }
774
-            }
775
-            foreach ($this->children() as $child_line_item) {
776
-                $items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
777
-            }
778
-            return $items_deleted;
779
-        }
780
-        if (isset($this->_children[ $code ])) {
781
-            unset($this->_children[ $code ]);
782
-            return 1;
783
-        }
784
-        return 0;
785
-    }
786
-
787
-
788
-    /**
789
-     * If this line item is in the database, is of the type subtotal, and
790
-     * has no children, why do we have it? It should be deleted so this function
791
-     * does that
792
-     *
793
-     * @return boolean
794
-     * @throws EE_Error
795
-     */
796
-    public function delete_if_childless_subtotal()
797
-    {
798
-        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
799
-            return $this->delete();
800
-        }
801
-        return false;
802
-    }
803
-
804
-
805
-    /**
806
-     * Creates a code and returns a string. doesn't assign the code to this model object
807
-     *
808
-     * @return string
809
-     * @throws EE_Error
810
-     */
811
-    public function generate_code()
812
-    {
813
-        // each line item in the cart requires a unique identifier
814
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
815
-    }
816
-
817
-
818
-    /**
819
-     * @return bool
820
-     * @throws EE_Error
821
-     */
822
-    public function is_tax()
823
-    {
824
-        return $this->type() === EEM_Line_Item::type_tax;
825
-    }
826
-
827
-
828
-    /**
829
-     * @return bool
830
-     * @throws EE_Error
831
-     */
832
-    public function is_tax_sub_total()
833
-    {
834
-        return $this->type() === EEM_Line_Item::type_tax_sub_total;
835
-    }
836
-
837
-
838
-    /**
839
-     * @return bool
840
-     * @throws EE_Error
841
-     */
842
-    public function is_line_item()
843
-    {
844
-        return $this->type() === EEM_Line_Item::type_line_item;
845
-    }
846
-
847
-
848
-    /**
849
-     * @return bool
850
-     * @throws EE_Error
851
-     */
852
-    public function is_sub_line_item()
853
-    {
854
-        return $this->type() === EEM_Line_Item::type_sub_line_item;
855
-    }
856
-
857
-
858
-    /**
859
-     * @return bool
860
-     * @throws EE_Error
861
-     */
862
-    public function is_sub_total()
863
-    {
864
-        return $this->type() === EEM_Line_Item::type_sub_total;
865
-    }
866
-
867
-
868
-    /**
869
-     * Whether or not this line item is a cancellation line item
870
-     *
871
-     * @return boolean
872
-     * @throws EE_Error
873
-     */
874
-    public function is_cancellation()
875
-    {
876
-        return EEM_Line_Item::type_cancellation === $this->type();
877
-    }
878
-
879
-
880
-    /**
881
-     * @return bool
882
-     * @throws EE_Error
883
-     */
884
-    public function is_total()
885
-    {
886
-        return $this->type() === EEM_Line_Item::type_total;
887
-    }
888
-
889
-
890
-    /**
891
-     * @return bool
892
-     * @throws EE_Error
893
-     */
894
-    public function is_cancelled()
895
-    {
896
-        return $this->type() === EEM_Line_Item::type_cancellation;
897
-    }
898
-
899
-
900
-    /**
901
-     * @return string like '2, 004.00', formatted according to the localized currency
902
-     * @throws EE_Error
903
-     */
904
-    public function unit_price_no_code()
905
-    {
906
-        return $this->get_pretty('LIN_unit_price', 'no_currency_code');
907
-    }
908
-
909
-
910
-    /**
911
-     * @return string like '2, 004.00', formatted according to the localized currency
912
-     * @throws EE_Error
913
-     */
914
-    public function total_no_code()
915
-    {
916
-        return $this->get_pretty('LIN_total', 'no_currency_code');
917
-    }
918
-
919
-
920
-    /**
921
-     * Gets the final total on this item, taking taxes into account.
922
-     * Has the side-effect of setting the sub-total as it was just calculated.
923
-     * If this is used on a grand-total line item, also updates the transaction's
924
-     * TXN_total (provided this line item is allowed to persist, otherwise we don't
925
-     * want to change a persistable transaction with info from a non-persistent line item)
926
-     *
927
-     * @return float
928
-     * @throws EE_Error
929
-     * @throws InvalidArgumentException
930
-     * @throws InvalidInterfaceException
931
-     * @throws InvalidDataTypeException
932
-     */
933
-    public function recalculate_total_including_taxes()
934
-    {
935
-        $pre_tax_total = $this->recalculate_pre_tax_total();
936
-        $tax_total = $this->recalculate_taxes_and_tax_total();
937
-        $total = $pre_tax_total + $tax_total;
938
-        // no negative totals plz
939
-        $total = max($total, 0);
940
-        $this->set_total($total);
941
-        // only update the related transaction's total
942
-        // if we intend to save this line item and its a grand total
943
-        if ($this->allow_persist() && $this->type() === EEM_Line_Item::type_total
944
-            && $this->transaction()
945
-               instanceof
946
-               EE_Transaction
947
-        ) {
948
-            $this->transaction()->set_total($total);
949
-            if ($this->transaction()->ID()) {
950
-                $this->transaction()->save();
951
-            }
952
-        }
953
-        $this->maybe_save();
954
-        return $total;
955
-    }
956
-
957
-
958
-    /**
959
-     * Recursively goes through all the children and recalculates sub-totals EXCEPT for
960
-     * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
961
-     * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
962
-     * when this is called on the grand total
963
-     *
964
-     * @return float
965
-     * @throws InvalidArgumentException
966
-     * @throws InvalidInterfaceException
967
-     * @throws InvalidDataTypeException
968
-     * @throws EE_Error
969
-     */
970
-    public function recalculate_pre_tax_total()
971
-    {
972
-        $total = 0;
973
-        $my_children = $this->children();
974
-        $has_children = ! empty($my_children);
975
-        if ($has_children && $this->is_line_item()) {
976
-            $total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
977
-        } elseif (! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
978
-            $total = $this->unit_price() * $this->quantity();
979
-        } elseif ($this->is_sub_total() || $this->is_total()) {
980
-            $total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
981
-        } elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
982
-            // completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
983
-            return 0;
984
-        }
985
-        // ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
986
-        if (! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()
987
-        ) {
988
-            if ($this->OBJ_type() !== 'Event') {
989
-                $this->set_quantity(1);
990
-            }
991
-            if (! $this->is_percent()) {
992
-                $this->set_unit_price($total);
993
-            }
994
-        }
995
-        // we don't want to bother saving grand totals, because that needs to factor in taxes anyways
996
-        // so it ought to be
997
-        if (! $this->is_total()) {
998
-            $this->set_total($total);
999
-            // if not a percent line item, make sure we keep the unit price in sync
1000
-            if ($has_children
1001
-                && $this->is_line_item()
1002
-                && ! $this->is_percent()
1003
-            ) {
1004
-                if ($this->quantity() === 0) {
1005
-                    $new_unit_price = 0;
1006
-                } else {
1007
-                    $new_unit_price = $this->total() / $this->quantity();
1008
-                }
1009
-                $this->set_unit_price($new_unit_price);
1010
-            }
1011
-            $this->maybe_save();
1012
-        }
1013
-        return $total;
1014
-    }
1015
-
1016
-
1017
-    /**
1018
-     * Calculates the pretax total when this line item is a subtotal or total line item.
1019
-     * Basically does a sum-then-round approach (ie, any percent line item that are children
1020
-     * will calculate their total based on the un-rounded total we're working with so far, and
1021
-     * THEN round the result; instead of rounding as we go like with sub-line-items)
1022
-     *
1023
-     * @param float          $calculated_total_so_far
1024
-     * @param EE_Line_Item[] $my_children
1025
-     * @return float
1026
-     * @throws InvalidArgumentException
1027
-     * @throws InvalidInterfaceException
1028
-     * @throws InvalidDataTypeException
1029
-     * @throws EE_Error
1030
-     */
1031
-    protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1032
-    {
1033
-        if ($my_children === null) {
1034
-            $my_children = $this->children();
1035
-        }
1036
-        $subtotal_quantity = 0;
1037
-        // get the total of all its children
1038
-        foreach ($my_children as $child_line_item) {
1039
-            if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1040
-                // percentage line items are based on total so far
1041
-                if ($child_line_item->is_percent()) {
1042
-                    // round as we go so that the line items add up ok
1043
-                    $percent_total = round(
1044
-                        $calculated_total_so_far * $child_line_item->percent() / 100,
1045
-                        EE_Registry::instance()->CFG->currency->dec_plc
1046
-                    );
1047
-                    $child_line_item->set_total($percent_total);
1048
-                    // so far all percent line items should have a quantity of 1
1049
-                    // (ie, no double percent discounts. Although that might be requested someday)
1050
-                    $child_line_item->set_quantity(1);
1051
-                    $child_line_item->maybe_save();
1052
-                    $calculated_total_so_far += $percent_total;
1053
-                } else {
1054
-                    // verify flat sub-line-item quantities match their parent
1055
-                    if ($child_line_item->is_sub_line_item()) {
1056
-                        $child_line_item->set_quantity($this->quantity());
1057
-                    }
1058
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1059
-                    $subtotal_quantity += $child_line_item->quantity();
1060
-                }
1061
-            }
1062
-        }
1063
-        if ($this->is_sub_total()) {
1064
-            // no negative totals plz
1065
-            $calculated_total_so_far = max($calculated_total_so_far, 0);
1066
-            $subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1067
-            $this->set_quantity($subtotal_quantity);
1068
-            $this->maybe_save();
1069
-        }
1070
-        return $calculated_total_so_far;
1071
-    }
1072
-
1073
-
1074
-    /**
1075
-     * Calculates the pretax total for a normal line item, in a round-then-sum approach
1076
-     * (where each sub-line-item is applied to the base price for the line item
1077
-     * and the result is immediately rounded, rather than summing all the sub-line-items
1078
-     * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1079
-     *
1080
-     * @param float          $calculated_total_so_far
1081
-     * @param EE_Line_Item[] $my_children
1082
-     * @return float
1083
-     * @throws InvalidArgumentException
1084
-     * @throws InvalidInterfaceException
1085
-     * @throws InvalidDataTypeException
1086
-     * @throws EE_Error
1087
-     */
1088
-    protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1089
-    {
1090
-        if ($my_children === null) {
1091
-            $my_children = $this->children();
1092
-        }
1093
-        // we need to keep track of the running total for a single item,
1094
-        // because we need to round as we go
1095
-        $unit_price_for_total = 0;
1096
-        $quantity_for_total = 1;
1097
-        // get the total of all its children
1098
-        foreach ($my_children as $child_line_item) {
1099
-            if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1100
-                if ($child_line_item->is_percent()) {
1101
-                    // it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1102
-                    // not total multiplied by percent, because that ignores rounding along-the-way
1103
-                    $percent_unit_price = round(
1104
-                        $unit_price_for_total * $child_line_item->percent() / 100,
1105
-                        EE_Registry::instance()->CFG->currency->dec_plc
1106
-                    );
1107
-                    $percent_total = $percent_unit_price * $quantity_for_total;
1108
-                    $child_line_item->set_total($percent_total);
1109
-                    // so far all percent line items should have a quantity of 1
1110
-                    // (ie, no double percent discounts. Although that might be requested someday)
1111
-                    $child_line_item->set_quantity(1);
1112
-                    $child_line_item->maybe_save();
1113
-                    $calculated_total_so_far += $percent_total;
1114
-                    $unit_price_for_total += $percent_unit_price;
1115
-                } else {
1116
-                    // verify flat sub-line-item quantities match their parent
1117
-                    if ($child_line_item->is_sub_line_item()) {
1118
-                        $child_line_item->set_quantity($this->quantity());
1119
-                    }
1120
-                    $quantity_for_total = $child_line_item->quantity();
1121
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1122
-                    $unit_price_for_total += $child_line_item->unit_price();
1123
-                }
1124
-            }
1125
-        }
1126
-        return $calculated_total_so_far;
1127
-    }
1128
-
1129
-
1130
-    /**
1131
-     * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1132
-     * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1133
-     * and tax sub-total if already in the DB
1134
-     *
1135
-     * @return float
1136
-     * @throws EE_Error
1137
-     */
1138
-    public function recalculate_taxes_and_tax_total()
1139
-    {
1140
-        // get all taxes
1141
-        $taxes = $this->tax_descendants();
1142
-        // calculate the pretax total
1143
-        $taxable_total = $this->taxable_total();
1144
-        $tax_total = 0;
1145
-        foreach ($taxes as $tax) {
1146
-            $total_on_this_tax = $taxable_total * $tax->percent() / 100;
1147
-            // remember the total on this line item
1148
-            $tax->set_total($total_on_this_tax);
1149
-            $tax->maybe_save();
1150
-            $tax_total += $tax->total();
1151
-        }
1152
-        $this->_recalculate_tax_sub_total();
1153
-        return $tax_total;
1154
-    }
1155
-
1156
-
1157
-    /**
1158
-     * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1159
-     *
1160
-     * @return void
1161
-     * @throws EE_Error
1162
-     */
1163
-    private function _recalculate_tax_sub_total()
1164
-    {
1165
-        if ($this->is_tax_sub_total()) {
1166
-            $total = 0;
1167
-            $total_percent = 0;
1168
-            // simply loop through all its children (which should be taxes) and sum their total
1169
-            foreach ($this->children() as $child_tax) {
1170
-                if ($child_tax instanceof EE_Line_Item) {
1171
-                    $total += $child_tax->total();
1172
-                    $total_percent += $child_tax->percent();
1173
-                }
1174
-            }
1175
-            $this->set_total($total);
1176
-            $this->set_percent($total_percent);
1177
-            $this->maybe_save();
1178
-        } elseif ($this->is_total()) {
1179
-            foreach ($this->children() as $maybe_tax_subtotal) {
1180
-                if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1181
-                    $maybe_tax_subtotal->_recalculate_tax_sub_total();
1182
-                }
1183
-            }
1184
-        }
1185
-    }
1186
-
1187
-
1188
-    /**
1189
-     * Gets the total tax on this line item. Assumes taxes have already been calculated using
1190
-     * recalculate_taxes_and_total
1191
-     *
1192
-     * @return float
1193
-     * @throws EE_Error
1194
-     */
1195
-    public function get_total_tax()
1196
-    {
1197
-        $this->_recalculate_tax_sub_total();
1198
-        $total = 0;
1199
-        foreach ($this->tax_descendants() as $tax_line_item) {
1200
-            if ($tax_line_item instanceof EE_Line_Item) {
1201
-                $total += $tax_line_item->total();
1202
-            }
1203
-        }
1204
-        return $total;
1205
-    }
1206
-
1207
-
1208
-    /**
1209
-     * Gets the total for all the items purchased only
1210
-     *
1211
-     * @return float
1212
-     * @throws EE_Error
1213
-     */
1214
-    public function get_items_total()
1215
-    {
1216
-        // by default, let's make sure we're consistent with the existing line item
1217
-        if ($this->is_total()) {
1218
-            $pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1219
-            if ($pretax_subtotal_li instanceof EE_Line_Item) {
1220
-                return $pretax_subtotal_li->total();
1221
-            }
1222
-        }
1223
-        $total = 0;
1224
-        foreach ($this->get_items() as $item) {
1225
-            if ($item instanceof EE_Line_Item) {
1226
-                $total += $item->total();
1227
-            }
1228
-        }
1229
-        return $total;
1230
-    }
1231
-
1232
-
1233
-    /**
1234
-     * Gets all the descendants (ie, children or children of children etc) that
1235
-     * are of the type 'tax'
1236
-     *
1237
-     * @return EE_Line_Item[]
1238
-     */
1239
-    public function tax_descendants()
1240
-    {
1241
-        return EEH_Line_Item::get_tax_descendants($this);
1242
-    }
1243
-
1244
-
1245
-    /**
1246
-     * Gets all the real items purchased which are children of this item
1247
-     *
1248
-     * @return EE_Line_Item[]
1249
-     */
1250
-    public function get_items()
1251
-    {
1252
-        return EEH_Line_Item::get_line_item_descendants($this);
1253
-    }
1254
-
1255
-
1256
-    /**
1257
-     * Returns the amount taxable among this line item's children (or if it has no children,
1258
-     * how much of it is taxable). Does not recalculate totals or subtotals.
1259
-     * If the taxable total is negative, (eg, if none of the tickets were taxable,
1260
-     * but there is a "Taxable" discount), returns 0.
1261
-     *
1262
-     * @return float
1263
-     * @throws EE_Error
1264
-     */
1265
-    public function taxable_total()
1266
-    {
1267
-        $total = 0;
1268
-        if ($this->children()) {
1269
-            foreach ($this->children() as $child_line_item) {
1270
-                if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1271
-                    // if it's a percent item, only take into account the percent
1272
-                    // that's taxable too (the taxable total so far)
1273
-                    if ($child_line_item->is_percent()) {
1274
-                        $total += ($total * $child_line_item->percent() / 100);
1275
-                    } else {
1276
-                        $total += $child_line_item->total();
1277
-                    }
1278
-                } elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1279
-                    $total += $child_line_item->taxable_total();
1280
-                }
1281
-            }
1282
-        }
1283
-        return max($total, 0);
1284
-    }
1285
-
1286
-
1287
-    /**
1288
-     * Gets the transaction for this line item
1289
-     *
1290
-     * @return EE_Base_Class|EE_Transaction
1291
-     * @throws EE_Error
1292
-     */
1293
-    public function transaction()
1294
-    {
1295
-        return $this->get_first_related('Transaction');
1296
-    }
1297
-
1298
-
1299
-    /**
1300
-     * Saves this line item to the DB, and recursively saves its descendants.
1301
-     * Because there currently is no proper parent-child relation on the model,
1302
-     * save_this_and_cached() will NOT save the descendants.
1303
-     * Also sets the transaction on this line item and all its descendants before saving
1304
-     *
1305
-     * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1306
-     * @return int count of items saved
1307
-     * @throws EE_Error
1308
-     */
1309
-    public function save_this_and_descendants_to_txn($txn_id = null)
1310
-    {
1311
-        $count = 0;
1312
-        if (! $txn_id) {
1313
-            $txn_id = $this->TXN_ID();
1314
-        }
1315
-        $this->set_TXN_ID($txn_id);
1316
-        $children = $this->children();
1317
-        $count += $this->save()
1318
-            ? 1
1319
-            : 0;
1320
-        foreach ($children as $child_line_item) {
1321
-            if ($child_line_item instanceof EE_Line_Item) {
1322
-                $child_line_item->set_parent_ID($this->ID());
1323
-                $count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1324
-            }
1325
-        }
1326
-        return $count;
1327
-    }
1328
-
1329
-
1330
-    /**
1331
-     * Saves this line item to the DB, and recursively saves its descendants.
1332
-     *
1333
-     * @return int count of items saved
1334
-     * @throws EE_Error
1335
-     */
1336
-    public function save_this_and_descendants()
1337
-    {
1338
-        $count = 0;
1339
-        $children = $this->children();
1340
-        $count += $this->save()
1341
-            ? 1
1342
-            : 0;
1343
-        foreach ($children as $child_line_item) {
1344
-            if ($child_line_item instanceof EE_Line_Item) {
1345
-                $child_line_item->set_parent_ID($this->ID());
1346
-                $count += $child_line_item->save_this_and_descendants();
1347
-            }
1348
-        }
1349
-        return $count;
1350
-    }
1351
-
1352
-
1353
-    /**
1354
-     * returns the cancellation line item if this item was cancelled
1355
-     *
1356
-     * @return EE_Line_Item[]
1357
-     * @throws InvalidArgumentException
1358
-     * @throws InvalidInterfaceException
1359
-     * @throws InvalidDataTypeException
1360
-     * @throws ReflectionException
1361
-     * @throws EE_Error
1362
-     */
1363
-    public function get_cancellations()
1364
-    {
1365
-        EE_Registry::instance()->load_helper('Line_Item');
1366
-        return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1367
-    }
1368
-
1369
-
1370
-    /**
1371
-     * If this item has an ID, then this saves it again to update the db
1372
-     *
1373
-     * @return int count of items saved
1374
-     * @throws EE_Error
1375
-     */
1376
-    public function maybe_save()
1377
-    {
1378
-        if ($this->ID()) {
1379
-            return $this->save();
1380
-        }
1381
-        return false;
1382
-    }
1383
-
1384
-
1385
-    /**
1386
-     * clears the cached children and parent from the line item
1387
-     *
1388
-     * @return void
1389
-     */
1390
-    public function clear_related_line_item_cache()
1391
-    {
1392
-        $this->_children = array();
1393
-        $this->_parent = null;
1394
-    }
1395
-
1396
-
1397
-    /**
1398
-     * @param bool $raw
1399
-     * @return int
1400
-     * @throws EE_Error
1401
-     */
1402
-    public function timestamp($raw = false)
1403
-    {
1404
-        return $raw
1405
-            ? $this->get_raw('LIN_timestamp')
1406
-            : $this->get('LIN_timestamp');
1407
-    }
1408
-
1409
-
1410
-
1411
-
1412
-    /************************* DEPRECATED *************************/
1413
-    /**
1414
-     * @deprecated 4.6.0
1415
-     * @param string $type one of the constants on EEM_Line_Item
1416
-     * @return EE_Line_Item[]
1417
-     */
1418
-    protected function _get_descendants_of_type($type)
1419
-    {
1420
-        EE_Error::doing_it_wrong(
1421
-            'EE_Line_Item::_get_descendants_of_type()',
1422
-            __('Method replaced with EEH_Line_Item::get_descendants_of_type()', 'event_espresso'),
1423
-            '4.6.0'
1424
-        );
1425
-        return EEH_Line_Item::get_descendants_of_type($this, $type);
1426
-    }
1427
-
1428
-
1429
-    /**
1430
-     * @deprecated 4.6.0
1431
-     * @param string $type like one of the EEM_Line_Item::type_*
1432
-     * @return EE_Line_Item
1433
-     */
1434
-    public function get_nearest_descendant_of_type($type)
1435
-    {
1436
-        EE_Error::doing_it_wrong(
1437
-            'EE_Line_Item::get_nearest_descendant_of_type()',
1438
-            __('Method replaced with EEH_Line_Item::get_nearest_descendant_of_type()', 'event_espresso'),
1439
-            '4.6.0'
1440
-        );
1441
-        return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1442
-    }
17
+	/**
18
+	 * for children line items (currently not a normal relation)
19
+	 *
20
+	 * @type EE_Line_Item[]
21
+	 */
22
+	protected $_children = array();
23
+
24
+	/**
25
+	 * for the parent line item
26
+	 *
27
+	 * @var EE_Line_Item
28
+	 */
29
+	protected $_parent;
30
+
31
+
32
+	/**
33
+	 *
34
+	 * @param array  $props_n_values          incoming values
35
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
36
+	 *                                        used.)
37
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
38
+	 *                                        date_format and the second value is the time format
39
+	 * @return EE_Line_Item
40
+	 * @throws EE_Error
41
+	 */
42
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
43
+	{
44
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
45
+		return $has_object
46
+			? $has_object
47
+			: new self($props_n_values, false, $timezone);
48
+	}
49
+
50
+
51
+	/**
52
+	 * @param array  $props_n_values  incoming values from the database
53
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
54
+	 *                                the website will be used.
55
+	 * @return EE_Line_Item
56
+	 * @throws EE_Error
57
+	 */
58
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
59
+	{
60
+		return new self($props_n_values, true, $timezone);
61
+	}
62
+
63
+
64
+	/**
65
+	 * Adds some defaults if they're not specified
66
+	 *
67
+	 * @param array  $fieldValues
68
+	 * @param bool   $bydb
69
+	 * @param string $timezone
70
+	 * @throws EE_Error
71
+	 */
72
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
73
+	{
74
+		parent::__construct($fieldValues, $bydb, $timezone);
75
+		if (! $this->get('LIN_code')) {
76
+			$this->set_code($this->generate_code());
77
+		}
78
+	}
79
+
80
+
81
+	/**
82
+	 * Gets ID
83
+	 *
84
+	 * @return int
85
+	 * @throws EE_Error
86
+	 */
87
+	public function ID()
88
+	{
89
+		return $this->get('LIN_ID');
90
+	}
91
+
92
+
93
+	/**
94
+	 * Gets TXN_ID
95
+	 *
96
+	 * @return int
97
+	 * @throws EE_Error
98
+	 */
99
+	public function TXN_ID()
100
+	{
101
+		return $this->get('TXN_ID');
102
+	}
103
+
104
+
105
+	/**
106
+	 * Sets TXN_ID
107
+	 *
108
+	 * @param int $TXN_ID
109
+	 * @throws EE_Error
110
+	 */
111
+	public function set_TXN_ID($TXN_ID)
112
+	{
113
+		$this->set('TXN_ID', $TXN_ID);
114
+	}
115
+
116
+
117
+	/**
118
+	 * Gets name
119
+	 *
120
+	 * @return string
121
+	 * @throws EE_Error
122
+	 */
123
+	public function name()
124
+	{
125
+		$name = $this->get('LIN_name');
126
+		if (! $name) {
127
+			$name = ucwords(str_replace('-', ' ', $this->type()));
128
+		}
129
+		return $name;
130
+	}
131
+
132
+
133
+	/**
134
+	 * Sets name
135
+	 *
136
+	 * @param string $name
137
+	 * @throws EE_Error
138
+	 */
139
+	public function set_name($name)
140
+	{
141
+		$this->set('LIN_name', $name);
142
+	}
143
+
144
+
145
+	/**
146
+	 * Gets desc
147
+	 *
148
+	 * @return string
149
+	 * @throws EE_Error
150
+	 */
151
+	public function desc()
152
+	{
153
+		return $this->get('LIN_desc');
154
+	}
155
+
156
+
157
+	/**
158
+	 * Sets desc
159
+	 *
160
+	 * @param string $desc
161
+	 * @throws EE_Error
162
+	 */
163
+	public function set_desc($desc)
164
+	{
165
+		$this->set('LIN_desc', $desc);
166
+	}
167
+
168
+
169
+	/**
170
+	 * Gets quantity
171
+	 *
172
+	 * @return int
173
+	 * @throws EE_Error
174
+	 */
175
+	public function quantity()
176
+	{
177
+		return $this->get('LIN_quantity');
178
+	}
179
+
180
+
181
+	/**
182
+	 * Sets quantity
183
+	 *
184
+	 * @param int $quantity
185
+	 * @throws EE_Error
186
+	 */
187
+	public function set_quantity($quantity)
188
+	{
189
+		$this->set('LIN_quantity', max($quantity, 0));
190
+	}
191
+
192
+
193
+	/**
194
+	 * Gets item_id
195
+	 *
196
+	 * @return string
197
+	 * @throws EE_Error
198
+	 */
199
+	public function OBJ_ID()
200
+	{
201
+		return $this->get('OBJ_ID');
202
+	}
203
+
204
+
205
+	/**
206
+	 * Sets item_id
207
+	 *
208
+	 * @param string $item_id
209
+	 * @throws EE_Error
210
+	 */
211
+	public function set_OBJ_ID($item_id)
212
+	{
213
+		$this->set('OBJ_ID', $item_id);
214
+	}
215
+
216
+
217
+	/**
218
+	 * Gets item_type
219
+	 *
220
+	 * @return string
221
+	 * @throws EE_Error
222
+	 */
223
+	public function OBJ_type()
224
+	{
225
+		return $this->get('OBJ_type');
226
+	}
227
+
228
+
229
+	/**
230
+	 * Gets item_type
231
+	 *
232
+	 * @return string
233
+	 * @throws EE_Error
234
+	 */
235
+	public function OBJ_type_i18n()
236
+	{
237
+		$obj_type = $this->OBJ_type();
238
+		switch ($obj_type) {
239
+			case 'Event':
240
+				$obj_type = __('Event', 'event_espresso');
241
+				break;
242
+			case 'Price':
243
+				$obj_type = __('Price', 'event_espresso');
244
+				break;
245
+			case 'Promotion':
246
+				$obj_type = __('Promotion', 'event_espresso');
247
+				break;
248
+			case 'Ticket':
249
+				$obj_type = __('Ticket', 'event_espresso');
250
+				break;
251
+			case 'Transaction':
252
+				$obj_type = __('Transaction', 'event_espresso');
253
+				break;
254
+		}
255
+		return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
256
+	}
257
+
258
+
259
+	/**
260
+	 * Sets item_type
261
+	 *
262
+	 * @param string $OBJ_type
263
+	 * @throws EE_Error
264
+	 */
265
+	public function set_OBJ_type($OBJ_type)
266
+	{
267
+		$this->set('OBJ_type', $OBJ_type);
268
+	}
269
+
270
+
271
+	/**
272
+	 * Gets unit_price
273
+	 *
274
+	 * @return float
275
+	 * @throws EE_Error
276
+	 */
277
+	public function unit_price()
278
+	{
279
+		return $this->get('LIN_unit_price');
280
+	}
281
+
282
+
283
+	/**
284
+	 * Sets unit_price
285
+	 *
286
+	 * @param float $unit_price
287
+	 * @throws EE_Error
288
+	 */
289
+	public function set_unit_price($unit_price)
290
+	{
291
+		$this->set('LIN_unit_price', $unit_price);
292
+	}
293
+
294
+
295
+	/**
296
+	 * Checks if this item is a percentage modifier or not
297
+	 *
298
+	 * @return boolean
299
+	 * @throws EE_Error
300
+	 */
301
+	public function is_percent()
302
+	{
303
+		if ($this->is_tax_sub_total()) {
304
+			// tax subtotals HAVE a percent on them, that percentage only applies
305
+			// to taxable items, so its' an exception. Treat it like a flat line item
306
+			return false;
307
+		}
308
+		$unit_price = abs($this->get('LIN_unit_price'));
309
+		$percent = abs($this->get('LIN_percent'));
310
+		if ($unit_price < .001 && $percent) {
311
+			return true;
312
+		}
313
+		if ($unit_price >= .001 && ! $percent) {
314
+			return false;
315
+		}
316
+		if ($unit_price >= .001 && $percent) {
317
+			throw new EE_Error(
318
+				sprintf(
319
+					esc_html__('A Line Item can not have a unit price of (%s) AND a percent (%s)!', 'event_espresso'),
320
+					$unit_price,
321
+					$percent
322
+				)
323
+			);
324
+		}
325
+		// if they're both 0, assume its not a percent item
326
+		return false;
327
+	}
328
+
329
+
330
+	/**
331
+	 * Gets percent (between 100-.001)
332
+	 *
333
+	 * @return float
334
+	 * @throws EE_Error
335
+	 */
336
+	public function percent()
337
+	{
338
+		return $this->get('LIN_percent');
339
+	}
340
+
341
+
342
+	/**
343
+	 * Sets percent (between 100-0.01)
344
+	 *
345
+	 * @param float $percent
346
+	 * @throws EE_Error
347
+	 */
348
+	public function set_percent($percent)
349
+	{
350
+		$this->set('LIN_percent', $percent);
351
+	}
352
+
353
+
354
+	/**
355
+	 * Gets total
356
+	 *
357
+	 * @return float
358
+	 * @throws EE_Error
359
+	 */
360
+	public function total()
361
+	{
362
+		return $this->get('LIN_total');
363
+	}
364
+
365
+
366
+	/**
367
+	 * Sets total
368
+	 *
369
+	 * @param float $total
370
+	 * @throws EE_Error
371
+	 */
372
+	public function set_total($total)
373
+	{
374
+		$this->set('LIN_total', $total);
375
+	}
376
+
377
+
378
+	/**
379
+	 * Gets order
380
+	 *
381
+	 * @return int
382
+	 * @throws EE_Error
383
+	 */
384
+	public function order()
385
+	{
386
+		return $this->get('LIN_order');
387
+	}
388
+
389
+
390
+	/**
391
+	 * Sets order
392
+	 *
393
+	 * @param int $order
394
+	 * @throws EE_Error
395
+	 */
396
+	public function set_order($order)
397
+	{
398
+		$this->set('LIN_order', $order);
399
+	}
400
+
401
+
402
+	/**
403
+	 * Gets parent
404
+	 *
405
+	 * @return int
406
+	 * @throws EE_Error
407
+	 */
408
+	public function parent_ID()
409
+	{
410
+		return $this->get('LIN_parent');
411
+	}
412
+
413
+
414
+	/**
415
+	 * Sets parent
416
+	 *
417
+	 * @param int $parent
418
+	 * @throws EE_Error
419
+	 */
420
+	public function set_parent_ID($parent)
421
+	{
422
+		$this->set('LIN_parent', $parent);
423
+	}
424
+
425
+
426
+	/**
427
+	 * Gets type
428
+	 *
429
+	 * @return string
430
+	 * @throws EE_Error
431
+	 */
432
+	public function type()
433
+	{
434
+		return $this->get('LIN_type');
435
+	}
436
+
437
+
438
+	/**
439
+	 * Sets type
440
+	 *
441
+	 * @param string $type
442
+	 * @throws EE_Error
443
+	 */
444
+	public function set_type($type)
445
+	{
446
+		$this->set('LIN_type', $type);
447
+	}
448
+
449
+
450
+	/**
451
+	 * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
452
+	 * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
453
+	 * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
454
+	 * or indirectly by `EE_Line_item::add_child_line_item()`)
455
+	 *
456
+	 * @return EE_Base_Class|EE_Line_Item
457
+	 * @throws EE_Error
458
+	 */
459
+	public function parent()
460
+	{
461
+		return $this->ID()
462
+			? $this->get_model()->get_one_by_ID($this->parent_ID())
463
+			: $this->_parent;
464
+	}
465
+
466
+
467
+	/**
468
+	 * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
469
+	 *
470
+	 * @return EE_Base_Class[]|EE_Line_Item[]
471
+	 * @throws EE_Error
472
+	 */
473
+	public function children()
474
+	{
475
+		if ($this->ID()) {
476
+			return $this->get_model()->get_all(
477
+				array(
478
+					array('LIN_parent' => $this->ID()),
479
+					'order_by' => array('LIN_order' => 'ASC'),
480
+				)
481
+			);
482
+		}
483
+		if (! is_array($this->_children)) {
484
+			$this->_children = array();
485
+		}
486
+		return $this->_children;
487
+	}
488
+
489
+
490
+	/**
491
+	 * Gets code
492
+	 *
493
+	 * @return string
494
+	 * @throws EE_Error
495
+	 */
496
+	public function code()
497
+	{
498
+		return $this->get('LIN_code');
499
+	}
500
+
501
+
502
+	/**
503
+	 * Sets code
504
+	 *
505
+	 * @param string $code
506
+	 * @throws EE_Error
507
+	 */
508
+	public function set_code($code)
509
+	{
510
+		$this->set('LIN_code', $code);
511
+	}
512
+
513
+
514
+	/**
515
+	 * Gets is_taxable
516
+	 *
517
+	 * @return boolean
518
+	 * @throws EE_Error
519
+	 */
520
+	public function is_taxable()
521
+	{
522
+		return $this->get('LIN_is_taxable');
523
+	}
524
+
525
+
526
+	/**
527
+	 * Sets is_taxable
528
+	 *
529
+	 * @param boolean $is_taxable
530
+	 * @throws EE_Error
531
+	 */
532
+	public function set_is_taxable($is_taxable)
533
+	{
534
+		$this->set('LIN_is_taxable', $is_taxable);
535
+	}
536
+
537
+
538
+	/**
539
+	 * Gets the object that this model-joins-to.
540
+	 * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
541
+	 * EEM_Promotion_Object
542
+	 *
543
+	 *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
544
+	 *
545
+	 * @return EE_Base_Class | NULL
546
+	 * @throws EE_Error
547
+	 */
548
+	public function get_object()
549
+	{
550
+		$model_name_of_related_obj = $this->OBJ_type();
551
+		return $this->get_model()->has_relation($model_name_of_related_obj)
552
+			? $this->get_first_related($model_name_of_related_obj)
553
+			: null;
554
+	}
555
+
556
+
557
+	/**
558
+	 * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
559
+	 * (IE, if this line item is for a price or something else, will return NULL)
560
+	 *
561
+	 * @param array $query_params
562
+	 * @return EE_Base_Class|EE_Ticket
563
+	 * @throws EE_Error
564
+	 */
565
+	public function ticket($query_params = array())
566
+	{
567
+		// we're going to assume that when this method is called we always want to receive the attached ticket EVEN if that ticket is archived.  This can be overridden via the incoming $query_params argument
568
+		$remove_defaults = array('default_where_conditions' => 'none');
569
+		$query_params = array_merge($remove_defaults, $query_params);
570
+		return $this->get_first_related('Ticket', $query_params);
571
+	}
572
+
573
+
574
+	/**
575
+	 * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
576
+	 *
577
+	 * @return EE_Datetime | NULL
578
+	 * @throws EE_Error
579
+	 */
580
+	public function get_ticket_datetime()
581
+	{
582
+		if ($this->OBJ_type() === 'Ticket') {
583
+			$ticket = $this->ticket();
584
+			if ($ticket instanceof EE_Ticket) {
585
+				$datetime = $ticket->first_datetime();
586
+				if ($datetime instanceof EE_Datetime) {
587
+					return $datetime;
588
+				}
589
+			}
590
+		}
591
+		return null;
592
+	}
593
+
594
+
595
+	/**
596
+	 * Gets the event's name that's related to the ticket, if this is for
597
+	 * a ticket
598
+	 *
599
+	 * @return string
600
+	 * @throws EE_Error
601
+	 */
602
+	public function ticket_event_name()
603
+	{
604
+		$event_name = esc_html__('Unknown', 'event_espresso');
605
+		$event = $this->ticket_event();
606
+		if ($event instanceof EE_Event) {
607
+			$event_name = $event->name();
608
+		}
609
+		return $event_name;
610
+	}
611
+
612
+
613
+	/**
614
+	 * Gets the event that's related to the ticket, if this line item represents a ticket.
615
+	 *
616
+	 * @return EE_Event|null
617
+	 * @throws EE_Error
618
+	 */
619
+	public function ticket_event()
620
+	{
621
+		$event = null;
622
+		$ticket = $this->ticket();
623
+		if ($ticket instanceof EE_Ticket) {
624
+			$datetime = $ticket->first_datetime();
625
+			if ($datetime instanceof EE_Datetime) {
626
+				$event = $datetime->event();
627
+			}
628
+		}
629
+		return $event;
630
+	}
631
+
632
+
633
+	/**
634
+	 * Gets the first datetime for this lien item, assuming it's for a ticket
635
+	 *
636
+	 * @param string $date_format
637
+	 * @param string $time_format
638
+	 * @return string
639
+	 * @throws EE_Error
640
+	 */
641
+	public function ticket_datetime_start($date_format = '', $time_format = '')
642
+	{
643
+		$first_datetime_string = esc_html__('Unknown', 'event_espresso');
644
+		$datetime = $this->get_ticket_datetime();
645
+		if ($datetime) {
646
+			$first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
647
+		}
648
+		return $first_datetime_string;
649
+	}
650
+
651
+
652
+	/**
653
+	 * Adds the line item as a child to this line item. If there is another child line
654
+	 * item with the same LIN_code, it is overwritten by this new one
655
+	 *
656
+	 * @param EEI_Line_Item $line_item
657
+	 * @param bool          $set_order
658
+	 * @return bool success
659
+	 * @throws EE_Error
660
+	 */
661
+	public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
662
+	{
663
+		// should we calculate the LIN_order for this line item ?
664
+		if ($set_order || $line_item->order() === null) {
665
+			$line_item->set_order(count($this->children()));
666
+		}
667
+		if ($this->ID()) {
668
+			// check for any duplicate line items (with the same code), if so, this replaces it
669
+			$line_item_with_same_code = $this->get_child_line_item($line_item->code());
670
+			if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
671
+				$this->delete_child_line_item($line_item_with_same_code->code());
672
+			}
673
+			$line_item->set_parent_ID($this->ID());
674
+			if ($this->TXN_ID()) {
675
+				$line_item->set_TXN_ID($this->TXN_ID());
676
+			}
677
+			return $line_item->save();
678
+		}
679
+		$this->_children[ $line_item->code() ] = $line_item;
680
+		if ($line_item->parent() !== $this) {
681
+			$line_item->set_parent($this);
682
+		}
683
+		return true;
684
+	}
685
+
686
+
687
+	/**
688
+	 * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
689
+	 * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
690
+	 * However, if this line item is NOT saved to the DB, this just caches the parent on
691
+	 * the EE_Line_Item::_parent property.
692
+	 *
693
+	 * @param EE_Line_Item $line_item
694
+	 * @throws EE_Error
695
+	 */
696
+	public function set_parent($line_item)
697
+	{
698
+		if ($this->ID()) {
699
+			if (! $line_item->ID()) {
700
+				$line_item->save();
701
+			}
702
+			$this->set_parent_ID($line_item->ID());
703
+			$this->save();
704
+		} else {
705
+			$this->_parent = $line_item;
706
+			$this->set_parent_ID($line_item->ID());
707
+		}
708
+	}
709
+
710
+
711
+	/**
712
+	 * Gets the child line item as specified by its code. Because this returns an object (by reference)
713
+	 * you can modify this child line item and the parent (this object) can know about them
714
+	 * because it also has a reference to that line item
715
+	 *
716
+	 * @param string $code
717
+	 * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
718
+	 * @throws EE_Error
719
+	 */
720
+	public function get_child_line_item($code)
721
+	{
722
+		if ($this->ID()) {
723
+			return $this->get_model()->get_one(
724
+				array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
725
+			);
726
+		}
727
+		return isset($this->_children[ $code ])
728
+			? $this->_children[ $code ]
729
+			: null;
730
+	}
731
+
732
+
733
+	/**
734
+	 * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
735
+	 * cached on it)
736
+	 *
737
+	 * @return int
738
+	 * @throws EE_Error
739
+	 */
740
+	public function delete_children_line_items()
741
+	{
742
+		if ($this->ID()) {
743
+			return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
744
+		}
745
+		$count = count($this->_children);
746
+		$this->_children = array();
747
+		return $count;
748
+	}
749
+
750
+
751
+	/**
752
+	 * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
753
+	 * HAS NOT been saved to the DB, removes the child line item with index $code.
754
+	 * Also searches through the child's children for a matching line item. However, once a line item has been found
755
+	 * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
756
+	 * deleted)
757
+	 *
758
+	 * @param string $code
759
+	 * @param bool   $stop_search_once_found
760
+	 * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
761
+	 *             the DB yet)
762
+	 * @throws EE_Error
763
+	 */
764
+	public function delete_child_line_item($code, $stop_search_once_found = true)
765
+	{
766
+		if ($this->ID()) {
767
+			$items_deleted = 0;
768
+			if ($this->code() === $code) {
769
+				$items_deleted += EEH_Line_Item::delete_all_child_items($this);
770
+				$items_deleted += (int) $this->delete();
771
+				if ($stop_search_once_found) {
772
+					return $items_deleted;
773
+				}
774
+			}
775
+			foreach ($this->children() as $child_line_item) {
776
+				$items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
777
+			}
778
+			return $items_deleted;
779
+		}
780
+		if (isset($this->_children[ $code ])) {
781
+			unset($this->_children[ $code ]);
782
+			return 1;
783
+		}
784
+		return 0;
785
+	}
786
+
787
+
788
+	/**
789
+	 * If this line item is in the database, is of the type subtotal, and
790
+	 * has no children, why do we have it? It should be deleted so this function
791
+	 * does that
792
+	 *
793
+	 * @return boolean
794
+	 * @throws EE_Error
795
+	 */
796
+	public function delete_if_childless_subtotal()
797
+	{
798
+		if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
799
+			return $this->delete();
800
+		}
801
+		return false;
802
+	}
803
+
804
+
805
+	/**
806
+	 * Creates a code and returns a string. doesn't assign the code to this model object
807
+	 *
808
+	 * @return string
809
+	 * @throws EE_Error
810
+	 */
811
+	public function generate_code()
812
+	{
813
+		// each line item in the cart requires a unique identifier
814
+		return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
815
+	}
816
+
817
+
818
+	/**
819
+	 * @return bool
820
+	 * @throws EE_Error
821
+	 */
822
+	public function is_tax()
823
+	{
824
+		return $this->type() === EEM_Line_Item::type_tax;
825
+	}
826
+
827
+
828
+	/**
829
+	 * @return bool
830
+	 * @throws EE_Error
831
+	 */
832
+	public function is_tax_sub_total()
833
+	{
834
+		return $this->type() === EEM_Line_Item::type_tax_sub_total;
835
+	}
836
+
837
+
838
+	/**
839
+	 * @return bool
840
+	 * @throws EE_Error
841
+	 */
842
+	public function is_line_item()
843
+	{
844
+		return $this->type() === EEM_Line_Item::type_line_item;
845
+	}
846
+
847
+
848
+	/**
849
+	 * @return bool
850
+	 * @throws EE_Error
851
+	 */
852
+	public function is_sub_line_item()
853
+	{
854
+		return $this->type() === EEM_Line_Item::type_sub_line_item;
855
+	}
856
+
857
+
858
+	/**
859
+	 * @return bool
860
+	 * @throws EE_Error
861
+	 */
862
+	public function is_sub_total()
863
+	{
864
+		return $this->type() === EEM_Line_Item::type_sub_total;
865
+	}
866
+
867
+
868
+	/**
869
+	 * Whether or not this line item is a cancellation line item
870
+	 *
871
+	 * @return boolean
872
+	 * @throws EE_Error
873
+	 */
874
+	public function is_cancellation()
875
+	{
876
+		return EEM_Line_Item::type_cancellation === $this->type();
877
+	}
878
+
879
+
880
+	/**
881
+	 * @return bool
882
+	 * @throws EE_Error
883
+	 */
884
+	public function is_total()
885
+	{
886
+		return $this->type() === EEM_Line_Item::type_total;
887
+	}
888
+
889
+
890
+	/**
891
+	 * @return bool
892
+	 * @throws EE_Error
893
+	 */
894
+	public function is_cancelled()
895
+	{
896
+		return $this->type() === EEM_Line_Item::type_cancellation;
897
+	}
898
+
899
+
900
+	/**
901
+	 * @return string like '2, 004.00', formatted according to the localized currency
902
+	 * @throws EE_Error
903
+	 */
904
+	public function unit_price_no_code()
905
+	{
906
+		return $this->get_pretty('LIN_unit_price', 'no_currency_code');
907
+	}
908
+
909
+
910
+	/**
911
+	 * @return string like '2, 004.00', formatted according to the localized currency
912
+	 * @throws EE_Error
913
+	 */
914
+	public function total_no_code()
915
+	{
916
+		return $this->get_pretty('LIN_total', 'no_currency_code');
917
+	}
918
+
919
+
920
+	/**
921
+	 * Gets the final total on this item, taking taxes into account.
922
+	 * Has the side-effect of setting the sub-total as it was just calculated.
923
+	 * If this is used on a grand-total line item, also updates the transaction's
924
+	 * TXN_total (provided this line item is allowed to persist, otherwise we don't
925
+	 * want to change a persistable transaction with info from a non-persistent line item)
926
+	 *
927
+	 * @return float
928
+	 * @throws EE_Error
929
+	 * @throws InvalidArgumentException
930
+	 * @throws InvalidInterfaceException
931
+	 * @throws InvalidDataTypeException
932
+	 */
933
+	public function recalculate_total_including_taxes()
934
+	{
935
+		$pre_tax_total = $this->recalculate_pre_tax_total();
936
+		$tax_total = $this->recalculate_taxes_and_tax_total();
937
+		$total = $pre_tax_total + $tax_total;
938
+		// no negative totals plz
939
+		$total = max($total, 0);
940
+		$this->set_total($total);
941
+		// only update the related transaction's total
942
+		// if we intend to save this line item and its a grand total
943
+		if ($this->allow_persist() && $this->type() === EEM_Line_Item::type_total
944
+			&& $this->transaction()
945
+			   instanceof
946
+			   EE_Transaction
947
+		) {
948
+			$this->transaction()->set_total($total);
949
+			if ($this->transaction()->ID()) {
950
+				$this->transaction()->save();
951
+			}
952
+		}
953
+		$this->maybe_save();
954
+		return $total;
955
+	}
956
+
957
+
958
+	/**
959
+	 * Recursively goes through all the children and recalculates sub-totals EXCEPT for
960
+	 * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
961
+	 * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
962
+	 * when this is called on the grand total
963
+	 *
964
+	 * @return float
965
+	 * @throws InvalidArgumentException
966
+	 * @throws InvalidInterfaceException
967
+	 * @throws InvalidDataTypeException
968
+	 * @throws EE_Error
969
+	 */
970
+	public function recalculate_pre_tax_total()
971
+	{
972
+		$total = 0;
973
+		$my_children = $this->children();
974
+		$has_children = ! empty($my_children);
975
+		if ($has_children && $this->is_line_item()) {
976
+			$total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
977
+		} elseif (! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
978
+			$total = $this->unit_price() * $this->quantity();
979
+		} elseif ($this->is_sub_total() || $this->is_total()) {
980
+			$total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
981
+		} elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
982
+			// completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
983
+			return 0;
984
+		}
985
+		// ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
986
+		if (! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()
987
+		) {
988
+			if ($this->OBJ_type() !== 'Event') {
989
+				$this->set_quantity(1);
990
+			}
991
+			if (! $this->is_percent()) {
992
+				$this->set_unit_price($total);
993
+			}
994
+		}
995
+		// we don't want to bother saving grand totals, because that needs to factor in taxes anyways
996
+		// so it ought to be
997
+		if (! $this->is_total()) {
998
+			$this->set_total($total);
999
+			// if not a percent line item, make sure we keep the unit price in sync
1000
+			if ($has_children
1001
+				&& $this->is_line_item()
1002
+				&& ! $this->is_percent()
1003
+			) {
1004
+				if ($this->quantity() === 0) {
1005
+					$new_unit_price = 0;
1006
+				} else {
1007
+					$new_unit_price = $this->total() / $this->quantity();
1008
+				}
1009
+				$this->set_unit_price($new_unit_price);
1010
+			}
1011
+			$this->maybe_save();
1012
+		}
1013
+		return $total;
1014
+	}
1015
+
1016
+
1017
+	/**
1018
+	 * Calculates the pretax total when this line item is a subtotal or total line item.
1019
+	 * Basically does a sum-then-round approach (ie, any percent line item that are children
1020
+	 * will calculate their total based on the un-rounded total we're working with so far, and
1021
+	 * THEN round the result; instead of rounding as we go like with sub-line-items)
1022
+	 *
1023
+	 * @param float          $calculated_total_so_far
1024
+	 * @param EE_Line_Item[] $my_children
1025
+	 * @return float
1026
+	 * @throws InvalidArgumentException
1027
+	 * @throws InvalidInterfaceException
1028
+	 * @throws InvalidDataTypeException
1029
+	 * @throws EE_Error
1030
+	 */
1031
+	protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1032
+	{
1033
+		if ($my_children === null) {
1034
+			$my_children = $this->children();
1035
+		}
1036
+		$subtotal_quantity = 0;
1037
+		// get the total of all its children
1038
+		foreach ($my_children as $child_line_item) {
1039
+			if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1040
+				// percentage line items are based on total so far
1041
+				if ($child_line_item->is_percent()) {
1042
+					// round as we go so that the line items add up ok
1043
+					$percent_total = round(
1044
+						$calculated_total_so_far * $child_line_item->percent() / 100,
1045
+						EE_Registry::instance()->CFG->currency->dec_plc
1046
+					);
1047
+					$child_line_item->set_total($percent_total);
1048
+					// so far all percent line items should have a quantity of 1
1049
+					// (ie, no double percent discounts. Although that might be requested someday)
1050
+					$child_line_item->set_quantity(1);
1051
+					$child_line_item->maybe_save();
1052
+					$calculated_total_so_far += $percent_total;
1053
+				} else {
1054
+					// verify flat sub-line-item quantities match their parent
1055
+					if ($child_line_item->is_sub_line_item()) {
1056
+						$child_line_item->set_quantity($this->quantity());
1057
+					}
1058
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1059
+					$subtotal_quantity += $child_line_item->quantity();
1060
+				}
1061
+			}
1062
+		}
1063
+		if ($this->is_sub_total()) {
1064
+			// no negative totals plz
1065
+			$calculated_total_so_far = max($calculated_total_so_far, 0);
1066
+			$subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1067
+			$this->set_quantity($subtotal_quantity);
1068
+			$this->maybe_save();
1069
+		}
1070
+		return $calculated_total_so_far;
1071
+	}
1072
+
1073
+
1074
+	/**
1075
+	 * Calculates the pretax total for a normal line item, in a round-then-sum approach
1076
+	 * (where each sub-line-item is applied to the base price for the line item
1077
+	 * and the result is immediately rounded, rather than summing all the sub-line-items
1078
+	 * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1079
+	 *
1080
+	 * @param float          $calculated_total_so_far
1081
+	 * @param EE_Line_Item[] $my_children
1082
+	 * @return float
1083
+	 * @throws InvalidArgumentException
1084
+	 * @throws InvalidInterfaceException
1085
+	 * @throws InvalidDataTypeException
1086
+	 * @throws EE_Error
1087
+	 */
1088
+	protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1089
+	{
1090
+		if ($my_children === null) {
1091
+			$my_children = $this->children();
1092
+		}
1093
+		// we need to keep track of the running total for a single item,
1094
+		// because we need to round as we go
1095
+		$unit_price_for_total = 0;
1096
+		$quantity_for_total = 1;
1097
+		// get the total of all its children
1098
+		foreach ($my_children as $child_line_item) {
1099
+			if ($child_line_item instanceof EE_Line_Item && ! $child_line_item->is_cancellation()) {
1100
+				if ($child_line_item->is_percent()) {
1101
+					// it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1102
+					// not total multiplied by percent, because that ignores rounding along-the-way
1103
+					$percent_unit_price = round(
1104
+						$unit_price_for_total * $child_line_item->percent() / 100,
1105
+						EE_Registry::instance()->CFG->currency->dec_plc
1106
+					);
1107
+					$percent_total = $percent_unit_price * $quantity_for_total;
1108
+					$child_line_item->set_total($percent_total);
1109
+					// so far all percent line items should have a quantity of 1
1110
+					// (ie, no double percent discounts. Although that might be requested someday)
1111
+					$child_line_item->set_quantity(1);
1112
+					$child_line_item->maybe_save();
1113
+					$calculated_total_so_far += $percent_total;
1114
+					$unit_price_for_total += $percent_unit_price;
1115
+				} else {
1116
+					// verify flat sub-line-item quantities match their parent
1117
+					if ($child_line_item->is_sub_line_item()) {
1118
+						$child_line_item->set_quantity($this->quantity());
1119
+					}
1120
+					$quantity_for_total = $child_line_item->quantity();
1121
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1122
+					$unit_price_for_total += $child_line_item->unit_price();
1123
+				}
1124
+			}
1125
+		}
1126
+		return $calculated_total_so_far;
1127
+	}
1128
+
1129
+
1130
+	/**
1131
+	 * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1132
+	 * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1133
+	 * and tax sub-total if already in the DB
1134
+	 *
1135
+	 * @return float
1136
+	 * @throws EE_Error
1137
+	 */
1138
+	public function recalculate_taxes_and_tax_total()
1139
+	{
1140
+		// get all taxes
1141
+		$taxes = $this->tax_descendants();
1142
+		// calculate the pretax total
1143
+		$taxable_total = $this->taxable_total();
1144
+		$tax_total = 0;
1145
+		foreach ($taxes as $tax) {
1146
+			$total_on_this_tax = $taxable_total * $tax->percent() / 100;
1147
+			// remember the total on this line item
1148
+			$tax->set_total($total_on_this_tax);
1149
+			$tax->maybe_save();
1150
+			$tax_total += $tax->total();
1151
+		}
1152
+		$this->_recalculate_tax_sub_total();
1153
+		return $tax_total;
1154
+	}
1155
+
1156
+
1157
+	/**
1158
+	 * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1159
+	 *
1160
+	 * @return void
1161
+	 * @throws EE_Error
1162
+	 */
1163
+	private function _recalculate_tax_sub_total()
1164
+	{
1165
+		if ($this->is_tax_sub_total()) {
1166
+			$total = 0;
1167
+			$total_percent = 0;
1168
+			// simply loop through all its children (which should be taxes) and sum their total
1169
+			foreach ($this->children() as $child_tax) {
1170
+				if ($child_tax instanceof EE_Line_Item) {
1171
+					$total += $child_tax->total();
1172
+					$total_percent += $child_tax->percent();
1173
+				}
1174
+			}
1175
+			$this->set_total($total);
1176
+			$this->set_percent($total_percent);
1177
+			$this->maybe_save();
1178
+		} elseif ($this->is_total()) {
1179
+			foreach ($this->children() as $maybe_tax_subtotal) {
1180
+				if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1181
+					$maybe_tax_subtotal->_recalculate_tax_sub_total();
1182
+				}
1183
+			}
1184
+		}
1185
+	}
1186
+
1187
+
1188
+	/**
1189
+	 * Gets the total tax on this line item. Assumes taxes have already been calculated using
1190
+	 * recalculate_taxes_and_total
1191
+	 *
1192
+	 * @return float
1193
+	 * @throws EE_Error
1194
+	 */
1195
+	public function get_total_tax()
1196
+	{
1197
+		$this->_recalculate_tax_sub_total();
1198
+		$total = 0;
1199
+		foreach ($this->tax_descendants() as $tax_line_item) {
1200
+			if ($tax_line_item instanceof EE_Line_Item) {
1201
+				$total += $tax_line_item->total();
1202
+			}
1203
+		}
1204
+		return $total;
1205
+	}
1206
+
1207
+
1208
+	/**
1209
+	 * Gets the total for all the items purchased only
1210
+	 *
1211
+	 * @return float
1212
+	 * @throws EE_Error
1213
+	 */
1214
+	public function get_items_total()
1215
+	{
1216
+		// by default, let's make sure we're consistent with the existing line item
1217
+		if ($this->is_total()) {
1218
+			$pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1219
+			if ($pretax_subtotal_li instanceof EE_Line_Item) {
1220
+				return $pretax_subtotal_li->total();
1221
+			}
1222
+		}
1223
+		$total = 0;
1224
+		foreach ($this->get_items() as $item) {
1225
+			if ($item instanceof EE_Line_Item) {
1226
+				$total += $item->total();
1227
+			}
1228
+		}
1229
+		return $total;
1230
+	}
1231
+
1232
+
1233
+	/**
1234
+	 * Gets all the descendants (ie, children or children of children etc) that
1235
+	 * are of the type 'tax'
1236
+	 *
1237
+	 * @return EE_Line_Item[]
1238
+	 */
1239
+	public function tax_descendants()
1240
+	{
1241
+		return EEH_Line_Item::get_tax_descendants($this);
1242
+	}
1243
+
1244
+
1245
+	/**
1246
+	 * Gets all the real items purchased which are children of this item
1247
+	 *
1248
+	 * @return EE_Line_Item[]
1249
+	 */
1250
+	public function get_items()
1251
+	{
1252
+		return EEH_Line_Item::get_line_item_descendants($this);
1253
+	}
1254
+
1255
+
1256
+	/**
1257
+	 * Returns the amount taxable among this line item's children (or if it has no children,
1258
+	 * how much of it is taxable). Does not recalculate totals or subtotals.
1259
+	 * If the taxable total is negative, (eg, if none of the tickets were taxable,
1260
+	 * but there is a "Taxable" discount), returns 0.
1261
+	 *
1262
+	 * @return float
1263
+	 * @throws EE_Error
1264
+	 */
1265
+	public function taxable_total()
1266
+	{
1267
+		$total = 0;
1268
+		if ($this->children()) {
1269
+			foreach ($this->children() as $child_line_item) {
1270
+				if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1271
+					// if it's a percent item, only take into account the percent
1272
+					// that's taxable too (the taxable total so far)
1273
+					if ($child_line_item->is_percent()) {
1274
+						$total += ($total * $child_line_item->percent() / 100);
1275
+					} else {
1276
+						$total += $child_line_item->total();
1277
+					}
1278
+				} elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1279
+					$total += $child_line_item->taxable_total();
1280
+				}
1281
+			}
1282
+		}
1283
+		return max($total, 0);
1284
+	}
1285
+
1286
+
1287
+	/**
1288
+	 * Gets the transaction for this line item
1289
+	 *
1290
+	 * @return EE_Base_Class|EE_Transaction
1291
+	 * @throws EE_Error
1292
+	 */
1293
+	public function transaction()
1294
+	{
1295
+		return $this->get_first_related('Transaction');
1296
+	}
1297
+
1298
+
1299
+	/**
1300
+	 * Saves this line item to the DB, and recursively saves its descendants.
1301
+	 * Because there currently is no proper parent-child relation on the model,
1302
+	 * save_this_and_cached() will NOT save the descendants.
1303
+	 * Also sets the transaction on this line item and all its descendants before saving
1304
+	 *
1305
+	 * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1306
+	 * @return int count of items saved
1307
+	 * @throws EE_Error
1308
+	 */
1309
+	public function save_this_and_descendants_to_txn($txn_id = null)
1310
+	{
1311
+		$count = 0;
1312
+		if (! $txn_id) {
1313
+			$txn_id = $this->TXN_ID();
1314
+		}
1315
+		$this->set_TXN_ID($txn_id);
1316
+		$children = $this->children();
1317
+		$count += $this->save()
1318
+			? 1
1319
+			: 0;
1320
+		foreach ($children as $child_line_item) {
1321
+			if ($child_line_item instanceof EE_Line_Item) {
1322
+				$child_line_item->set_parent_ID($this->ID());
1323
+				$count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1324
+			}
1325
+		}
1326
+		return $count;
1327
+	}
1328
+
1329
+
1330
+	/**
1331
+	 * Saves this line item to the DB, and recursively saves its descendants.
1332
+	 *
1333
+	 * @return int count of items saved
1334
+	 * @throws EE_Error
1335
+	 */
1336
+	public function save_this_and_descendants()
1337
+	{
1338
+		$count = 0;
1339
+		$children = $this->children();
1340
+		$count += $this->save()
1341
+			? 1
1342
+			: 0;
1343
+		foreach ($children as $child_line_item) {
1344
+			if ($child_line_item instanceof EE_Line_Item) {
1345
+				$child_line_item->set_parent_ID($this->ID());
1346
+				$count += $child_line_item->save_this_and_descendants();
1347
+			}
1348
+		}
1349
+		return $count;
1350
+	}
1351
+
1352
+
1353
+	/**
1354
+	 * returns the cancellation line item if this item was cancelled
1355
+	 *
1356
+	 * @return EE_Line_Item[]
1357
+	 * @throws InvalidArgumentException
1358
+	 * @throws InvalidInterfaceException
1359
+	 * @throws InvalidDataTypeException
1360
+	 * @throws ReflectionException
1361
+	 * @throws EE_Error
1362
+	 */
1363
+	public function get_cancellations()
1364
+	{
1365
+		EE_Registry::instance()->load_helper('Line_Item');
1366
+		return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1367
+	}
1368
+
1369
+
1370
+	/**
1371
+	 * If this item has an ID, then this saves it again to update the db
1372
+	 *
1373
+	 * @return int count of items saved
1374
+	 * @throws EE_Error
1375
+	 */
1376
+	public function maybe_save()
1377
+	{
1378
+		if ($this->ID()) {
1379
+			return $this->save();
1380
+		}
1381
+		return false;
1382
+	}
1383
+
1384
+
1385
+	/**
1386
+	 * clears the cached children and parent from the line item
1387
+	 *
1388
+	 * @return void
1389
+	 */
1390
+	public function clear_related_line_item_cache()
1391
+	{
1392
+		$this->_children = array();
1393
+		$this->_parent = null;
1394
+	}
1395
+
1396
+
1397
+	/**
1398
+	 * @param bool $raw
1399
+	 * @return int
1400
+	 * @throws EE_Error
1401
+	 */
1402
+	public function timestamp($raw = false)
1403
+	{
1404
+		return $raw
1405
+			? $this->get_raw('LIN_timestamp')
1406
+			: $this->get('LIN_timestamp');
1407
+	}
1408
+
1409
+
1410
+
1411
+
1412
+	/************************* DEPRECATED *************************/
1413
+	/**
1414
+	 * @deprecated 4.6.0
1415
+	 * @param string $type one of the constants on EEM_Line_Item
1416
+	 * @return EE_Line_Item[]
1417
+	 */
1418
+	protected function _get_descendants_of_type($type)
1419
+	{
1420
+		EE_Error::doing_it_wrong(
1421
+			'EE_Line_Item::_get_descendants_of_type()',
1422
+			__('Method replaced with EEH_Line_Item::get_descendants_of_type()', 'event_espresso'),
1423
+			'4.6.0'
1424
+		);
1425
+		return EEH_Line_Item::get_descendants_of_type($this, $type);
1426
+	}
1427
+
1428
+
1429
+	/**
1430
+	 * @deprecated 4.6.0
1431
+	 * @param string $type like one of the EEM_Line_Item::type_*
1432
+	 * @return EE_Line_Item
1433
+	 */
1434
+	public function get_nearest_descendant_of_type($type)
1435
+	{
1436
+		EE_Error::doing_it_wrong(
1437
+			'EE_Line_Item::get_nearest_descendant_of_type()',
1438
+			__('Method replaced with EEH_Line_Item::get_nearest_descendant_of_type()', 'event_espresso'),
1439
+			'4.6.0'
1440
+		);
1441
+		return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1442
+	}
1443 1443
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -72,7 +72,7 @@  discard block
 block discarded – undo
72 72
     protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
73 73
     {
74 74
         parent::__construct($fieldValues, $bydb, $timezone);
75
-        if (! $this->get('LIN_code')) {
75
+        if ( ! $this->get('LIN_code')) {
76 76
             $this->set_code($this->generate_code());
77 77
         }
78 78
     }
@@ -123,7 +123,7 @@  discard block
 block discarded – undo
123 123
     public function name()
124 124
     {
125 125
         $name = $this->get('LIN_name');
126
-        if (! $name) {
126
+        if ( ! $name) {
127 127
             $name = ucwords(str_replace('-', ' ', $this->type()));
128 128
         }
129 129
         return $name;
@@ -480,7 +480,7 @@  discard block
 block discarded – undo
480 480
                 )
481 481
             );
482 482
         }
483
-        if (! is_array($this->_children)) {
483
+        if ( ! is_array($this->_children)) {
484 484
             $this->_children = array();
485 485
         }
486 486
         return $this->_children;
@@ -676,7 +676,7 @@  discard block
 block discarded – undo
676 676
             }
677 677
             return $line_item->save();
678 678
         }
679
-        $this->_children[ $line_item->code() ] = $line_item;
679
+        $this->_children[$line_item->code()] = $line_item;
680 680
         if ($line_item->parent() !== $this) {
681 681
             $line_item->set_parent($this);
682 682
         }
@@ -696,7 +696,7 @@  discard block
 block discarded – undo
696 696
     public function set_parent($line_item)
697 697
     {
698 698
         if ($this->ID()) {
699
-            if (! $line_item->ID()) {
699
+            if ( ! $line_item->ID()) {
700 700
                 $line_item->save();
701 701
             }
702 702
             $this->set_parent_ID($line_item->ID());
@@ -724,8 +724,8 @@  discard block
 block discarded – undo
724 724
                 array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
725 725
             );
726 726
         }
727
-        return isset($this->_children[ $code ])
728
-            ? $this->_children[ $code ]
727
+        return isset($this->_children[$code])
728
+            ? $this->_children[$code]
729 729
             : null;
730 730
     }
731 731
 
@@ -777,8 +777,8 @@  discard block
 block discarded – undo
777 777
             }
778 778
             return $items_deleted;
779 779
         }
780
-        if (isset($this->_children[ $code ])) {
781
-            unset($this->_children[ $code ]);
780
+        if (isset($this->_children[$code])) {
781
+            unset($this->_children[$code]);
782 782
             return 1;
783 783
         }
784 784
         return 0;
@@ -811,7 +811,7 @@  discard block
 block discarded – undo
811 811
     public function generate_code()
812 812
     {
813 813
         // each line item in the cart requires a unique identifier
814
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
814
+        return md5($this->get('OBJ_type').$this->get('OBJ_ID').microtime());
815 815
     }
816 816
 
817 817
 
@@ -974,7 +974,7 @@  discard block
 block discarded – undo
974 974
         $has_children = ! empty($my_children);
975 975
         if ($has_children && $this->is_line_item()) {
976 976
             $total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
977
-        } elseif (! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
977
+        } elseif ( ! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
978 978
             $total = $this->unit_price() * $this->quantity();
979 979
         } elseif ($this->is_sub_total() || $this->is_total()) {
980 980
             $total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
@@ -983,18 +983,18 @@  discard block
 block discarded – undo
983 983
             return 0;
984 984
         }
985 985
         // ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
986
-        if (! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()
986
+        if ( ! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()
987 987
         ) {
988 988
             if ($this->OBJ_type() !== 'Event') {
989 989
                 $this->set_quantity(1);
990 990
             }
991
-            if (! $this->is_percent()) {
991
+            if ( ! $this->is_percent()) {
992 992
                 $this->set_unit_price($total);
993 993
             }
994 994
         }
995 995
         // we don't want to bother saving grand totals, because that needs to factor in taxes anyways
996 996
         // so it ought to be
997
-        if (! $this->is_total()) {
997
+        if ( ! $this->is_total()) {
998 998
             $this->set_total($total);
999 999
             // if not a percent line item, make sure we keep the unit price in sync
1000 1000
             if ($has_children
@@ -1309,7 +1309,7 @@  discard block
 block discarded – undo
1309 1309
     public function save_this_and_descendants_to_txn($txn_id = null)
1310 1310
     {
1311 1311
         $count = 0;
1312
-        if (! $txn_id) {
1312
+        if ( ! $txn_id) {
1313 1313
             $txn_id = $this->TXN_ID();
1314 1314
         }
1315 1315
         $this->set_TXN_ID($txn_id);
Please login to merge, or discard this patch.