Completed
Branch FET/Gutenberg/11467/espresso-c... (b0019e)
by
unknown
244:19 queued 198:55
created
caffeinated/modules/recaptcha_invisible/RecaptchaAdminSettings.php 1 patch
Indentation   +321 added lines, -321 removed lines patch added patch discarded remove patch
@@ -35,337 +35,337 @@
 block discarded – undo
35 35
 class RecaptchaAdminSettings
36 36
 {
37 37
 
38
-    /**
39
-     * @var EE_Registration_Config $config
40
-     */
41
-    private $config;
38
+	/**
39
+	 * @var EE_Registration_Config $config
40
+	 */
41
+	private $config;
42 42
 
43 43
 
44
-    /**
45
-     * RecaptchaAdminSettings constructor.
46
-     *
47
-     * @param EE_Registration_Config $registration_config
48
-     */
49
-    public function __construct(EE_Registration_Config $registration_config)
50
-    {
51
-        $this->config = $registration_config;
52
-    }
44
+	/**
45
+	 * RecaptchaAdminSettings constructor.
46
+	 *
47
+	 * @param EE_Registration_Config $registration_config
48
+	 */
49
+	public function __construct(EE_Registration_Config $registration_config)
50
+	{
51
+		$this->config = $registration_config;
52
+	}
53 53
 
54 54
 
55
-    /**
56
-     * @throws InvalidArgumentException
57
-     * @throws InvalidInterfaceException
58
-     * @throws InvalidDataTypeException
59
-     * @throws EE_Error
60
-     */
61
-    public function adminSettings()
62
-    {
63
-        echo $this->settingsForm()->get_html_and_js();
64
-    }
55
+	/**
56
+	 * @throws InvalidArgumentException
57
+	 * @throws InvalidInterfaceException
58
+	 * @throws InvalidDataTypeException
59
+	 * @throws EE_Error
60
+	 */
61
+	public function adminSettings()
62
+	{
63
+		echo $this->settingsForm()->get_html_and_js();
64
+	}
65 65
 
66 66
 
67
-    /**
68
-     * @return EE_Form_Section_Proper
69
-     * @throws EE_Error
70
-     */
71
-    protected function settingsForm()
72
-    {
73
-        return new EE_Form_Section_Proper(
74
-            array(
75
-                'name'            => 'recaptcha_settings_form',
76
-                'html_id'         => 'recaptcha_settings_form',
77
-                'layout_strategy' => new EE_Div_Per_Section_Layout(),
78
-                'subsections'     => apply_filters(
79
-                    'FHEE__EED_Recaptcha___recaptcha_settings_form__form_subsections',
80
-                    array(
81
-                        'main_settings_hdr'       => new EE_Form_Section_HTML(
82
-                            EEH_HTML::h2(
83
-                                esc_html__('reCAPTCHA Anti-spam Settings', 'event_espresso')
84
-                                . EEH_Template::get_help_tab_link('recaptcha_info')
85
-                            )
86
-                        ),
87
-                        'main_settings'           => $this->mainSettings(),
88
-                        'appearance_settings_hdr' => new EE_Form_Section_HTML(
89
-                            EEH_HTML::h2(esc_html__('reCAPTCHA Appearance', 'event_espresso'))
90
-                        ),
91
-                        'appearance_settings'     => $this->appearanceSettings(),
92
-                        'required_fields_note'    => new EE_Form_Section_HTML(
93
-                            EEH_HTML::p(
94
-                                esc_html__('All fields marked with a * are required fields', 'event_espresso'),
95
-                                '', 'grey-text'
96
-                            )
97
-                        ),
98
-                    )
99
-                ),
100
-            )
101
-        );
102
-    }
67
+	/**
68
+	 * @return EE_Form_Section_Proper
69
+	 * @throws EE_Error
70
+	 */
71
+	protected function settingsForm()
72
+	{
73
+		return new EE_Form_Section_Proper(
74
+			array(
75
+				'name'            => 'recaptcha_settings_form',
76
+				'html_id'         => 'recaptcha_settings_form',
77
+				'layout_strategy' => new EE_Div_Per_Section_Layout(),
78
+				'subsections'     => apply_filters(
79
+					'FHEE__EED_Recaptcha___recaptcha_settings_form__form_subsections',
80
+					array(
81
+						'main_settings_hdr'       => new EE_Form_Section_HTML(
82
+							EEH_HTML::h2(
83
+								esc_html__('reCAPTCHA Anti-spam Settings', 'event_espresso')
84
+								. EEH_Template::get_help_tab_link('recaptcha_info')
85
+							)
86
+						),
87
+						'main_settings'           => $this->mainSettings(),
88
+						'appearance_settings_hdr' => new EE_Form_Section_HTML(
89
+							EEH_HTML::h2(esc_html__('reCAPTCHA Appearance', 'event_espresso'))
90
+						),
91
+						'appearance_settings'     => $this->appearanceSettings(),
92
+						'required_fields_note'    => new EE_Form_Section_HTML(
93
+							EEH_HTML::p(
94
+								esc_html__('All fields marked with a * are required fields', 'event_espresso'),
95
+								'', 'grey-text'
96
+							)
97
+						),
98
+					)
99
+				),
100
+			)
101
+		);
102
+	}
103 103
 
104 104
 
105
-    /**
106
-     * @return EE_Form_Section_Proper
107
-     * @throws EE_Error
108
-     */
109
-    protected function mainSettings()
110
-    {
111
-        return new EE_Form_Section_Proper(
112
-            array(
113
-                'name'            => 'recaptcha_settings_tbl',
114
-                'html_id'         => 'recaptcha_settings_tbl',
115
-                'html_class'      => 'form-table',
116
-                'layout_strategy' => new EE_Admin_Two_Column_Layout(),
117
-                'subsections'     => apply_filters(
118
-                    'FHEE__EED_Recaptcha___recaptcha_main_settings__form_subsections',
119
-                    array(
120
-                        'use_captcha'          => new EE_Yes_No_Input(
121
-                            array(
122
-                                'html_label_text'         => esc_html__('Use reCAPTCHA', 'event_espresso'),
123
-                                'html_help_text'          => sprintf(
124
-                                    esc_html__('reCAPTCHA is a free service that  protects your website from spam and abuse. It employs advanced risk analysis technology to separate humans from abusive actors. Sign up %1$shere%2$s to receive your Public and Private keys.',
125
-                                        'event_espresso'),
126
-                                    '<a href="https://www.google.com/recaptcha/intro/index.html">',
127
-                                    '</a>'
128
-                                ),
129
-                                'default'                 => $this->config->use_captcha !== null
130
-                                    ? $this->config->use_captcha : false,
131
-                                'display_html_label_text' => false,
132
-                            )
133
-                        ),
134
-                        'recaptcha_publickey'  => new EE_Text_Input(
135
-                            array(
136
-                                'html_label_text' => esc_html__('Site Key', 'event_espresso'),
137
-                                'html_help_text'  => esc_html__('The site key is used to display the widget on your site.',
138
-                                    'event_espresso'),
139
-                                'default'         => $this->config->recaptcha_publickey !== null
140
-                                    ? stripslashes($this->config->recaptcha_publickey) : '',
141
-                            )
142
-                        ),
143
-                        'recaptcha_privatekey' => new EE_Text_Input(
144
-                            array(
145
-                                'html_label_text' => esc_html__('Secret Key', 'event_espresso'),
146
-                                'html_help_text'  => esc_html__('The secret key authorizes communication between your application backend and the reCAPTCHA server to verify the user\'s response. The secret key needs to be kept safe for security purposes.',
147
-                                    'event_espresso'),
148
-                                'default'         => $this->config->recaptcha_privatekey !== null
149
-                                    ? stripslashes($this->config->recaptcha_privatekey)
150
-                                    : '',
151
-                            )
152
-                        ),
153
-                        'recaptcha_protected_forms' => new EE_Checkbox_Multi_Input(
154
-                            array(
155
-                                'ticket_selector'   => esc_html__('Ticket Selector', 'event_espresso'),
156
-                                'registration_form' => esc_html__('Registration Form', 'event_espresso'),
157
-                            ),
158
-                            array(
159
-                                'html_label_text'         => esc_html__('Invisible reCAPTCHA Protection', 'event_espresso'),
160
-                                'html_help_text'          => esc_html__('Select which Event Espresso forms you would like to enable Invisible reCAPTCHA on.',
161
-                                    'event_espresso'),
162
-                                'default'                 => is_array($this->config->recaptcha_protected_forms)
163
-                                    ? $this->config->recaptcha_protected_forms
164
-                                    : array(),
165
-                                'display_html_label_text' => false,
166
-                            )
167
-                        ),
168
-                    )
169
-                ),
170
-            )
171
-        );
172
-    }
105
+	/**
106
+	 * @return EE_Form_Section_Proper
107
+	 * @throws EE_Error
108
+	 */
109
+	protected function mainSettings()
110
+	{
111
+		return new EE_Form_Section_Proper(
112
+			array(
113
+				'name'            => 'recaptcha_settings_tbl',
114
+				'html_id'         => 'recaptcha_settings_tbl',
115
+				'html_class'      => 'form-table',
116
+				'layout_strategy' => new EE_Admin_Two_Column_Layout(),
117
+				'subsections'     => apply_filters(
118
+					'FHEE__EED_Recaptcha___recaptcha_main_settings__form_subsections',
119
+					array(
120
+						'use_captcha'          => new EE_Yes_No_Input(
121
+							array(
122
+								'html_label_text'         => esc_html__('Use reCAPTCHA', 'event_espresso'),
123
+								'html_help_text'          => sprintf(
124
+									esc_html__('reCAPTCHA is a free service that  protects your website from spam and abuse. It employs advanced risk analysis technology to separate humans from abusive actors. Sign up %1$shere%2$s to receive your Public and Private keys.',
125
+										'event_espresso'),
126
+									'<a href="https://www.google.com/recaptcha/intro/index.html">',
127
+									'</a>'
128
+								),
129
+								'default'                 => $this->config->use_captcha !== null
130
+									? $this->config->use_captcha : false,
131
+								'display_html_label_text' => false,
132
+							)
133
+						),
134
+						'recaptcha_publickey'  => new EE_Text_Input(
135
+							array(
136
+								'html_label_text' => esc_html__('Site Key', 'event_espresso'),
137
+								'html_help_text'  => esc_html__('The site key is used to display the widget on your site.',
138
+									'event_espresso'),
139
+								'default'         => $this->config->recaptcha_publickey !== null
140
+									? stripslashes($this->config->recaptcha_publickey) : '',
141
+							)
142
+						),
143
+						'recaptcha_privatekey' => new EE_Text_Input(
144
+							array(
145
+								'html_label_text' => esc_html__('Secret Key', 'event_espresso'),
146
+								'html_help_text'  => esc_html__('The secret key authorizes communication between your application backend and the reCAPTCHA server to verify the user\'s response. The secret key needs to be kept safe for security purposes.',
147
+									'event_espresso'),
148
+								'default'         => $this->config->recaptcha_privatekey !== null
149
+									? stripslashes($this->config->recaptcha_privatekey)
150
+									: '',
151
+							)
152
+						),
153
+						'recaptcha_protected_forms' => new EE_Checkbox_Multi_Input(
154
+							array(
155
+								'ticket_selector'   => esc_html__('Ticket Selector', 'event_espresso'),
156
+								'registration_form' => esc_html__('Registration Form', 'event_espresso'),
157
+							),
158
+							array(
159
+								'html_label_text'         => esc_html__('Invisible reCAPTCHA Protection', 'event_espresso'),
160
+								'html_help_text'          => esc_html__('Select which Event Espresso forms you would like to enable Invisible reCAPTCHA on.',
161
+									'event_espresso'),
162
+								'default'                 => is_array($this->config->recaptcha_protected_forms)
163
+									? $this->config->recaptcha_protected_forms
164
+									: array(),
165
+								'display_html_label_text' => false,
166
+							)
167
+						),
168
+					)
169
+				),
170
+			)
171
+		);
172
+	}
173 173
 
174 174
 
175
-    /**
176
-     * @return EE_Form_Section_Proper
177
-     * @throws EE_Error
178
-     */
179
-    protected function appearanceSettings()
180
-    {
181
-        return new EE_Form_Section_Proper(
182
-            array(
183
-                'name'            => 'recaptcha_appearance_settings_tbl',
184
-                'html_id'         => 'recaptcha_appearance_settings_tbl',
185
-                'html_class'      => 'form-table',
186
-                'layout_strategy' => new EE_Admin_Two_Column_Layout(),
187
-                'subsections'     => apply_filters(
188
-                    'FHEE__EED_Recaptcha___recaptcha_appearance_settings__form_subsections',
189
-                    array(
190
-                        'recaptcha_theme'    => new EE_Radio_Button_Input(
191
-                            array(
192
-                                'invisible' => esc_html__('Invisible', 'event_espresso'),
193
-                                'light'     => esc_html__('Light', 'event_espresso'),
194
-                                'dark'      => esc_html__('Dark', 'event_espresso'),
195
-                            ),
196
-                            array(
197
-                                'html_label_text'         => esc_html__('Theme', 'event_espresso'),
198
-                                'html_help_text'          => esc_html__('The color theme of the widget.',
199
-                                    'event_espresso'),
200
-                                'default'                 => $this->config->recaptcha_theme !== null
201
-                                    ? $this->config->recaptcha_theme
202
-                                    : 'invisible',
203
-                                'display_html_label_text' => false,
204
-                            )
205
-                        ),
206
-                        'recaptcha_badge'     => new EE_Radio_Button_Input(
207
-                            array(
208
-                                'bottomleft' => esc_html__('Bottom Left', 'event_espresso'),
209
-                                'bottomright' => esc_html__('Bottom Right', 'event_espresso'),
210
-                                'inline' => esc_html__('Inline', 'event_espresso'),
211
-                            ),
212
-                            array(
213
-                                'html_label_text'         => esc_html__('Invisible reCAPTCHA Badge Position', 'event_espresso'),
214
-                                'html_help_text'          => esc_html__(
215
-                                    'If using Invisible reCAPTCHA, then this determines the position of the reCAPTCHA badge. "Bottom Left" and "Bottom Right" both will float at the bottom of the screen. "Inline" appears beside the submit button but allows you to control the CSS.',
216
-                                    'event_espresso'
217
-                                ),
218
-                                'default'                 => $this->config->recaptcha_badge !== null
219
-                                    ? $this->config->recaptcha_badge
220
-                                    : 'bottomleft',
221
-                                'display_html_label_text' => false,
222
-                            )
223
-                        ),
224
-                        'recaptcha_type'     => new EE_Radio_Button_Input(
225
-                            array(
226
-                                'image' => esc_html__('Image', 'event_espresso'),
227
-                                'audio' => esc_html__('Audio', 'event_espresso'),
228
-                            ),
229
-                            array(
230
-                                'html_label_text'         => esc_html__('Type', 'event_espresso'),
231
-                                'html_help_text'          => esc_html__('The type of CAPTCHA to serve.',
232
-                                    'event_espresso'),
233
-                                'default'                 => $this->config->recaptcha_type !== null
234
-                                    ? $this->config->recaptcha_type
235
-                                    : 'image',
236
-                                'display_html_label_text' => false,
237
-                            )
238
-                        ),
239
-                        'recaptcha_language' => new EE_Select_Input(
240
-                            array(
241
-                                'ar'     => esc_html__('Arabic', 'event_espresso'),
242
-                                'bg'     => esc_html__('Bulgarian', 'event_espresso'),
243
-                                'ca'     => esc_html__('Catalan', 'event_espresso'),
244
-                                'zh-CN'  => esc_html__('Chinese (Simplified)', 'event_espresso'),
245
-                                'zh-TW'  => esc_html__('Chinese (Traditional)	', 'event_espresso'),
246
-                                'hr'     => esc_html__('Croatian', 'event_espresso'),
247
-                                'cs'     => esc_html__('Czech', 'event_espresso'),
248
-                                'da'     => esc_html__('Danish', 'event_espresso'),
249
-                                'nl'     => esc_html__('Dutch', 'event_espresso'),
250
-                                'en-GB'  => esc_html__('English (UK)', 'event_espresso'),
251
-                                'en'     => esc_html__('English (US)', 'event_espresso'),
252
-                                'fil'    => esc_html__('Filipino', 'event_espresso'),
253
-                                'fi'     => esc_html__('Finnish', 'event_espresso'),
254
-                                'fr'     => esc_html__('French', 'event_espresso'),
255
-                                'fr-CA'  => esc_html__('French (Canadian)', 'event_espresso'),
256
-                                'de'     => esc_html__('German', 'event_espresso'),
257
-                                'de-AT'  => esc_html__('German (Austria)', 'event_espresso'),
258
-                                'de-CH'  => esc_html__('German (Switzerland)', 'event_espresso'),
259
-                                'el'     => esc_html__('Greek', 'event_espresso'),
260
-                                'iw'     => esc_html__('Hebrew', 'event_espresso'),
261
-                                'hi'     => esc_html__('Hindi', 'event_espresso'),
262
-                                'hu'     => esc_html__('Hungarian', 'event_espresso'),
263
-                                'id'     => esc_html__('Indonesian', 'event_espresso'),
264
-                                'it'     => esc_html__('Italian', 'event_espresso'),
265
-                                'ja'     => esc_html__('Japanese', 'event_espresso'),
266
-                                'ko'     => esc_html__('Korean', 'event_espresso'),
267
-                                'lv'     => esc_html__('Latvian', 'event_espresso'),
268
-                                'lt'     => esc_html__('Lithuanian', 'event_espresso'),
269
-                                'no'     => esc_html__('Norwegian', 'event_espresso'),
270
-                                'fa'     => esc_html__('Persian', 'event_espresso'),
271
-                                'pl'     => esc_html__('Polish', 'event_espresso'),
272
-                                'pt'     => esc_html__('Portuguese', 'event_espresso'),
273
-                                'pt-BR'  => esc_html__('Portuguese (Brazil)', 'event_espresso'),
274
-                                'pt-PT'  => esc_html__('Portuguese (Portugal)', 'event_espresso'),
275
-                                'ro'     => esc_html__('Romanian', 'event_espresso'),
276
-                                'ru'     => esc_html__('Russian', 'event_espresso'),
277
-                                'sr'     => esc_html__('Serbian', 'event_espresso'),
278
-                                'sk'     => esc_html__('Slovak', 'event_espresso'),
279
-                                'sl'     => esc_html__('Slovenian', 'event_espresso'),
280
-                                'es'     => esc_html__('Spanish', 'event_espresso'),
281
-                                'es-419' => esc_html__('Spanish (Latin America)', 'event_espresso'),
282
-                                'sv'     => esc_html__('Swedish', 'event_espresso'),
283
-                                'th'     => esc_html__('Thai', 'event_espresso'),
284
-                                'tr'     => esc_html__('Turkish', 'event_espresso'),
285
-                                'uk'     => esc_html__('Ukrainian', 'event_espresso'),
286
-                                'vi'     => esc_html__('Vietnamese', 'event_espresso'),
287
-                            ),
288
-                            array(
289
-                                'html_label_text' => esc_html__('Language', 'event_espresso'),
290
-                                'html_help_text'  => esc_html__('Forces the widget to render in a specific language.',
291
-                                    'event_espresso'),
292
-                                'default'         => $this->config->recaptcha_language !== null
293
-                                    ? $this->config->recaptcha_language : 'en',
294
-                            )
295
-                        ),
296
-                    )
297
-                ),
298
-            )
299
-        );
300
-    }
175
+	/**
176
+	 * @return EE_Form_Section_Proper
177
+	 * @throws EE_Error
178
+	 */
179
+	protected function appearanceSettings()
180
+	{
181
+		return new EE_Form_Section_Proper(
182
+			array(
183
+				'name'            => 'recaptcha_appearance_settings_tbl',
184
+				'html_id'         => 'recaptcha_appearance_settings_tbl',
185
+				'html_class'      => 'form-table',
186
+				'layout_strategy' => new EE_Admin_Two_Column_Layout(),
187
+				'subsections'     => apply_filters(
188
+					'FHEE__EED_Recaptcha___recaptcha_appearance_settings__form_subsections',
189
+					array(
190
+						'recaptcha_theme'    => new EE_Radio_Button_Input(
191
+							array(
192
+								'invisible' => esc_html__('Invisible', 'event_espresso'),
193
+								'light'     => esc_html__('Light', 'event_espresso'),
194
+								'dark'      => esc_html__('Dark', 'event_espresso'),
195
+							),
196
+							array(
197
+								'html_label_text'         => esc_html__('Theme', 'event_espresso'),
198
+								'html_help_text'          => esc_html__('The color theme of the widget.',
199
+									'event_espresso'),
200
+								'default'                 => $this->config->recaptcha_theme !== null
201
+									? $this->config->recaptcha_theme
202
+									: 'invisible',
203
+								'display_html_label_text' => false,
204
+							)
205
+						),
206
+						'recaptcha_badge'     => new EE_Radio_Button_Input(
207
+							array(
208
+								'bottomleft' => esc_html__('Bottom Left', 'event_espresso'),
209
+								'bottomright' => esc_html__('Bottom Right', 'event_espresso'),
210
+								'inline' => esc_html__('Inline', 'event_espresso'),
211
+							),
212
+							array(
213
+								'html_label_text'         => esc_html__('Invisible reCAPTCHA Badge Position', 'event_espresso'),
214
+								'html_help_text'          => esc_html__(
215
+									'If using Invisible reCAPTCHA, then this determines the position of the reCAPTCHA badge. "Bottom Left" and "Bottom Right" both will float at the bottom of the screen. "Inline" appears beside the submit button but allows you to control the CSS.',
216
+									'event_espresso'
217
+								),
218
+								'default'                 => $this->config->recaptcha_badge !== null
219
+									? $this->config->recaptcha_badge
220
+									: 'bottomleft',
221
+								'display_html_label_text' => false,
222
+							)
223
+						),
224
+						'recaptcha_type'     => new EE_Radio_Button_Input(
225
+							array(
226
+								'image' => esc_html__('Image', 'event_espresso'),
227
+								'audio' => esc_html__('Audio', 'event_espresso'),
228
+							),
229
+							array(
230
+								'html_label_text'         => esc_html__('Type', 'event_espresso'),
231
+								'html_help_text'          => esc_html__('The type of CAPTCHA to serve.',
232
+									'event_espresso'),
233
+								'default'                 => $this->config->recaptcha_type !== null
234
+									? $this->config->recaptcha_type
235
+									: 'image',
236
+								'display_html_label_text' => false,
237
+							)
238
+						),
239
+						'recaptcha_language' => new EE_Select_Input(
240
+							array(
241
+								'ar'     => esc_html__('Arabic', 'event_espresso'),
242
+								'bg'     => esc_html__('Bulgarian', 'event_espresso'),
243
+								'ca'     => esc_html__('Catalan', 'event_espresso'),
244
+								'zh-CN'  => esc_html__('Chinese (Simplified)', 'event_espresso'),
245
+								'zh-TW'  => esc_html__('Chinese (Traditional)	', 'event_espresso'),
246
+								'hr'     => esc_html__('Croatian', 'event_espresso'),
247
+								'cs'     => esc_html__('Czech', 'event_espresso'),
248
+								'da'     => esc_html__('Danish', 'event_espresso'),
249
+								'nl'     => esc_html__('Dutch', 'event_espresso'),
250
+								'en-GB'  => esc_html__('English (UK)', 'event_espresso'),
251
+								'en'     => esc_html__('English (US)', 'event_espresso'),
252
+								'fil'    => esc_html__('Filipino', 'event_espresso'),
253
+								'fi'     => esc_html__('Finnish', 'event_espresso'),
254
+								'fr'     => esc_html__('French', 'event_espresso'),
255
+								'fr-CA'  => esc_html__('French (Canadian)', 'event_espresso'),
256
+								'de'     => esc_html__('German', 'event_espresso'),
257
+								'de-AT'  => esc_html__('German (Austria)', 'event_espresso'),
258
+								'de-CH'  => esc_html__('German (Switzerland)', 'event_espresso'),
259
+								'el'     => esc_html__('Greek', 'event_espresso'),
260
+								'iw'     => esc_html__('Hebrew', 'event_espresso'),
261
+								'hi'     => esc_html__('Hindi', 'event_espresso'),
262
+								'hu'     => esc_html__('Hungarian', 'event_espresso'),
263
+								'id'     => esc_html__('Indonesian', 'event_espresso'),
264
+								'it'     => esc_html__('Italian', 'event_espresso'),
265
+								'ja'     => esc_html__('Japanese', 'event_espresso'),
266
+								'ko'     => esc_html__('Korean', 'event_espresso'),
267
+								'lv'     => esc_html__('Latvian', 'event_espresso'),
268
+								'lt'     => esc_html__('Lithuanian', 'event_espresso'),
269
+								'no'     => esc_html__('Norwegian', 'event_espresso'),
270
+								'fa'     => esc_html__('Persian', 'event_espresso'),
271
+								'pl'     => esc_html__('Polish', 'event_espresso'),
272
+								'pt'     => esc_html__('Portuguese', 'event_espresso'),
273
+								'pt-BR'  => esc_html__('Portuguese (Brazil)', 'event_espresso'),
274
+								'pt-PT'  => esc_html__('Portuguese (Portugal)', 'event_espresso'),
275
+								'ro'     => esc_html__('Romanian', 'event_espresso'),
276
+								'ru'     => esc_html__('Russian', 'event_espresso'),
277
+								'sr'     => esc_html__('Serbian', 'event_espresso'),
278
+								'sk'     => esc_html__('Slovak', 'event_espresso'),
279
+								'sl'     => esc_html__('Slovenian', 'event_espresso'),
280
+								'es'     => esc_html__('Spanish', 'event_espresso'),
281
+								'es-419' => esc_html__('Spanish (Latin America)', 'event_espresso'),
282
+								'sv'     => esc_html__('Swedish', 'event_espresso'),
283
+								'th'     => esc_html__('Thai', 'event_espresso'),
284
+								'tr'     => esc_html__('Turkish', 'event_espresso'),
285
+								'uk'     => esc_html__('Ukrainian', 'event_espresso'),
286
+								'vi'     => esc_html__('Vietnamese', 'event_espresso'),
287
+							),
288
+							array(
289
+								'html_label_text' => esc_html__('Language', 'event_espresso'),
290
+								'html_help_text'  => esc_html__('Forces the widget to render in a specific language.',
291
+									'event_espresso'),
292
+								'default'         => $this->config->recaptcha_language !== null
293
+									? $this->config->recaptcha_language : 'en',
294
+							)
295
+						),
296
+					)
297
+				),
298
+			)
299
+		);
300
+	}
301 301
 
302 302
 
303
-    /**
304
-     * @param EE_Registration_Config $EE_Registration_Config
305
-     * @return EE_Registration_Config
306
-     * @throws InvalidArgumentException
307
-     * @throws InvalidInterfaceException
308
-     * @throws InvalidDataTypeException
309
-     * @throws EE_Error
310
-     * @throws ReflectionException
311
-     */
312
-    public function updateAdminSettings(EE_Registration_Config $EE_Registration_Config)
313
-    {
314
-        try {
315
-            $recaptcha_settings_form = $this->settingsForm();
316
-            // if not displaying a form, then check for form submission
317
-            if ($recaptcha_settings_form->was_submitted()) {
318
-                // capture form data
319
-                $recaptcha_settings_form->receive_form_submission();
320
-                // validate form data
321
-                if ($recaptcha_settings_form->is_valid()) {
322
-                    // grab validated data from form
323
-                    $valid_data = $recaptcha_settings_form->valid_data();
324
-                    // user proofing recaptcha:  If Use reCAPTCHA is set to yes but we dont' have site or secret keys then set Use reCAPTCHA to FALSE and give error message.
325
-                    if (
326
-                        $valid_data['main_settings']['use_captcha']
327
-                        && (
328
-                            ! $EE_Registration_Config->use_captcha
329
-                            && (
330
-                                empty($valid_data['main_settings']['recaptcha_publickey'])
331
-                                || empty($valid_data['main_settings']['recaptcha_privatekey'])
332
-                            )
333
-                        )
334
-                        && apply_filters(
335
-                            'FHEE__Extend_Registration_Form_Admin_Page__check_for_recaptcha_keys',
336
-                            true,
337
-                            $EE_Registration_Config
338
-                        )
339
-                    ) {
340
-                        $valid_data['main_settings']['use_captcha'] = false;
341
-                        EE_Error::add_error(
342
-                            esc_html__(
343
-                                'The use reCAPTCHA setting has been reset to "no". In order to enable the reCAPTCHA service, you must enter a Site Key and Secret Key.',
344
-                                'event_espresso'
345
-                            ),
346
-                            __FILE__, __FUNCTION__, __LINE__
347
-                        );
348
-                    }
349
-                    $EE_Registration_Config->use_captcha          = $valid_data['main_settings']['use_captcha'];
350
-                    $EE_Registration_Config->recaptcha_publickey  = $valid_data['main_settings']['recaptcha_publickey'];
351
-                    $EE_Registration_Config->recaptcha_protected_forms = $valid_data['main_settings']['recaptcha_protected_forms'];
352
-                    $EE_Registration_Config->recaptcha_privatekey = $valid_data['main_settings']['recaptcha_privatekey'];
353
-                    $EE_Registration_Config->recaptcha_type       = $valid_data['appearance_settings']['recaptcha_type'];
354
-                    $EE_Registration_Config->recaptcha_theme      = $valid_data['appearance_settings']['recaptcha_theme'];
355
-                    $EE_Registration_Config->recaptcha_badge      = $valid_data['appearance_settings']['recaptcha_badge'];
356
-                    $EE_Registration_Config->recaptcha_language   = $valid_data['appearance_settings']['recaptcha_language'];
357
-                } else {
358
-                    if ($recaptcha_settings_form->submission_error_message() !== '') {
359
-                        EE_Error::add_error(
360
-                            $recaptcha_settings_form->submission_error_message(),
361
-                            __FILE__, __FUNCTION__, __LINE__
362
-                        );
363
-                    }
364
-                }
365
-            }
366
-        } catch (EE_Error $e) {
367
-            $e->get_error();
368
-        }
369
-        return $EE_Registration_Config;
370
-    }
303
+	/**
304
+	 * @param EE_Registration_Config $EE_Registration_Config
305
+	 * @return EE_Registration_Config
306
+	 * @throws InvalidArgumentException
307
+	 * @throws InvalidInterfaceException
308
+	 * @throws InvalidDataTypeException
309
+	 * @throws EE_Error
310
+	 * @throws ReflectionException
311
+	 */
312
+	public function updateAdminSettings(EE_Registration_Config $EE_Registration_Config)
313
+	{
314
+		try {
315
+			$recaptcha_settings_form = $this->settingsForm();
316
+			// if not displaying a form, then check for form submission
317
+			if ($recaptcha_settings_form->was_submitted()) {
318
+				// capture form data
319
+				$recaptcha_settings_form->receive_form_submission();
320
+				// validate form data
321
+				if ($recaptcha_settings_form->is_valid()) {
322
+					// grab validated data from form
323
+					$valid_data = $recaptcha_settings_form->valid_data();
324
+					// user proofing recaptcha:  If Use reCAPTCHA is set to yes but we dont' have site or secret keys then set Use reCAPTCHA to FALSE and give error message.
325
+					if (
326
+						$valid_data['main_settings']['use_captcha']
327
+						&& (
328
+							! $EE_Registration_Config->use_captcha
329
+							&& (
330
+								empty($valid_data['main_settings']['recaptcha_publickey'])
331
+								|| empty($valid_data['main_settings']['recaptcha_privatekey'])
332
+							)
333
+						)
334
+						&& apply_filters(
335
+							'FHEE__Extend_Registration_Form_Admin_Page__check_for_recaptcha_keys',
336
+							true,
337
+							$EE_Registration_Config
338
+						)
339
+					) {
340
+						$valid_data['main_settings']['use_captcha'] = false;
341
+						EE_Error::add_error(
342
+							esc_html__(
343
+								'The use reCAPTCHA setting has been reset to "no". In order to enable the reCAPTCHA service, you must enter a Site Key and Secret Key.',
344
+								'event_espresso'
345
+							),
346
+							__FILE__, __FUNCTION__, __LINE__
347
+						);
348
+					}
349
+					$EE_Registration_Config->use_captcha          = $valid_data['main_settings']['use_captcha'];
350
+					$EE_Registration_Config->recaptcha_publickey  = $valid_data['main_settings']['recaptcha_publickey'];
351
+					$EE_Registration_Config->recaptcha_protected_forms = $valid_data['main_settings']['recaptcha_protected_forms'];
352
+					$EE_Registration_Config->recaptcha_privatekey = $valid_data['main_settings']['recaptcha_privatekey'];
353
+					$EE_Registration_Config->recaptcha_type       = $valid_data['appearance_settings']['recaptcha_type'];
354
+					$EE_Registration_Config->recaptcha_theme      = $valid_data['appearance_settings']['recaptcha_theme'];
355
+					$EE_Registration_Config->recaptcha_badge      = $valid_data['appearance_settings']['recaptcha_badge'];
356
+					$EE_Registration_Config->recaptcha_language   = $valid_data['appearance_settings']['recaptcha_language'];
357
+				} else {
358
+					if ($recaptcha_settings_form->submission_error_message() !== '') {
359
+						EE_Error::add_error(
360
+							$recaptcha_settings_form->submission_error_message(),
361
+							__FILE__, __FUNCTION__, __LINE__
362
+						);
363
+					}
364
+				}
365
+			}
366
+		} catch (EE_Error $e) {
367
+			$e->get_error();
368
+		}
369
+		return $EE_Registration_Config;
370
+	}
371 371
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Payment.model.php 1 patch
Spacing   +48 added lines, -48 removed lines patch added patch discarded remove patch
@@ -8,7 +8,7 @@  discard block
 block discarded – undo
8 8
  * @author				Michael Nelson, Brent Christensen
9 9
  *
10 10
  */
11
-class EEM_Payment extends EEM_Base implements EEMI_Payment{
11
+class EEM_Payment extends EEM_Base implements EEMI_Payment {
12 12
 
13 13
   	// private instance of the Payment object
14 14
 	protected static $_instance = NULL;
@@ -56,28 +56,28 @@  discard block
 block discarded – undo
56 56
 	 *		@param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any incoming timezone data that gets saved).  Note this just sends the timezone info to the date time model field objects.  Default is NULL (and will be assumed using the set timezone in the 'timezone_string' wp option)
57 57
 	 *		@return EEM_Payment
58 58
 	 */
59
-	protected function __construct( $timezone ) {
59
+	protected function __construct($timezone) {
60 60
 
61
-		$this->singular_item = __('Payment','event_espresso');
62
-		$this->plural_item = __('Payments','event_espresso');
61
+		$this->singular_item = __('Payment', 'event_espresso');
62
+		$this->plural_item = __('Payments', 'event_espresso');
63 63
 
64 64
 		$this->_tables = array(
65
-			'Payment'=>new EE_Primary_Table('esp_payment','PAY_ID')
65
+			'Payment'=>new EE_Primary_Table('esp_payment', 'PAY_ID')
66 66
 		);
67 67
 		$this->_fields = array(
68 68
 			'Payment'=>array(
69
-				'PAY_ID'=>new EE_Primary_Key_Int_Field('PAY_ID', __('Payment ID','event_espresso')),
70
-				'TXN_ID'=>new EE_Foreign_Key_Int_Field('TXN_ID', __('Transaction ID','event_espresso'), false, 0, 'Transaction'),
71
-				'STS_ID'=>new EE_Foreign_Key_String_Field('STS_ID', __('Status ID','event_espresso'), false, EEM_Payment::status_id_failed, 'Status'),
72
-				'PAY_timestamp'=> new EE_Datetime_Field('PAY_timestamp', __('Timestamp of when payment was attempted','event_espresso'), false, EE_Datetime_Field::now, $timezone ),
73
-				'PAY_source'=>new EE_All_Caps_Text_Field('PAY_source', __('User-friendly description of payment','event_espresso'), false, 'CART'),
74
-				'PAY_amount'=>new EE_Money_Field('PAY_amount', __('Amount Payment should be for','event_espresso'), false, 0),
69
+				'PAY_ID'=>new EE_Primary_Key_Int_Field('PAY_ID', __('Payment ID', 'event_espresso')),
70
+				'TXN_ID'=>new EE_Foreign_Key_Int_Field('TXN_ID', __('Transaction ID', 'event_espresso'), false, 0, 'Transaction'),
71
+				'STS_ID'=>new EE_Foreign_Key_String_Field('STS_ID', __('Status ID', 'event_espresso'), false, EEM_Payment::status_id_failed, 'Status'),
72
+				'PAY_timestamp'=> new EE_Datetime_Field('PAY_timestamp', __('Timestamp of when payment was attempted', 'event_espresso'), false, EE_Datetime_Field::now, $timezone),
73
+				'PAY_source'=>new EE_All_Caps_Text_Field('PAY_source', __('User-friendly description of payment', 'event_espresso'), false, 'CART'),
74
+				'PAY_amount'=>new EE_Money_Field('PAY_amount', __('Amount Payment should be for', 'event_espresso'), false, 0),
75 75
 				'PMD_ID'=>new EE_Foreign_Key_Int_Field('PMD_ID', __("Payment Method ID", 'event_espresso'), false, NULL, 'Payment_Method'),
76
-				'PAY_gateway_response'=>new EE_Plain_Text_Field('PAY_gateway_response', __('Response from Gateway about the payment','event_espresso'), false, ''),
77
-				'PAY_txn_id_chq_nmbr'=>new EE_Plain_Text_Field('PAY_txn_id_chq_nmbr', __('Gateway Transaction ID or Cheque Number','event_espresso'), true, ''),
78
-				'PAY_po_number'=>new EE_Plain_Text_Field('PAY_po_number', __('Purchase or Sales Number','event_espresso'), true, ''),
79
-				'PAY_extra_accntng'=>new EE_Simple_HTML_Field('PAY_extra_accntng', __('Extra Account Info','event_espresso'), true, ''),
80
-				'PAY_details'=>new EE_Serialized_Text_Field('PAY_details', __('Full Gateway response about payment','event_espresso'), true, ''),
76
+				'PAY_gateway_response'=>new EE_Plain_Text_Field('PAY_gateway_response', __('Response from Gateway about the payment', 'event_espresso'), false, ''),
77
+				'PAY_txn_id_chq_nmbr'=>new EE_Plain_Text_Field('PAY_txn_id_chq_nmbr', __('Gateway Transaction ID or Cheque Number', 'event_espresso'), true, ''),
78
+				'PAY_po_number'=>new EE_Plain_Text_Field('PAY_po_number', __('Purchase or Sales Number', 'event_espresso'), true, ''),
79
+				'PAY_extra_accntng'=>new EE_Simple_HTML_Field('PAY_extra_accntng', __('Extra Account Info', 'event_espresso'), true, ''),
80
+				'PAY_details'=>new EE_Serialized_Text_Field('PAY_details', __('Full Gateway response about payment', 'event_espresso'), true, ''),
81 81
 				'PAY_redirect_url'=>new EE_Plain_Text_Field('PAY_redirect_url', __("Redirect URL", 'event_espresso'), true),
82 82
 				'PAY_redirect_args'=>new EE_Serialized_Text_Field('PAY_redirect_args', __("Key-Value POST vars to send along with redirect", 'event_espresso'), true)
83 83
 			)
@@ -87,11 +87,11 @@  discard block
 block discarded – undo
87 87
 			'Status'=> new EE_Belongs_To_Relation(),
88 88
 			'Payment_Method'=>new EE_Belongs_To_Relation(),
89 89
 			'Registration_Payment' => new EE_Has_Many_Relation(),
90
-			'Registration' => new EE_HABTM_Relation( 'Registration_Payment' ),
90
+			'Registration' => new EE_HABTM_Relation('Registration_Payment'),
91 91
 		);
92 92
 		$this->_model_chain_to_wp_user = 'Payment_Method';
93 93
 		$this->_caps_slug = 'transactions';
94
-		parent::__construct( $timezone );
94
+		parent::__construct($timezone);
95 95
 	}
96 96
 
97 97
 
@@ -103,7 +103,7 @@  discard block
 block discarded – undo
103 103
 	 * @param string $PAY_txn_id_chq_nmbr
104 104
 	 * @return EE_Payment
105 105
 	 */
106
-	public function get_payment_by_txn_id_chq_nmbr( $PAY_txn_id_chq_nmbr ){
106
+	public function get_payment_by_txn_id_chq_nmbr($PAY_txn_id_chq_nmbr) {
107 107
 		return $this->get_one(array(array('PAY_txn_id_chq_nmbr'=>$PAY_txn_id_chq_nmbr)));
108 108
 	}
109 109
 
@@ -119,15 +119,15 @@  discard block
 block discarded – undo
119 119
 	 *		@param	string	$status_of_payment one of EEM_Payment::status_id_*, like 'PAP','PCN',etc. If none is provided, gets payments with any status
120 120
 	*		@return		EE_Payment[]
121 121
 	*/
122
-	public function get_payments_for_transaction( $TXN_ID = FALSE, $status_of_payment = null ) {
122
+	public function get_payments_for_transaction($TXN_ID = FALSE, $status_of_payment = null) {
123 123
 		// all payments for a TXN ordered chronologically
124
-		$query_params = array( array( 'TXN_ID' => $TXN_ID ), 'order_by' => array( 'PAY_timestamp' => 'ASC' ));
124
+		$query_params = array(array('TXN_ID' => $TXN_ID), 'order_by' => array('PAY_timestamp' => 'ASC'));
125 125
 		// if provided with a status, search specifically for that status. Otherwise get them all
126
-		if ( $status_of_payment ){
126
+		if ($status_of_payment) {
127 127
 			$query_params[0]['STS_ID'] = $status_of_payment;
128 128
 		}
129 129
 		// retrieve payments
130
-		return $this->get_all ( $query_params );
130
+		return $this->get_all($query_params);
131 131
 	}
132 132
 
133 133
 
@@ -137,8 +137,8 @@  discard block
 block discarded – undo
137 137
 	 * @param int $TXN_ID
138 138
 	 * @return EE_Payment[]
139 139
 	 */
140
-	public function get_approved_payments_for_transaction( $TXN_ID = 0 ) {
141
-		return $this->get_payments_for_transaction( $TXN_ID, EEM_Payment::status_id_approved );
140
+	public function get_approved_payments_for_transaction($TXN_ID = 0) {
141
+		return $this->get_payments_for_transaction($TXN_ID, EEM_Payment::status_id_approved);
142 142
 
143 143
 	}
144 144
 
@@ -159,36 +159,36 @@  discard block
 block discarded – undo
159 159
 	 *
160 160
 	 * @return EE_Payment[]
161 161
 	 */
162
-	public function get_payments_made_between_dates( $start_date = '', $end_date = '', $format = '', $timezone = '' ) {
163
-		$timezone = empty( $timezone ) ? EEH_DTT_Helper::get_timezone() : $timezone;
162
+	public function get_payments_made_between_dates($start_date = '', $end_date = '', $format = '', $timezone = '') {
163
+		$timezone = empty($timezone) ? EEH_DTT_Helper::get_timezone() : $timezone;
164 164
 		//if $start_date or $end date, verify $format is included.
165
-		if ( ( ! empty( $start_date ) || ! empty( $end_date ) ) && empty( $format ) ) {
166
-			throw new EE_Error( __('You included a start date and/or a end date for this method but did not include a format string.  The format string is needed for setting up the query', 'event_espresso' ) );
165
+		if (( ! empty($start_date) || ! empty($end_date)) && empty($format)) {
166
+			throw new EE_Error(__('You included a start date and/or a end date for this method but did not include a format string.  The format string is needed for setting up the query', 'event_espresso'));
167 167
 		}
168
-		$now = new DateTime( 'now' );
168
+		$now = new DateTime('now');
169 169
 		// setup timezone objects once
170
-		$modelDateTimeZone = new DateTimeZone( $this->_timezone );
171
-		$passedDateTimeZone = new DateTimeZone( $timezone );
170
+		$modelDateTimeZone = new DateTimeZone($this->_timezone);
171
+		$passedDateTimeZone = new DateTimeZone($timezone);
172 172
 		// setup start date
173
-		$start_date = ! empty( $start_date ) ? date_create_from_format( $format, $start_date, $passedDateTimeZone ) : $now;
173
+		$start_date = ! empty($start_date) ? date_create_from_format($format, $start_date, $passedDateTimeZone) : $now;
174 174
 		EEH_DTT_Helper::setTimezone($start_date, $modelDateTimeZone);
175
-		$start_date = $start_date->format( 'Y-m-d' ) . ' 00:00:00';
176
-		$start_date = strtotime( $start_date );
175
+		$start_date = $start_date->format('Y-m-d').' 00:00:00';
176
+		$start_date = strtotime($start_date);
177 177
 		// setup end date
178
-		$end_date = ! empty( $end_date ) ? date_create_from_format( $format, $end_date, $passedDateTimeZone ) : $now;
178
+		$end_date = ! empty($end_date) ? date_create_from_format($format, $end_date, $passedDateTimeZone) : $now;
179 179
 		EEH_DTT_Helper::setTimezone($end_date, $modelDateTimeZone);
180
-		$end_date = $end_date->format('Y-m-d') . ' 23:59:59';
181
-		$end_date = strtotime( $end_date );
180
+		$end_date = $end_date->format('Y-m-d').' 23:59:59';
181
+		$end_date = strtotime($end_date);
182 182
 
183 183
 		// make sure our start date is the lowest value and vice versa
184
-		$start = min( $start_date, $end_date );
185
-		$end = max( $start_date, $end_date );
184
+		$start = min($start_date, $end_date);
185
+		$end = max($start_date, $end_date);
186 186
 
187 187
 		//yes we generated the date and time string in utc but we WANT this start date and time used in the set timezone on the model.
188
-		$start_date = $this->convert_datetime_for_query( 'PAY_timestamp', date( 'Y-m-d', $start ) . ' 00:00:00', 'Y-m-d H:i:s', $this->get_timezone() );
189
-		$end_date = $this->convert_datetime_for_query( 'PAY_timestamp', date( 'Y-m-d', $end) . ' 23:59:59' , 'Y-m-d H:i:s', $this->get_timezone() );
188
+		$start_date = $this->convert_datetime_for_query('PAY_timestamp', date('Y-m-d', $start).' 00:00:00', 'Y-m-d H:i:s', $this->get_timezone());
189
+		$end_date = $this->convert_datetime_for_query('PAY_timestamp', date('Y-m-d', $end).' 23:59:59', 'Y-m-d H:i:s', $this->get_timezone());
190 190
 
191
-		return $this->get_all(array(array('PAY_timestamp'=>array('>=',$start_date),'PAY_timestamp*'=>array('<=',$end_date))));
191
+		return $this->get_all(array(array('PAY_timestamp'=>array('>=', $start_date), 'PAY_timestamp*'=>array('<=', $end_date))));
192 192
 	}
193 193
 
194 194
 	/**
@@ -198,35 +198,35 @@  discard block
 block discarded – undo
198 198
 	 * returns a string for the approved status
199 199
 	 * @return 	string
200 200
 	 */
201
-	function approved_status(){
201
+	function approved_status() {
202 202
 		return self::status_id_approved;
203 203
 	}
204 204
 	/**
205 205
 	 * returns a string for the pending status
206 206
 	 * @return 	string
207 207
 	 */
208
-	function pending_status(){
208
+	function pending_status() {
209 209
 		return self::status_id_pending;
210 210
 	}
211 211
 	/**
212 212
 	 * returns a string for the cancelled status
213 213
 	 * @return 	string
214 214
 	 */
215
-	function cancelled_status(){
215
+	function cancelled_status() {
216 216
 		return self::status_id_cancelled;
217 217
 	}
218 218
 	/**
219 219
 	 * returns a string for the failed status
220 220
 	 * @return 	string
221 221
 	 */
222
-	function failed_status(){
222
+	function failed_status() {
223 223
 		return self::status_id_failed;
224 224
 	}
225 225
 	/**
226 226
 	 * returns a string for the declined status
227 227
 	 * @return 	string
228 228
 	 */
229
-	function declined_status(){
229
+	function declined_status() {
230 230
 		return self::status_id_declined;
231 231
 	}
232 232
 
Please login to merge, or discard this patch.
core/db_models/fields/EE_Datetime_Field.php 2 patches
Indentation   +742 added lines, -742 removed lines patch added patch discarded remove patch
@@ -17,747 +17,747 @@
 block discarded – undo
17 17
 class EE_Datetime_Field extends EE_Model_Field_Base
18 18
 {
19 19
 
20
-    /**
21
-     * The pattern we're looking for is if only the characters 0-9 are found and there are only
22
-     * 10 or more numbers (because 9 numbers even with all 9's would be sometime in 2001 )
23
-     *
24
-     * @type string unix_timestamp_regex
25
-     */
26
-    const unix_timestamp_regex = '/[0-9]{10,}/';
27
-
28
-    /**
29
-     * @type string mysql_timestamp_format
30
-     */
31
-    const mysql_timestamp_format = 'Y-m-d H:i:s';
32
-
33
-    /**
34
-     * @type string mysql_date_format
35
-     */
36
-    const mysql_date_format = 'Y-m-d';
37
-
38
-    /**
39
-     * @type string mysql_time_format
40
-     */
41
-    const mysql_time_format = 'H:i:s';
42
-
43
-    /**
44
-     * Const for using in the default value. If the field's default is set to this,
45
-     * then we will return the time of calling `get_default_value()`, not
46
-     * just the current time at construction
47
-     */
48
-    const now = 'now';
49
-
50
-    /**
51
-     * The following properties hold the default formats for date and time.
52
-     * Defaults are set via the constructor and can be overridden on class instantiation.
53
-     * However they can also be overridden later by the set_format() method
54
-     * (and corresponding set_date_format, set_time_format methods);
55
-     */
56
-    /**
57
-     * @type string $_date_format
58
-     */
59
-    protected $_date_format = '';
60
-
61
-    /**
62
-     * @type string $_time_format
63
-     */
64
-    protected $_time_format = '';
65
-
66
-    /**
67
-     * @type string $_pretty_date_format
68
-     */
69
-    protected $_pretty_date_format = '';
70
-
71
-    /**
72
-     * @type string $_pretty_time_format
73
-     */
74
-    protected $_pretty_time_format = '';
75
-
76
-    /**
77
-     * @type DateTimeZone $_DateTimeZone
78
-     */
79
-    protected $_DateTimeZone;
80
-
81
-    /**
82
-     * @type DateTimeZone $_UTC_DateTimeZone
83
-     */
84
-    protected $_UTC_DateTimeZone;
85
-
86
-    /**
87
-     * @type DateTimeZone $_blog_DateTimeZone
88
-     */
89
-    protected $_blog_DateTimeZone;
90
-
91
-
92
-    /**
93
-     * This property holds how we want the output returned when getting a datetime string.  It is set for the
94
-     * set_date_time_output() method.  By default this is empty.  When empty, we are assuming that we want both date
95
-     * and time returned via getters.
96
-     *
97
-     * @var mixed (null|string)
98
-     */
99
-    protected $_date_time_output;
100
-
101
-
102
-    /**
103
-     * timezone string
104
-     * This gets set by the constructor and can be changed by the "set_timezone()" method so that we know what timezone
105
-     * incoming strings|timestamps are in.  This can also be used before a get to set what timezone you want strings
106
-     * coming out of the object to be in.  Default timezone is the current WP timezone option setting
107
-     *
108
-     * @var string
109
-     */
110
-    protected $_timezone_string;
111
-
112
-
113
-    /**
114
-     * This holds whatever UTC offset for the blog (we automatically convert timezone strings into their related
115
-     * offsets for comparison purposes).
116
-     *
117
-     * @var int
118
-     */
119
-    protected $_blog_offset;
120
-
121
-
122
-
123
-    /**
124
-     * @param string $table_column
125
-     * @param string $nice_name
126
-     * @param bool   $nullable
127
-     * @param string $default_value
128
-     * @param string $timezone_string
129
-     * @param string $date_format
130
-     * @param string $time_format
131
-     * @param string $pretty_date_format
132
-     * @param string $pretty_time_format
133
-     * @throws EE_Error
134
-     * @throws InvalidArgumentException
135
-     */
136
-    public function __construct(
137
-        $table_column,
138
-        $nice_name,
139
-        $nullable,
140
-        $default_value,
141
-        $timezone_string = '',
142
-        $date_format = '',
143
-        $time_format = '',
144
-        $pretty_date_format = '',
145
-        $pretty_time_format = ''
146
-    ) {
147
-
148
-        $this->_date_format        = ! empty($date_format) ? $date_format : get_option('date_format');
149
-        $this->_time_format        = ! empty($time_format) ? $time_format : get_option('time_format');
150
-        $this->_pretty_date_format = ! empty($pretty_date_format) ? $pretty_date_format : get_option('date_format');
151
-        $this->_pretty_time_format = ! empty($pretty_time_format) ? $pretty_time_format : get_option('time_format');
152
-
153
-        parent::__construct($table_column, $nice_name, $nullable, $default_value);
154
-        $this->set_timezone($timezone_string);
155
-        $this->setSchemaFormat('date-time');
156
-    }
157
-
158
-
159
-    /**
160
-     * @return DateTimeZone
161
-     * @throws \EE_Error
162
-     */
163
-    public function get_UTC_DateTimeZone()
164
-    {
165
-        return $this->_UTC_DateTimeZone instanceof DateTimeZone
166
-            ? $this->_UTC_DateTimeZone
167
-            : $this->_create_timezone_object_from_timezone_string('UTC');
168
-    }
169
-
170
-
171
-    /**
172
-     * @return DateTimeZone
173
-     * @throws \EE_Error
174
-     */
175
-    public function get_blog_DateTimeZone()
176
-    {
177
-        return $this->_blog_DateTimeZone instanceof DateTimeZone
178
-            ? $this->_blog_DateTimeZone
179
-            : $this->_create_timezone_object_from_timezone_string('');
180
-    }
181
-
182
-
183
-    /**
184
-     * this prepares any incoming date data and make sure its converted to a utc unix timestamp
185
-     *
186
-     * @param  string|int $value_inputted_for_field_on_model_object could be a string formatted date time or int unix
187
-     *                                                              timestamp
188
-     * @return DateTime
189
-     */
190
-    public function prepare_for_set($value_inputted_for_field_on_model_object)
191
-    {
192
-        return $this->_get_date_object($value_inputted_for_field_on_model_object);
193
-    }
194
-
195
-
196
-    /**
197
-     * This returns the format string to be used by getters depending on what the $_date_time_output property is set at.
198
-     * getters need to know whether we're just returning the date or the time or both.  By default we return both.
199
-     *
200
-     * @param bool $pretty If we're returning the pretty formats or standard format string.
201
-     * @return string    The final assembled format string.
202
-     */
203
-    protected function _get_date_time_output($pretty = false)
204
-    {
205
-
206
-        switch ($this->_date_time_output) {
207
-            case 'time' :
208
-                return $pretty ? $this->_pretty_time_format : $this->_time_format;
209
-                break;
210
-
211
-            case 'date' :
212
-                return $pretty ? $this->_pretty_date_format : $this->_date_format;
213
-                break;
214
-
215
-            default :
216
-                return $pretty
217
-                    ? $this->_pretty_date_format . ' ' . $this->_pretty_time_format
218
-                    : $this->_date_format . ' ' . $this->_time_format;
219
-        }
220
-    }
221
-
222
-
223
-    /**
224
-     * This just sets the $_date_time_output property so we can flag how date and times are formatted before being
225
-     * returned (using the format properties)
226
-     *
227
-     * @param string $what acceptable values are 'time' or 'date'.
228
-     *                     Any other value will be set but will always result
229
-     *                     in both 'date' and 'time' being returned.
230
-     * @return void
231
-     */
232
-    public function set_date_time_output($what = null)
233
-    {
234
-        $this->_date_time_output = $what;
235
-    }
236
-
237
-
238
-    /**
239
-     * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
240
-     * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
241
-     * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp).
242
-     * We also set some other properties in this method.
243
-     *
244
-     * @param string $timezone_string A valid timezone string as described by @link
245
-     *                                http://www.php.net/manual/en/timezones.php
246
-     * @return void
247
-     * @throws InvalidArgumentException
248
-     * @throws InvalidDataTypeException
249
-     * @throws InvalidInterfaceException
250
-     */
251
-    public function set_timezone($timezone_string)
252
-    {
253
-        if (empty($timezone_string) && $this->_timezone_string !== null) {
254
-            // leave the timezone AS-IS if we already have one and
255
-            // the function arg didn't provide one
256
-            return;
257
-        }
258
-        $timezone_string        = EEH_DTT_Helper::get_valid_timezone_string($timezone_string);
259
-        $this->_timezone_string = ! empty($timezone_string) ? $timezone_string : 'UTC';
260
-        $this->_DateTimeZone    = $this->_create_timezone_object_from_timezone_string($this->_timezone_string);
261
-    }
262
-
263
-
264
-    /**
265
-     * _create_timezone_object_from_timezone_name
266
-     *
267
-     * @access protected
268
-     * @param string $timezone_string
269
-     * @return \DateTimeZone
270
-     * @throws InvalidArgumentException
271
-     * @throws InvalidDataTypeException
272
-     * @throws InvalidInterfaceException
273
-     */
274
-    protected function _create_timezone_object_from_timezone_string($timezone_string = '')
275
-    {
276
-        return new DateTimeZone(EEH_DTT_Helper::get_valid_timezone_string($timezone_string));
277
-    }
278
-
279
-
280
-    /**
281
-     * This just returns whatever is set for the current timezone.
282
-     *
283
-     * @access public
284
-     * @return string timezone string
285
-     */
286
-    public function get_timezone()
287
-    {
288
-        return $this->_timezone_string;
289
-    }
290
-
291
-
292
-    /**
293
-     * set the $_date_format property
294
-     *
295
-     * @access public
296
-     * @param string $format a new date format (corresponding to formats accepted by PHP date() function)
297
-     * @param bool   $pretty Whether to set pretty format or not.
298
-     * @return void
299
-     */
300
-    public function set_date_format($format, $pretty = false)
301
-    {
302
-        if ($pretty) {
303
-            $this->_pretty_date_format = $format;
304
-        } else {
305
-            $this->_date_format = $format;
306
-        }
307
-    }
308
-
309
-
310
-    /**
311
-     * return the $_date_format property value.
312
-     *
313
-     * @param bool $pretty Whether to get pretty format or not.
314
-     * @return string
315
-     */
316
-    public function get_date_format($pretty = false)
317
-    {
318
-        return $pretty ? $this->_pretty_date_format : $this->_date_format;
319
-    }
320
-
321
-
322
-    /**
323
-     * set the $_time_format property
324
-     *
325
-     * @access public
326
-     * @param string $format a new time format (corresponding to formats accepted by PHP date() function)
327
-     * @param bool   $pretty Whether to set pretty format or not.
328
-     * @return void
329
-     */
330
-    public function set_time_format($format, $pretty = false)
331
-    {
332
-        if ($pretty) {
333
-            $this->_pretty_time_format = $format;
334
-        } else {
335
-            $this->_time_format = $format;
336
-        }
337
-    }
338
-
339
-
340
-    /**
341
-     * return the $_time_format property value.
342
-     *
343
-     * @param bool $pretty Whether to get pretty format or not.
344
-     * @return string
345
-     */
346
-    public function get_time_format($pretty = false)
347
-    {
348
-        return $pretty ? $this->_pretty_time_format : $this->_time_format;
349
-    }
350
-
351
-
352
-    /**
353
-     * set the $_pretty_date_format property
354
-     *
355
-     * @access public
356
-     * @param string $format a new pretty date format (corresponding to formats accepted by PHP date() function)
357
-     * @return void
358
-     */
359
-    public function set_pretty_date_format($format)
360
-    {
361
-        $this->_pretty_date_format = $format;
362
-    }
363
-
364
-
365
-    /**
366
-     * set the $_pretty_time_format property
367
-     *
368
-     * @access public
369
-     * @param string $format a new pretty time format (corresponding to formats accepted by PHP date() function)
370
-     * @return void
371
-     */
372
-    public function set_pretty_time_format($format)
373
-    {
374
-        $this->_pretty_time_format = $format;
375
-    }
376
-
377
-
378
-    /**
379
-     * Only sets the time portion of the datetime.
380
-     *
381
-     * @param string|DateTime $time_to_set_string like 8am OR a DateTime object.
382
-     * @param DateTime        $current            current DateTime object for the datetime field
383
-     * @return DateTime
384
-     */
385
-    public function prepare_for_set_with_new_time($time_to_set_string, DateTime $current)
386
-    {
387
-        // if $time_to_set_string is datetime object, then let's use it to set the parse array.
388
-        // Otherwise parse the string.
389
-        if ($time_to_set_string instanceof DateTime) {
390
-            $parsed = array(
391
-                'hour'   => $time_to_set_string->format('H'),
392
-                'minute' => $time_to_set_string->format('i'),
393
-                'second' => $time_to_set_string->format('s'),
394
-            );
395
-        } else {
396
-            //parse incoming string
397
-            $parsed = date_parse_from_format($this->_time_format, $time_to_set_string);
398
-        }
399
-        EEH_DTT_Helper::setTimezone($current, $this->_DateTimeZone);
400
-        return $current->setTime($parsed['hour'], $parsed['minute'], $parsed['second']);
401
-    }
402
-
403
-
404
-    /**
405
-     * Only sets the date portion of the datetime.
406
-     *
407
-     * @param string|DateTime $date_to_set_string like Friday, January 8th or a DateTime object.
408
-     * @param DateTime        $current            current DateTime object for the datetime field
409
-     * @return DateTime
410
-     */
411
-    public function prepare_for_set_with_new_date($date_to_set_string, DateTime $current)
412
-    {
413
-        // if $time_to_set_string is datetime object, then let's use it to set the parse array.
414
-        // Otherwise parse the string.
415
-        if ($date_to_set_string instanceof DateTime) {
416
-            $parsed = array(
417
-                'year'  => $date_to_set_string->format('Y'),
418
-                'month' => $date_to_set_string->format('m'),
419
-                'day'   => $date_to_set_string->format('d'),
420
-            );
421
-        } else {
422
-            //parse incoming string
423
-            $parsed = date_parse_from_format($this->_date_format, $date_to_set_string);
424
-        }
425
-        EEH_DTT_Helper::setTimezone($current, $this->_DateTimeZone);
426
-        return $current->setDate($parsed['year'], $parsed['month'], $parsed['day']);
427
-    }
428
-
429
-
430
-    /**
431
-     * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0 timezone).  When the
432
-     * datetime gets to this stage it should ALREADY be in UTC time
433
-     *
434
-     * @param  DateTime $DateTime
435
-     * @return string formatted date time for given timezone
436
-     * @throws \EE_Error
437
-     */
438
-    public function prepare_for_get($DateTime)
439
-    {
440
-        return $this->_prepare_for_display($DateTime);
441
-    }
442
-
443
-
444
-    /**
445
-     * This differs from prepare_for_get in that it considers whether the internal $_timezone differs
446
-     * from the set wp timezone.  If so, then it returns the datetime string formatted via
447
-     * _pretty_date_format, and _pretty_time_format.  However, it also appends a timezone
448
-     * abbreviation to the date_string.
449
-     *
450
-     * @param mixed $DateTime
451
-     * @param null  $schema
452
-     * @return string
453
-     * @throws \EE_Error
454
-     */
455
-    public function prepare_for_pretty_echoing($DateTime, $schema = null)
456
-    {
457
-        return $this->_prepare_for_display($DateTime, $schema ? $schema : true);
458
-    }
459
-
460
-
461
-    /**
462
-     * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0
463
-     * timezone).
464
-     *
465
-     * @param DateTime    $DateTime
466
-     * @param bool|string $schema
467
-     * @return string
468
-     * @throws \EE_Error
469
-     */
470
-    protected function _prepare_for_display($DateTime, $schema = false)
471
-    {
472
-        if (! $DateTime instanceof DateTime) {
473
-            if ($this->_nullable) {
474
-                return '';
475
-            } else {
476
-                if (WP_DEBUG) {
477
-                    throw new EE_Error(
478
-                        sprintf(
479
-                            __(
480
-                                'EE_Datetime_Field::_prepare_for_display requires a DateTime class to be the value for the $DateTime argument because the %s field is not nullable.',
481
-                                'event_espresso'
482
-                            ),
483
-                            $this->_nicename
484
-                        )
485
-                    );
486
-                } else {
487
-                    $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now);
488
-                    EE_Error::add_error(
489
-                        sprintf(
490
-                            __(
491
-                                'EE_Datetime_Field::_prepare_for_display requires a DateTime class to be the value for the $DateTime argument because the %s field is not nullable.  When WP_DEBUG is false, the value is set to "now" instead of throwing an exception.',
492
-                                'event_espresso'
493
-                            ),
494
-                            $this->_nicename
495
-                        )
496
-                    );
497
-                }
498
-            }
499
-        }
500
-        $format_string = $this->_get_date_time_output($schema);
501
-        EEH_DTT_Helper::setTimezone($DateTime, $this->_DateTimeZone);
502
-        if ($schema) {
503
-            if ($this->_display_timezone()) {
504
-                //must be explicit because schema could equal true.
505
-                if ($schema === 'no_html') {
506
-                    $timezone_string = ' (' . $DateTime->format('T') . ')';
507
-                } else {
508
-                    $timezone_string = ' <span class="ee_dtt_timezone_string">(' . $DateTime->format('T') . ')</span>';
509
-                }
510
-            } else {
511
-                $timezone_string = '';
512
-            }
513
-
514
-            return $DateTime->format($format_string) . $timezone_string;
515
-        }
516
-        return $DateTime->format($format_string);
517
-    }
518
-
519
-
520
-    /**
521
-     * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0
522
-     * timezone).
523
-     *
524
-     * @param  mixed $datetime_value u
525
-     * @return string mysql timestamp in UTC
526
-     * @throws \EE_Error
527
-     */
528
-    public function prepare_for_use_in_db($datetime_value)
529
-    {
530
-        //we allow an empty value or DateTime object, but nothing else.
531
-        if (! empty($datetime_value) && ! $datetime_value instanceof DateTime) {
532
-            throw new EE_Error(
533
-            	sprintf(
534
-            	    __(
535
-            		    'The incoming value being prepared for setting in the database must either be empty or a php 
20
+	/**
21
+	 * The pattern we're looking for is if only the characters 0-9 are found and there are only
22
+	 * 10 or more numbers (because 9 numbers even with all 9's would be sometime in 2001 )
23
+	 *
24
+	 * @type string unix_timestamp_regex
25
+	 */
26
+	const unix_timestamp_regex = '/[0-9]{10,}/';
27
+
28
+	/**
29
+	 * @type string mysql_timestamp_format
30
+	 */
31
+	const mysql_timestamp_format = 'Y-m-d H:i:s';
32
+
33
+	/**
34
+	 * @type string mysql_date_format
35
+	 */
36
+	const mysql_date_format = 'Y-m-d';
37
+
38
+	/**
39
+	 * @type string mysql_time_format
40
+	 */
41
+	const mysql_time_format = 'H:i:s';
42
+
43
+	/**
44
+	 * Const for using in the default value. If the field's default is set to this,
45
+	 * then we will return the time of calling `get_default_value()`, not
46
+	 * just the current time at construction
47
+	 */
48
+	const now = 'now';
49
+
50
+	/**
51
+	 * The following properties hold the default formats for date and time.
52
+	 * Defaults are set via the constructor and can be overridden on class instantiation.
53
+	 * However they can also be overridden later by the set_format() method
54
+	 * (and corresponding set_date_format, set_time_format methods);
55
+	 */
56
+	/**
57
+	 * @type string $_date_format
58
+	 */
59
+	protected $_date_format = '';
60
+
61
+	/**
62
+	 * @type string $_time_format
63
+	 */
64
+	protected $_time_format = '';
65
+
66
+	/**
67
+	 * @type string $_pretty_date_format
68
+	 */
69
+	protected $_pretty_date_format = '';
70
+
71
+	/**
72
+	 * @type string $_pretty_time_format
73
+	 */
74
+	protected $_pretty_time_format = '';
75
+
76
+	/**
77
+	 * @type DateTimeZone $_DateTimeZone
78
+	 */
79
+	protected $_DateTimeZone;
80
+
81
+	/**
82
+	 * @type DateTimeZone $_UTC_DateTimeZone
83
+	 */
84
+	protected $_UTC_DateTimeZone;
85
+
86
+	/**
87
+	 * @type DateTimeZone $_blog_DateTimeZone
88
+	 */
89
+	protected $_blog_DateTimeZone;
90
+
91
+
92
+	/**
93
+	 * This property holds how we want the output returned when getting a datetime string.  It is set for the
94
+	 * set_date_time_output() method.  By default this is empty.  When empty, we are assuming that we want both date
95
+	 * and time returned via getters.
96
+	 *
97
+	 * @var mixed (null|string)
98
+	 */
99
+	protected $_date_time_output;
100
+
101
+
102
+	/**
103
+	 * timezone string
104
+	 * This gets set by the constructor and can be changed by the "set_timezone()" method so that we know what timezone
105
+	 * incoming strings|timestamps are in.  This can also be used before a get to set what timezone you want strings
106
+	 * coming out of the object to be in.  Default timezone is the current WP timezone option setting
107
+	 *
108
+	 * @var string
109
+	 */
110
+	protected $_timezone_string;
111
+
112
+
113
+	/**
114
+	 * This holds whatever UTC offset for the blog (we automatically convert timezone strings into their related
115
+	 * offsets for comparison purposes).
116
+	 *
117
+	 * @var int
118
+	 */
119
+	protected $_blog_offset;
120
+
121
+
122
+
123
+	/**
124
+	 * @param string $table_column
125
+	 * @param string $nice_name
126
+	 * @param bool   $nullable
127
+	 * @param string $default_value
128
+	 * @param string $timezone_string
129
+	 * @param string $date_format
130
+	 * @param string $time_format
131
+	 * @param string $pretty_date_format
132
+	 * @param string $pretty_time_format
133
+	 * @throws EE_Error
134
+	 * @throws InvalidArgumentException
135
+	 */
136
+	public function __construct(
137
+		$table_column,
138
+		$nice_name,
139
+		$nullable,
140
+		$default_value,
141
+		$timezone_string = '',
142
+		$date_format = '',
143
+		$time_format = '',
144
+		$pretty_date_format = '',
145
+		$pretty_time_format = ''
146
+	) {
147
+
148
+		$this->_date_format        = ! empty($date_format) ? $date_format : get_option('date_format');
149
+		$this->_time_format        = ! empty($time_format) ? $time_format : get_option('time_format');
150
+		$this->_pretty_date_format = ! empty($pretty_date_format) ? $pretty_date_format : get_option('date_format');
151
+		$this->_pretty_time_format = ! empty($pretty_time_format) ? $pretty_time_format : get_option('time_format');
152
+
153
+		parent::__construct($table_column, $nice_name, $nullable, $default_value);
154
+		$this->set_timezone($timezone_string);
155
+		$this->setSchemaFormat('date-time');
156
+	}
157
+
158
+
159
+	/**
160
+	 * @return DateTimeZone
161
+	 * @throws \EE_Error
162
+	 */
163
+	public function get_UTC_DateTimeZone()
164
+	{
165
+		return $this->_UTC_DateTimeZone instanceof DateTimeZone
166
+			? $this->_UTC_DateTimeZone
167
+			: $this->_create_timezone_object_from_timezone_string('UTC');
168
+	}
169
+
170
+
171
+	/**
172
+	 * @return DateTimeZone
173
+	 * @throws \EE_Error
174
+	 */
175
+	public function get_blog_DateTimeZone()
176
+	{
177
+		return $this->_blog_DateTimeZone instanceof DateTimeZone
178
+			? $this->_blog_DateTimeZone
179
+			: $this->_create_timezone_object_from_timezone_string('');
180
+	}
181
+
182
+
183
+	/**
184
+	 * this prepares any incoming date data and make sure its converted to a utc unix timestamp
185
+	 *
186
+	 * @param  string|int $value_inputted_for_field_on_model_object could be a string formatted date time or int unix
187
+	 *                                                              timestamp
188
+	 * @return DateTime
189
+	 */
190
+	public function prepare_for_set($value_inputted_for_field_on_model_object)
191
+	{
192
+		return $this->_get_date_object($value_inputted_for_field_on_model_object);
193
+	}
194
+
195
+
196
+	/**
197
+	 * This returns the format string to be used by getters depending on what the $_date_time_output property is set at.
198
+	 * getters need to know whether we're just returning the date or the time or both.  By default we return both.
199
+	 *
200
+	 * @param bool $pretty If we're returning the pretty formats or standard format string.
201
+	 * @return string    The final assembled format string.
202
+	 */
203
+	protected function _get_date_time_output($pretty = false)
204
+	{
205
+
206
+		switch ($this->_date_time_output) {
207
+			case 'time' :
208
+				return $pretty ? $this->_pretty_time_format : $this->_time_format;
209
+				break;
210
+
211
+			case 'date' :
212
+				return $pretty ? $this->_pretty_date_format : $this->_date_format;
213
+				break;
214
+
215
+			default :
216
+				return $pretty
217
+					? $this->_pretty_date_format . ' ' . $this->_pretty_time_format
218
+					: $this->_date_format . ' ' . $this->_time_format;
219
+		}
220
+	}
221
+
222
+
223
+	/**
224
+	 * This just sets the $_date_time_output property so we can flag how date and times are formatted before being
225
+	 * returned (using the format properties)
226
+	 *
227
+	 * @param string $what acceptable values are 'time' or 'date'.
228
+	 *                     Any other value will be set but will always result
229
+	 *                     in both 'date' and 'time' being returned.
230
+	 * @return void
231
+	 */
232
+	public function set_date_time_output($what = null)
233
+	{
234
+		$this->_date_time_output = $what;
235
+	}
236
+
237
+
238
+	/**
239
+	 * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
240
+	 * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
241
+	 * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp).
242
+	 * We also set some other properties in this method.
243
+	 *
244
+	 * @param string $timezone_string A valid timezone string as described by @link
245
+	 *                                http://www.php.net/manual/en/timezones.php
246
+	 * @return void
247
+	 * @throws InvalidArgumentException
248
+	 * @throws InvalidDataTypeException
249
+	 * @throws InvalidInterfaceException
250
+	 */
251
+	public function set_timezone($timezone_string)
252
+	{
253
+		if (empty($timezone_string) && $this->_timezone_string !== null) {
254
+			// leave the timezone AS-IS if we already have one and
255
+			// the function arg didn't provide one
256
+			return;
257
+		}
258
+		$timezone_string        = EEH_DTT_Helper::get_valid_timezone_string($timezone_string);
259
+		$this->_timezone_string = ! empty($timezone_string) ? $timezone_string : 'UTC';
260
+		$this->_DateTimeZone    = $this->_create_timezone_object_from_timezone_string($this->_timezone_string);
261
+	}
262
+
263
+
264
+	/**
265
+	 * _create_timezone_object_from_timezone_name
266
+	 *
267
+	 * @access protected
268
+	 * @param string $timezone_string
269
+	 * @return \DateTimeZone
270
+	 * @throws InvalidArgumentException
271
+	 * @throws InvalidDataTypeException
272
+	 * @throws InvalidInterfaceException
273
+	 */
274
+	protected function _create_timezone_object_from_timezone_string($timezone_string = '')
275
+	{
276
+		return new DateTimeZone(EEH_DTT_Helper::get_valid_timezone_string($timezone_string));
277
+	}
278
+
279
+
280
+	/**
281
+	 * This just returns whatever is set for the current timezone.
282
+	 *
283
+	 * @access public
284
+	 * @return string timezone string
285
+	 */
286
+	public function get_timezone()
287
+	{
288
+		return $this->_timezone_string;
289
+	}
290
+
291
+
292
+	/**
293
+	 * set the $_date_format property
294
+	 *
295
+	 * @access public
296
+	 * @param string $format a new date format (corresponding to formats accepted by PHP date() function)
297
+	 * @param bool   $pretty Whether to set pretty format or not.
298
+	 * @return void
299
+	 */
300
+	public function set_date_format($format, $pretty = false)
301
+	{
302
+		if ($pretty) {
303
+			$this->_pretty_date_format = $format;
304
+		} else {
305
+			$this->_date_format = $format;
306
+		}
307
+	}
308
+
309
+
310
+	/**
311
+	 * return the $_date_format property value.
312
+	 *
313
+	 * @param bool $pretty Whether to get pretty format or not.
314
+	 * @return string
315
+	 */
316
+	public function get_date_format($pretty = false)
317
+	{
318
+		return $pretty ? $this->_pretty_date_format : $this->_date_format;
319
+	}
320
+
321
+
322
+	/**
323
+	 * set the $_time_format property
324
+	 *
325
+	 * @access public
326
+	 * @param string $format a new time format (corresponding to formats accepted by PHP date() function)
327
+	 * @param bool   $pretty Whether to set pretty format or not.
328
+	 * @return void
329
+	 */
330
+	public function set_time_format($format, $pretty = false)
331
+	{
332
+		if ($pretty) {
333
+			$this->_pretty_time_format = $format;
334
+		} else {
335
+			$this->_time_format = $format;
336
+		}
337
+	}
338
+
339
+
340
+	/**
341
+	 * return the $_time_format property value.
342
+	 *
343
+	 * @param bool $pretty Whether to get pretty format or not.
344
+	 * @return string
345
+	 */
346
+	public function get_time_format($pretty = false)
347
+	{
348
+		return $pretty ? $this->_pretty_time_format : $this->_time_format;
349
+	}
350
+
351
+
352
+	/**
353
+	 * set the $_pretty_date_format property
354
+	 *
355
+	 * @access public
356
+	 * @param string $format a new pretty date format (corresponding to formats accepted by PHP date() function)
357
+	 * @return void
358
+	 */
359
+	public function set_pretty_date_format($format)
360
+	{
361
+		$this->_pretty_date_format = $format;
362
+	}
363
+
364
+
365
+	/**
366
+	 * set the $_pretty_time_format property
367
+	 *
368
+	 * @access public
369
+	 * @param string $format a new pretty time format (corresponding to formats accepted by PHP date() function)
370
+	 * @return void
371
+	 */
372
+	public function set_pretty_time_format($format)
373
+	{
374
+		$this->_pretty_time_format = $format;
375
+	}
376
+
377
+
378
+	/**
379
+	 * Only sets the time portion of the datetime.
380
+	 *
381
+	 * @param string|DateTime $time_to_set_string like 8am OR a DateTime object.
382
+	 * @param DateTime        $current            current DateTime object for the datetime field
383
+	 * @return DateTime
384
+	 */
385
+	public function prepare_for_set_with_new_time($time_to_set_string, DateTime $current)
386
+	{
387
+		// if $time_to_set_string is datetime object, then let's use it to set the parse array.
388
+		// Otherwise parse the string.
389
+		if ($time_to_set_string instanceof DateTime) {
390
+			$parsed = array(
391
+				'hour'   => $time_to_set_string->format('H'),
392
+				'minute' => $time_to_set_string->format('i'),
393
+				'second' => $time_to_set_string->format('s'),
394
+			);
395
+		} else {
396
+			//parse incoming string
397
+			$parsed = date_parse_from_format($this->_time_format, $time_to_set_string);
398
+		}
399
+		EEH_DTT_Helper::setTimezone($current, $this->_DateTimeZone);
400
+		return $current->setTime($parsed['hour'], $parsed['minute'], $parsed['second']);
401
+	}
402
+
403
+
404
+	/**
405
+	 * Only sets the date portion of the datetime.
406
+	 *
407
+	 * @param string|DateTime $date_to_set_string like Friday, January 8th or a DateTime object.
408
+	 * @param DateTime        $current            current DateTime object for the datetime field
409
+	 * @return DateTime
410
+	 */
411
+	public function prepare_for_set_with_new_date($date_to_set_string, DateTime $current)
412
+	{
413
+		// if $time_to_set_string is datetime object, then let's use it to set the parse array.
414
+		// Otherwise parse the string.
415
+		if ($date_to_set_string instanceof DateTime) {
416
+			$parsed = array(
417
+				'year'  => $date_to_set_string->format('Y'),
418
+				'month' => $date_to_set_string->format('m'),
419
+				'day'   => $date_to_set_string->format('d'),
420
+			);
421
+		} else {
422
+			//parse incoming string
423
+			$parsed = date_parse_from_format($this->_date_format, $date_to_set_string);
424
+		}
425
+		EEH_DTT_Helper::setTimezone($current, $this->_DateTimeZone);
426
+		return $current->setDate($parsed['year'], $parsed['month'], $parsed['day']);
427
+	}
428
+
429
+
430
+	/**
431
+	 * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0 timezone).  When the
432
+	 * datetime gets to this stage it should ALREADY be in UTC time
433
+	 *
434
+	 * @param  DateTime $DateTime
435
+	 * @return string formatted date time for given timezone
436
+	 * @throws \EE_Error
437
+	 */
438
+	public function prepare_for_get($DateTime)
439
+	{
440
+		return $this->_prepare_for_display($DateTime);
441
+	}
442
+
443
+
444
+	/**
445
+	 * This differs from prepare_for_get in that it considers whether the internal $_timezone differs
446
+	 * from the set wp timezone.  If so, then it returns the datetime string formatted via
447
+	 * _pretty_date_format, and _pretty_time_format.  However, it also appends a timezone
448
+	 * abbreviation to the date_string.
449
+	 *
450
+	 * @param mixed $DateTime
451
+	 * @param null  $schema
452
+	 * @return string
453
+	 * @throws \EE_Error
454
+	 */
455
+	public function prepare_for_pretty_echoing($DateTime, $schema = null)
456
+	{
457
+		return $this->_prepare_for_display($DateTime, $schema ? $schema : true);
458
+	}
459
+
460
+
461
+	/**
462
+	 * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0
463
+	 * timezone).
464
+	 *
465
+	 * @param DateTime    $DateTime
466
+	 * @param bool|string $schema
467
+	 * @return string
468
+	 * @throws \EE_Error
469
+	 */
470
+	protected function _prepare_for_display($DateTime, $schema = false)
471
+	{
472
+		if (! $DateTime instanceof DateTime) {
473
+			if ($this->_nullable) {
474
+				return '';
475
+			} else {
476
+				if (WP_DEBUG) {
477
+					throw new EE_Error(
478
+						sprintf(
479
+							__(
480
+								'EE_Datetime_Field::_prepare_for_display requires a DateTime class to be the value for the $DateTime argument because the %s field is not nullable.',
481
+								'event_espresso'
482
+							),
483
+							$this->_nicename
484
+						)
485
+					);
486
+				} else {
487
+					$DateTime = new DbSafeDateTime(\EE_Datetime_Field::now);
488
+					EE_Error::add_error(
489
+						sprintf(
490
+							__(
491
+								'EE_Datetime_Field::_prepare_for_display requires a DateTime class to be the value for the $DateTime argument because the %s field is not nullable.  When WP_DEBUG is false, the value is set to "now" instead of throwing an exception.',
492
+								'event_espresso'
493
+							),
494
+							$this->_nicename
495
+						)
496
+					);
497
+				}
498
+			}
499
+		}
500
+		$format_string = $this->_get_date_time_output($schema);
501
+		EEH_DTT_Helper::setTimezone($DateTime, $this->_DateTimeZone);
502
+		if ($schema) {
503
+			if ($this->_display_timezone()) {
504
+				//must be explicit because schema could equal true.
505
+				if ($schema === 'no_html') {
506
+					$timezone_string = ' (' . $DateTime->format('T') . ')';
507
+				} else {
508
+					$timezone_string = ' <span class="ee_dtt_timezone_string">(' . $DateTime->format('T') . ')</span>';
509
+				}
510
+			} else {
511
+				$timezone_string = '';
512
+			}
513
+
514
+			return $DateTime->format($format_string) . $timezone_string;
515
+		}
516
+		return $DateTime->format($format_string);
517
+	}
518
+
519
+
520
+	/**
521
+	 * This prepares the EE_DateTime value to be saved to the db as mysql timestamp (UTC +0
522
+	 * timezone).
523
+	 *
524
+	 * @param  mixed $datetime_value u
525
+	 * @return string mysql timestamp in UTC
526
+	 * @throws \EE_Error
527
+	 */
528
+	public function prepare_for_use_in_db($datetime_value)
529
+	{
530
+		//we allow an empty value or DateTime object, but nothing else.
531
+		if (! empty($datetime_value) && ! $datetime_value instanceof DateTime) {
532
+			throw new EE_Error(
533
+				sprintf(
534
+					__(
535
+						'The incoming value being prepared for setting in the database must either be empty or a php 
536 536
             		    DateTime object, instead of: %1$s %2$s',
537
-                        'event_espresso'
538
-	                ),
539
-                    '<br />',
540
-                    print_r($datetime_value, true)
541
-                )
542
-            );
543
-        }
544
-
545
-        if ($datetime_value instanceof DateTime) {
546
-            if (! $datetime_value instanceof DbSafeDateTime) {
547
-                $datetime_value = DbSafeDateTime::createFromDateTime($datetime_value);
548
-            }
549
-            EEH_DTT_Helper::setTimezone($datetime_value, $this->get_UTC_DateTimeZone());
550
-            return $datetime_value->format(
551
-                EE_Datetime_Field::mysql_timestamp_format
552
-            );
553
-        }
554
-
555
-        // if $datetime_value is empty, and ! $this->_nullable, use current_time() but set the GMT flag to true
556
-        return ! $this->_nullable && empty($datetime_value) ? current_time('mysql', true) : null;
557
-    }
558
-
559
-
560
-    /**
561
-     * This prepares the datetime for internal usage as a PHP DateTime object OR null (if nullable is
562
-     * allowed)
563
-     *
564
-     * @param string $datetime_string mysql timestamp in UTC
565
-     * @return  mixed null | DateTime
566
-     * @throws \EE_Error
567
-     */
568
-    public function prepare_for_set_from_db($datetime_string)
569
-    {
570
-        //if $datetime_value is empty, and ! $this->_nullable, just use time()
571
-        if (empty($datetime_string) && $this->_nullable) {
572
-            return null;
573
-        }
574
-        // datetime strings from the db should ALWAYS be in UTC+0, so use UTC_DateTimeZone when creating
575
-        if (empty($datetime_string)) {
576
-            $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->get_UTC_DateTimeZone());
577
-        } else {
578
-            $DateTime = DateTime::createFromFormat(
579
-                EE_Datetime_Field::mysql_timestamp_format,
580
-                $datetime_string,
581
-                $this->get_UTC_DateTimeZone()
582
-            );
583
-            if ($DateTime instanceof \DateTime) {
584
-                $DateTime = new DbSafeDateTime(
585
-                    $DateTime->format(\EE_Datetime_Field::mysql_timestamp_format),
586
-                    $this->get_UTC_DateTimeZone()
587
-                );
588
-            }
589
-        }
590
-
591
-        if (! $DateTime instanceof DbSafeDateTime) {
592
-            // if still no datetime object, then let's just use now
593
-            $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->get_UTC_DateTimeZone());
594
-        }
595
-        // THEN apply the field's set DateTimeZone
596
-        EEH_DTT_Helper::setTimezone($DateTime, $this->_DateTimeZone);
597
-        return $DateTime;
598
-    }
599
-
600
-
601
-    /**
602
-     * All this method does is determine if we're going to display the timezone string or not on any output.
603
-     * To determine this we check if the set timezone offset is different than the blog's set timezone offset.
604
-     * If so, then true.
605
-     *
606
-     * @return bool true for yes false for no
607
-     * @throws \EE_Error
608
-     */
609
-    protected function _display_timezone()
610
-    {
611
-
612
-        // first let's do a comparison of timezone strings.
613
-        // If they match then we can get out without any further calculations
614
-        $blog_string = get_option('timezone_string');
615
-        if ($blog_string === $this->_timezone_string) {
616
-            return false;
617
-        }
618
-        // now we need to calc the offset for the timezone string so we can compare with the blog offset.
619
-        $this_offset = $this->get_timezone_offset($this->_DateTimeZone);
620
-        $blog_offset = $this->get_timezone_offset($this->get_blog_DateTimeZone());
621
-        // now compare
622
-        return $blog_offset !== $this_offset;
623
-    }
624
-
625
-
626
-    /**
627
-     * This method returns a php DateTime object for setting on the EE_Base_Class model.
628
-     * EE passes around DateTime objects because they are MUCH easier to manipulate and deal
629
-     * with.
630
-     *
631
-     * @param int|string|DateTime $date_string            This should be the incoming date string.  It's assumed to be
632
-     *                                                    in the format that is set on the date_field (or DateTime
633
-     *                                                    object)!
634
-     * @return DateTime
635
-     */
636
-    protected function _get_date_object($date_string)
637
-    {
638
-        //first if this is an empty date_string and nullable is allowed, just return null.
639
-        if ($this->_nullable && empty($date_string)) {
640
-            return null;
641
-        }
642
-
643
-        // if incoming date
644
-        if ($date_string instanceof DateTime) {
645
-            EEH_DTT_Helper::setTimezone($date_string, $this->_DateTimeZone);
646
-            return $date_string;
647
-        }
648
-        // if empty date_string and made it here.
649
-        // Return a datetime object for now in the given timezone.
650
-        if (empty($date_string)) {
651
-            return new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
652
-        }
653
-        // if $date_string is matches something that looks like a Unix timestamp let's just use it.
654
-        if (preg_match(EE_Datetime_Field::unix_timestamp_regex, $date_string)) {
655
-            try {
656
-                // This is operating under the assumption that the incoming Unix timestamp
657
-                // is an ACTUAL Unix timestamp and not the calculated one output by current_time('timestamp');
658
-                $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
659
-                $DateTime->setTimestamp($date_string);
660
-
661
-                return $DateTime;
662
-            } catch (Exception $e) {
663
-                // should be rare, but if things got fooled then let's just continue
664
-            }
665
-        }
666
-        //not a unix timestamp.  So we will use the set format on this object and set timezone to
667
-        //create the DateTime object.
668
-        $format = $this->_date_format . ' ' . $this->_time_format;
669
-        try {
670
-            $DateTime = DateTime::createFromFormat($format, $date_string, $this->_DateTimeZone);
671
-            if ($DateTime instanceof DateTime) {
672
-                $DateTime = new DbSafeDateTime(
673
-                    $DateTime->format(\EE_Datetime_Field::mysql_timestamp_format),
674
-                    $this->_DateTimeZone
675
-                );
676
-            }
677
-            if (! $DateTime instanceof DbSafeDateTime) {
678
-                throw new EE_Error(
679
-                    sprintf(
680
-                        __('"%1$s" does not represent a valid Date Time in the format "%2$s".', 'event_espresso'),
681
-                        $date_string,
682
-                        $format
683
-                    )
684
-                );
685
-            }
686
-        } catch (Exception $e) {
687
-            // if we made it here then likely then something went really wrong.
688
-            // Instead of throwing an exception, let's just return a DateTime object for now, in the set timezone.
689
-            $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
690
-        }
691
-
692
-        return $DateTime;
693
-    }
694
-
695
-
696
-
697
-    /**
698
-     * get_timezone_transitions
699
-     *
700
-     * @param \DateTimeZone $DateTimeZone
701
-     * @param int           $time
702
-     * @param bool          $first_only
703
-     * @return mixed
704
-     */
705
-    public function get_timezone_transitions(DateTimeZone $DateTimeZone, $time = null, $first_only = true)
706
-    {
707
-        return EEH_DTT_Helper::get_timezone_transitions($DateTimeZone, $time, $first_only);
708
-    }
709
-
710
-
711
-
712
-    /**
713
-     * get_timezone_offset
714
-     *
715
-     * @param \DateTimeZone $DateTimeZone
716
-     * @param int           $time
717
-     * @return mixed
718
-     * @throws \DomainException
719
-     */
720
-    public function get_timezone_offset(DateTimeZone $DateTimeZone, $time = null)
721
-    {
722
-        return EEH_DTT_Helper::get_timezone_offset($DateTimeZone, $time);
723
-    }
724
-
725
-
726
-    /**
727
-     * This will take an incoming timezone string and return the abbreviation for that timezone
728
-     *
729
-     * @param  string $timezone_string
730
-     * @return string           abbreviation
731
-     * @throws \EE_Error
732
-     */
733
-    public function get_timezone_abbrev($timezone_string)
734
-    {
735
-        $timezone_string = EEH_DTT_Helper::get_valid_timezone_string($timezone_string);
736
-        $dateTime        = new DateTime(\EE_Datetime_Field::now, new DateTimeZone($timezone_string));
737
-
738
-        return $dateTime->format('T');
739
-    }
740
-
741
-    /**
742
-     * Overrides the parent to allow for having a dynamic "now" value
743
-     *
744
-     * @return mixed
745
-     */
746
-    public function get_default_value()
747
-    {
748
-        if ($this->_default_value === EE_Datetime_Field::now) {
749
-            return time();
750
-        } else {
751
-            return parent::get_default_value();
752
-        }
753
-    }
754
-
755
-
756
-    public function getSchemaDescription()
757
-    {
758
-        return sprintf(
759
-            esc_html__('%s - the value for this field is in the timezone of the site.', 'event_espresso'),
760
-            $this->get_nicename()
761
-        );
762
-    }
537
+						'event_espresso'
538
+					),
539
+					'<br />',
540
+					print_r($datetime_value, true)
541
+				)
542
+			);
543
+		}
544
+
545
+		if ($datetime_value instanceof DateTime) {
546
+			if (! $datetime_value instanceof DbSafeDateTime) {
547
+				$datetime_value = DbSafeDateTime::createFromDateTime($datetime_value);
548
+			}
549
+			EEH_DTT_Helper::setTimezone($datetime_value, $this->get_UTC_DateTimeZone());
550
+			return $datetime_value->format(
551
+				EE_Datetime_Field::mysql_timestamp_format
552
+			);
553
+		}
554
+
555
+		// if $datetime_value is empty, and ! $this->_nullable, use current_time() but set the GMT flag to true
556
+		return ! $this->_nullable && empty($datetime_value) ? current_time('mysql', true) : null;
557
+	}
558
+
559
+
560
+	/**
561
+	 * This prepares the datetime for internal usage as a PHP DateTime object OR null (if nullable is
562
+	 * allowed)
563
+	 *
564
+	 * @param string $datetime_string mysql timestamp in UTC
565
+	 * @return  mixed null | DateTime
566
+	 * @throws \EE_Error
567
+	 */
568
+	public function prepare_for_set_from_db($datetime_string)
569
+	{
570
+		//if $datetime_value is empty, and ! $this->_nullable, just use time()
571
+		if (empty($datetime_string) && $this->_nullable) {
572
+			return null;
573
+		}
574
+		// datetime strings from the db should ALWAYS be in UTC+0, so use UTC_DateTimeZone when creating
575
+		if (empty($datetime_string)) {
576
+			$DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->get_UTC_DateTimeZone());
577
+		} else {
578
+			$DateTime = DateTime::createFromFormat(
579
+				EE_Datetime_Field::mysql_timestamp_format,
580
+				$datetime_string,
581
+				$this->get_UTC_DateTimeZone()
582
+			);
583
+			if ($DateTime instanceof \DateTime) {
584
+				$DateTime = new DbSafeDateTime(
585
+					$DateTime->format(\EE_Datetime_Field::mysql_timestamp_format),
586
+					$this->get_UTC_DateTimeZone()
587
+				);
588
+			}
589
+		}
590
+
591
+		if (! $DateTime instanceof DbSafeDateTime) {
592
+			// if still no datetime object, then let's just use now
593
+			$DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->get_UTC_DateTimeZone());
594
+		}
595
+		// THEN apply the field's set DateTimeZone
596
+		EEH_DTT_Helper::setTimezone($DateTime, $this->_DateTimeZone);
597
+		return $DateTime;
598
+	}
599
+
600
+
601
+	/**
602
+	 * All this method does is determine if we're going to display the timezone string or not on any output.
603
+	 * To determine this we check if the set timezone offset is different than the blog's set timezone offset.
604
+	 * If so, then true.
605
+	 *
606
+	 * @return bool true for yes false for no
607
+	 * @throws \EE_Error
608
+	 */
609
+	protected function _display_timezone()
610
+	{
611
+
612
+		// first let's do a comparison of timezone strings.
613
+		// If they match then we can get out without any further calculations
614
+		$blog_string = get_option('timezone_string');
615
+		if ($blog_string === $this->_timezone_string) {
616
+			return false;
617
+		}
618
+		// now we need to calc the offset for the timezone string so we can compare with the blog offset.
619
+		$this_offset = $this->get_timezone_offset($this->_DateTimeZone);
620
+		$blog_offset = $this->get_timezone_offset($this->get_blog_DateTimeZone());
621
+		// now compare
622
+		return $blog_offset !== $this_offset;
623
+	}
624
+
625
+
626
+	/**
627
+	 * This method returns a php DateTime object for setting on the EE_Base_Class model.
628
+	 * EE passes around DateTime objects because they are MUCH easier to manipulate and deal
629
+	 * with.
630
+	 *
631
+	 * @param int|string|DateTime $date_string            This should be the incoming date string.  It's assumed to be
632
+	 *                                                    in the format that is set on the date_field (or DateTime
633
+	 *                                                    object)!
634
+	 * @return DateTime
635
+	 */
636
+	protected function _get_date_object($date_string)
637
+	{
638
+		//first if this is an empty date_string and nullable is allowed, just return null.
639
+		if ($this->_nullable && empty($date_string)) {
640
+			return null;
641
+		}
642
+
643
+		// if incoming date
644
+		if ($date_string instanceof DateTime) {
645
+			EEH_DTT_Helper::setTimezone($date_string, $this->_DateTimeZone);
646
+			return $date_string;
647
+		}
648
+		// if empty date_string and made it here.
649
+		// Return a datetime object for now in the given timezone.
650
+		if (empty($date_string)) {
651
+			return new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
652
+		}
653
+		// if $date_string is matches something that looks like a Unix timestamp let's just use it.
654
+		if (preg_match(EE_Datetime_Field::unix_timestamp_regex, $date_string)) {
655
+			try {
656
+				// This is operating under the assumption that the incoming Unix timestamp
657
+				// is an ACTUAL Unix timestamp and not the calculated one output by current_time('timestamp');
658
+				$DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
659
+				$DateTime->setTimestamp($date_string);
660
+
661
+				return $DateTime;
662
+			} catch (Exception $e) {
663
+				// should be rare, but if things got fooled then let's just continue
664
+			}
665
+		}
666
+		//not a unix timestamp.  So we will use the set format on this object and set timezone to
667
+		//create the DateTime object.
668
+		$format = $this->_date_format . ' ' . $this->_time_format;
669
+		try {
670
+			$DateTime = DateTime::createFromFormat($format, $date_string, $this->_DateTimeZone);
671
+			if ($DateTime instanceof DateTime) {
672
+				$DateTime = new DbSafeDateTime(
673
+					$DateTime->format(\EE_Datetime_Field::mysql_timestamp_format),
674
+					$this->_DateTimeZone
675
+				);
676
+			}
677
+			if (! $DateTime instanceof DbSafeDateTime) {
678
+				throw new EE_Error(
679
+					sprintf(
680
+						__('"%1$s" does not represent a valid Date Time in the format "%2$s".', 'event_espresso'),
681
+						$date_string,
682
+						$format
683
+					)
684
+				);
685
+			}
686
+		} catch (Exception $e) {
687
+			// if we made it here then likely then something went really wrong.
688
+			// Instead of throwing an exception, let's just return a DateTime object for now, in the set timezone.
689
+			$DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->_DateTimeZone);
690
+		}
691
+
692
+		return $DateTime;
693
+	}
694
+
695
+
696
+
697
+	/**
698
+	 * get_timezone_transitions
699
+	 *
700
+	 * @param \DateTimeZone $DateTimeZone
701
+	 * @param int           $time
702
+	 * @param bool          $first_only
703
+	 * @return mixed
704
+	 */
705
+	public function get_timezone_transitions(DateTimeZone $DateTimeZone, $time = null, $first_only = true)
706
+	{
707
+		return EEH_DTT_Helper::get_timezone_transitions($DateTimeZone, $time, $first_only);
708
+	}
709
+
710
+
711
+
712
+	/**
713
+	 * get_timezone_offset
714
+	 *
715
+	 * @param \DateTimeZone $DateTimeZone
716
+	 * @param int           $time
717
+	 * @return mixed
718
+	 * @throws \DomainException
719
+	 */
720
+	public function get_timezone_offset(DateTimeZone $DateTimeZone, $time = null)
721
+	{
722
+		return EEH_DTT_Helper::get_timezone_offset($DateTimeZone, $time);
723
+	}
724
+
725
+
726
+	/**
727
+	 * This will take an incoming timezone string and return the abbreviation for that timezone
728
+	 *
729
+	 * @param  string $timezone_string
730
+	 * @return string           abbreviation
731
+	 * @throws \EE_Error
732
+	 */
733
+	public function get_timezone_abbrev($timezone_string)
734
+	{
735
+		$timezone_string = EEH_DTT_Helper::get_valid_timezone_string($timezone_string);
736
+		$dateTime        = new DateTime(\EE_Datetime_Field::now, new DateTimeZone($timezone_string));
737
+
738
+		return $dateTime->format('T');
739
+	}
740
+
741
+	/**
742
+	 * Overrides the parent to allow for having a dynamic "now" value
743
+	 *
744
+	 * @return mixed
745
+	 */
746
+	public function get_default_value()
747
+	{
748
+		if ($this->_default_value === EE_Datetime_Field::now) {
749
+			return time();
750
+		} else {
751
+			return parent::get_default_value();
752
+		}
753
+	}
754
+
755
+
756
+	public function getSchemaDescription()
757
+	{
758
+		return sprintf(
759
+			esc_html__('%s - the value for this field is in the timezone of the site.', 'event_espresso'),
760
+			$this->get_nicename()
761
+		);
762
+	}
763 763
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -214,8 +214,8 @@  discard block
 block discarded – undo
214 214
 
215 215
             default :
216 216
                 return $pretty
217
-                    ? $this->_pretty_date_format . ' ' . $this->_pretty_time_format
218
-                    : $this->_date_format . ' ' . $this->_time_format;
217
+                    ? $this->_pretty_date_format.' '.$this->_pretty_time_format
218
+                    : $this->_date_format.' '.$this->_time_format;
219 219
         }
220 220
     }
221 221
 
@@ -469,7 +469,7 @@  discard block
 block discarded – undo
469 469
      */
470 470
     protected function _prepare_for_display($DateTime, $schema = false)
471 471
     {
472
-        if (! $DateTime instanceof DateTime) {
472
+        if ( ! $DateTime instanceof DateTime) {
473 473
             if ($this->_nullable) {
474 474
                 return '';
475 475
             } else {
@@ -503,15 +503,15 @@  discard block
 block discarded – undo
503 503
             if ($this->_display_timezone()) {
504 504
                 //must be explicit because schema could equal true.
505 505
                 if ($schema === 'no_html') {
506
-                    $timezone_string = ' (' . $DateTime->format('T') . ')';
506
+                    $timezone_string = ' ('.$DateTime->format('T').')';
507 507
                 } else {
508
-                    $timezone_string = ' <span class="ee_dtt_timezone_string">(' . $DateTime->format('T') . ')</span>';
508
+                    $timezone_string = ' <span class="ee_dtt_timezone_string">('.$DateTime->format('T').')</span>';
509 509
                 }
510 510
             } else {
511 511
                 $timezone_string = '';
512 512
             }
513 513
 
514
-            return $DateTime->format($format_string) . $timezone_string;
514
+            return $DateTime->format($format_string).$timezone_string;
515 515
         }
516 516
         return $DateTime->format($format_string);
517 517
     }
@@ -528,7 +528,7 @@  discard block
 block discarded – undo
528 528
     public function prepare_for_use_in_db($datetime_value)
529 529
     {
530 530
         //we allow an empty value or DateTime object, but nothing else.
531
-        if (! empty($datetime_value) && ! $datetime_value instanceof DateTime) {
531
+        if ( ! empty($datetime_value) && ! $datetime_value instanceof DateTime) {
532 532
             throw new EE_Error(
533 533
             	sprintf(
534 534
             	    __(
@@ -543,7 +543,7 @@  discard block
 block discarded – undo
543 543
         }
544 544
 
545 545
         if ($datetime_value instanceof DateTime) {
546
-            if (! $datetime_value instanceof DbSafeDateTime) {
546
+            if ( ! $datetime_value instanceof DbSafeDateTime) {
547 547
                 $datetime_value = DbSafeDateTime::createFromDateTime($datetime_value);
548 548
             }
549 549
             EEH_DTT_Helper::setTimezone($datetime_value, $this->get_UTC_DateTimeZone());
@@ -588,7 +588,7 @@  discard block
 block discarded – undo
588 588
             }
589 589
         }
590 590
 
591
-        if (! $DateTime instanceof DbSafeDateTime) {
591
+        if ( ! $DateTime instanceof DbSafeDateTime) {
592 592
             // if still no datetime object, then let's just use now
593 593
             $DateTime = new DbSafeDateTime(\EE_Datetime_Field::now, $this->get_UTC_DateTimeZone());
594 594
         }
@@ -665,7 +665,7 @@  discard block
 block discarded – undo
665 665
         }
666 666
         //not a unix timestamp.  So we will use the set format on this object and set timezone to
667 667
         //create the DateTime object.
668
-        $format = $this->_date_format . ' ' . $this->_time_format;
668
+        $format = $this->_date_format.' '.$this->_time_format;
669 669
         try {
670 670
             $DateTime = DateTime::createFromFormat($format, $date_string, $this->_DateTimeZone);
671 671
             if ($DateTime instanceof DateTime) {
@@ -674,7 +674,7 @@  discard block
 block discarded – undo
674 674
                     $this->_DateTimeZone
675 675
                 );
676 676
             }
677
-            if (! $DateTime instanceof DbSafeDateTime) {
677
+            if ( ! $DateTime instanceof DbSafeDateTime) {
678 678
                 throw new EE_Error(
679 679
                     sprintf(
680 680
                         __('"%1$s" does not represent a valid Date Time in the format "%2$s".', 'event_espresso'),
Please login to merge, or discard this patch.
core/libraries/rest_api/controllers/model/Read.php 2 patches
Indentation   +1341 added lines, -1347 removed lines patch added patch discarded remove patch
@@ -23,7 +23,7 @@  discard block
 block discarded – undo
23 23
 use EEM_CPT_Base;
24 24
 
25 25
 if (! defined('EVENT_ESPRESSO_VERSION')) {
26
-    exit('No direct script access allowed');
26
+	exit('No direct script access allowed');
27 27
 }
28 28
 
29 29
 
@@ -41,1364 +41,1358 @@  discard block
 block discarded – undo
41 41
 
42 42
 
43 43
 
44
-    /**
45
-     * @var CalculatedModelFields
46
-     */
47
-    protected $fields_calculator;
44
+	/**
45
+	 * @var CalculatedModelFields
46
+	 */
47
+	protected $fields_calculator;
48 48
 
49 49
 
50 50
 
51
-    /**
52
-     * Read constructor.
53
-     */
54
-    public function __construct()
55
-    {
56
-        parent::__construct();
57
-        $this->fields_calculator = new CalculatedModelFields();
58
-    }
51
+	/**
52
+	 * Read constructor.
53
+	 */
54
+	public function __construct()
55
+	{
56
+		parent::__construct();
57
+		$this->fields_calculator = new CalculatedModelFields();
58
+	}
59 59
 
60 60
 
61 61
 
62
-    /**
63
-     * Handles requests to get all (or a filtered subset) of entities for a particular model
64
-
65
-     *
62
+	/**
63
+	 * Handles requests to get all (or a filtered subset) of entities for a particular model
64
+	 *
66 65
 *@param WP_REST_Request $request
67
-     * @param string           $version
68
-     * @param string           $model_name
69
-     * @return \WP_REST_Response|WP_Error
70
-     */
71
-    public static function handleRequestGetAll(WP_REST_Request $request, $version, $model_name)
72
-    {
73
-        $controller = new Read();
74
-        try {
75
-            $controller->setRequestedVersion($version);
76
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
77
-                return $controller->sendResponse(
78
-                    new WP_Error(
79
-                        'endpoint_parsing_error',
80
-                        sprintf(
81
-                            __(
82
-                                'There is no model for endpoint %s. Please contact event espresso support',
83
-                                'event_espresso'
84
-                            ),
85
-                            $model_name
86
-                        )
87
-                    )
88
-                );
89
-            }
90
-            return $controller->sendResponse(
91
-                $controller->getEntitiesFromModel(
92
-                    $controller->getModelVersionInfo()->loadModel($model_name),
93
-                    $request
94
-                )
95
-            );
96
-        } catch (Exception $e) {
97
-            return $controller->sendResponse($e);
98
-        }
99
-    }
100
-
101
-
102
-
103
-    /**
104
-     * Prepares and returns schema for any OPTIONS request.
105
-     *
106
-     * @param string $version    The API endpoint version being used.
107
-     * @param string $model_name Something like `Event` or `Registration`
108
-     * @return array
109
-     */
110
-    public static function handleSchemaRequest($version, $model_name)
111
-    {
112
-        $controller = new Read();
113
-        try {
114
-            $controller->setRequestedVersion($version);
115
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
116
-                return array();
117
-            }
118
-            //get the model for this version
119
-            $model = $controller->getModelVersionInfo()->loadModel($model_name);
120
-            $model_schema = new JsonModelSchema($model);
121
-            return $model_schema->getModelSchemaForRelations(
122
-                $controller->getModelVersionInfo()->relationSettings($model),
123
-                $controller->customizeSchemaForRestResponse(
124
-                    $model,
125
-                    $model_schema->getModelSchemaForFields(
126
-                        $controller->getModelVersionInfo()->fieldsOnModelInThisVersion($model),
127
-                        $model_schema->getInitialSchemaStructure()
128
-                    )
129
-                )
130
-            );
131
-        } catch (Exception $e) {
132
-            return array();
133
-        }
134
-    }
135
-
136
-
137
-
138
-    /**
139
-     * This loops through each field in the given schema for the model and does the following:
140
-     * - add any extra fields that are REST API specific and related to existing fields.
141
-     * - transform default values into the correct format for a REST API response.
142
-     *
143
-     * @param EEM_Base $model
144
-     * @param array     $schema
145
-     * @return array  The final schema.
146
-     */
147
-    protected function customizeSchemaForRestResponse(EEM_Base $model, array $schema)
148
-    {
149
-        foreach ($this->getModelVersionInfo()->fieldsOnModelInThisVersion($model) as $field_name => $field) {
150
-            $schema = $this->translateDefaultsForRestResponse(
151
-                $field_name,
152
-                $field,
153
-                $this->maybeAddExtraFieldsToSchema($field_name, $field, $schema)
154
-            );
155
-        }
156
-        return $schema;
157
-    }
158
-
159
-
160
-
161
-    /**
162
-     * This is used to ensure that the 'default' value set in the schema response is formatted correctly for the REST
163
-     * response.
164
-     *
165
-     * @param                      $field_name
166
-     * @param EE_Model_Field_Base $field
167
-     * @param array                $schema
168
-     * @return array
169
-     * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
170
-     * did, let's know about it ASAP, so let the exception bubble up)
171
-     */
172
-    protected function translateDefaultsForRestResponse($field_name, EE_Model_Field_Base $field, array $schema)
173
-    {
174
-        if (isset($schema['properties'][$field_name]['default'])) {
175
-            if (is_array($schema['properties'][$field_name]['default'])) {
176
-                foreach ($schema['properties'][$field_name]['default'] as $default_key => $default_value) {
177
-                    if ($default_key === 'raw') {
178
-                        $schema['properties'][$field_name]['default'][$default_key] =
179
-                            ModelDataTranslator::prepareFieldValueForJson(
180
-                                $field,
181
-                                $default_value,
182
-                                $this->getModelVersionInfo()->requestedVersion()
183
-                            );
184
-                    }
185
-                }
186
-            } else {
187
-                $schema['properties'][$field_name]['default'] = ModelDataTranslator::prepareFieldValueForJson(
188
-                    $field,
189
-                    $schema['properties'][$field_name]['default'],
190
-                    $this->getModelVersionInfo()->requestedVersion()
191
-                );
192
-            }
193
-        }
194
-        return $schema;
195
-    }
196
-
197
-
198
-
199
-    /**
200
-     * Adds additional fields to the schema
201
-     * The REST API returns a GMT value field for each datetime field in the resource.  Thus the description about this
202
-     * needs to be added to the schema.
203
-     *
204
-     * @param                      $field_name
205
-     * @param EE_Model_Field_Base $field
206
-     * @param array                $schema
207
-     * @return array
208
-     */
209
-    protected function maybeAddExtraFieldsToSchema($field_name, EE_Model_Field_Base $field, array $schema)
210
-    {
211
-        if ($field instanceof EE_Datetime_Field) {
212
-            $schema['properties'][$field_name . '_gmt'] = $field->getSchema();
213
-            //modify the description
214
-            $schema['properties'][$field_name . '_gmt']['description'] = sprintf(
215
-                esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
216
-                wp_specialchars_decode($field->get_nicename(), ENT_QUOTES)
217
-            );
218
-        }
219
-        return $schema;
220
-    }
221
-
222
-
223
-
224
-    /**
225
-     * Used to figure out the route from the request when a `WP_REST_Request` object is not available
226
-     *
227
-     * @return string
228
-     */
229
-    protected function getRouteFromRequest()
230
-    {
231
-        if (isset($GLOBALS['wp'])
232
-            && $GLOBALS['wp'] instanceof \WP
233
-            && isset($GLOBALS['wp']->query_vars['rest_route'])
234
-        ) {
235
-            return $GLOBALS['wp']->query_vars['rest_route'];
236
-        } else {
237
-            return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
238
-        }
239
-    }
240
-
241
-
242
-
243
-    /**
244
-     * Gets a single entity related to the model indicated in the path and its id
245
-
246
-     *
66
+	 * @param string           $version
67
+	 * @param string           $model_name
68
+	 * @return \WP_REST_Response|WP_Error
69
+	 */
70
+	public static function handleRequestGetAll(WP_REST_Request $request, $version, $model_name)
71
+	{
72
+		$controller = new Read();
73
+		try {
74
+			$controller->setRequestedVersion($version);
75
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
76
+				return $controller->sendResponse(
77
+					new WP_Error(
78
+						'endpoint_parsing_error',
79
+						sprintf(
80
+							__(
81
+								'There is no model for endpoint %s. Please contact event espresso support',
82
+								'event_espresso'
83
+							),
84
+							$model_name
85
+						)
86
+					)
87
+				);
88
+			}
89
+			return $controller->sendResponse(
90
+				$controller->getEntitiesFromModel(
91
+					$controller->getModelVersionInfo()->loadModel($model_name),
92
+					$request
93
+				)
94
+			);
95
+		} catch (Exception $e) {
96
+			return $controller->sendResponse($e);
97
+		}
98
+	}
99
+
100
+
101
+
102
+	/**
103
+	 * Prepares and returns schema for any OPTIONS request.
104
+	 *
105
+	 * @param string $version    The API endpoint version being used.
106
+	 * @param string $model_name Something like `Event` or `Registration`
107
+	 * @return array
108
+	 */
109
+	public static function handleSchemaRequest($version, $model_name)
110
+	{
111
+		$controller = new Read();
112
+		try {
113
+			$controller->setRequestedVersion($version);
114
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
115
+				return array();
116
+			}
117
+			//get the model for this version
118
+			$model = $controller->getModelVersionInfo()->loadModel($model_name);
119
+			$model_schema = new JsonModelSchema($model);
120
+			return $model_schema->getModelSchemaForRelations(
121
+				$controller->getModelVersionInfo()->relationSettings($model),
122
+				$controller->customizeSchemaForRestResponse(
123
+					$model,
124
+					$model_schema->getModelSchemaForFields(
125
+						$controller->getModelVersionInfo()->fieldsOnModelInThisVersion($model),
126
+						$model_schema->getInitialSchemaStructure()
127
+					)
128
+				)
129
+			);
130
+		} catch (Exception $e) {
131
+			return array();
132
+		}
133
+	}
134
+
135
+
136
+
137
+	/**
138
+	 * This loops through each field in the given schema for the model and does the following:
139
+	 * - add any extra fields that are REST API specific and related to existing fields.
140
+	 * - transform default values into the correct format for a REST API response.
141
+	 *
142
+	 * @param EEM_Base $model
143
+	 * @param array     $schema
144
+	 * @return array  The final schema.
145
+	 */
146
+	protected function customizeSchemaForRestResponse(EEM_Base $model, array $schema)
147
+	{
148
+		foreach ($this->getModelVersionInfo()->fieldsOnModelInThisVersion($model) as $field_name => $field) {
149
+			$schema = $this->translateDefaultsForRestResponse(
150
+				$field_name,
151
+				$field,
152
+				$this->maybeAddExtraFieldsToSchema($field_name, $field, $schema)
153
+			);
154
+		}
155
+		return $schema;
156
+	}
157
+
158
+
159
+
160
+	/**
161
+	 * This is used to ensure that the 'default' value set in the schema response is formatted correctly for the REST
162
+	 * response.
163
+	 *
164
+	 * @param                      $field_name
165
+	 * @param EE_Model_Field_Base $field
166
+	 * @param array                $schema
167
+	 * @return array
168
+	 * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
169
+	 * did, let's know about it ASAP, so let the exception bubble up)
170
+	 */
171
+	protected function translateDefaultsForRestResponse($field_name, EE_Model_Field_Base $field, array $schema)
172
+	{
173
+		if (isset($schema['properties'][$field_name]['default'])) {
174
+			if (is_array($schema['properties'][$field_name]['default'])) {
175
+				foreach ($schema['properties'][$field_name]['default'] as $default_key => $default_value) {
176
+					if ($default_key === 'raw') {
177
+						$schema['properties'][$field_name]['default'][$default_key] =
178
+							ModelDataTranslator::prepareFieldValueForJson(
179
+								$field,
180
+								$default_value,
181
+								$this->getModelVersionInfo()->requestedVersion()
182
+							);
183
+					}
184
+				}
185
+			} else {
186
+				$schema['properties'][$field_name]['default'] = ModelDataTranslator::prepareFieldValueForJson(
187
+					$field,
188
+					$schema['properties'][$field_name]['default'],
189
+					$this->getModelVersionInfo()->requestedVersion()
190
+				);
191
+			}
192
+		}
193
+		return $schema;
194
+	}
195
+
196
+
197
+
198
+	/**
199
+	 * Adds additional fields to the schema
200
+	 * The REST API returns a GMT value field for each datetime field in the resource.  Thus the description about this
201
+	 * needs to be added to the schema.
202
+	 *
203
+	 * @param                      $field_name
204
+	 * @param EE_Model_Field_Base $field
205
+	 * @param array                $schema
206
+	 * @return array
207
+	 */
208
+	protected function maybeAddExtraFieldsToSchema($field_name, EE_Model_Field_Base $field, array $schema)
209
+	{
210
+		if ($field instanceof EE_Datetime_Field) {
211
+			$schema['properties'][$field_name . '_gmt'] = $field->getSchema();
212
+			//modify the description
213
+			$schema['properties'][$field_name . '_gmt']['description'] = sprintf(
214
+				esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
215
+				wp_specialchars_decode($field->get_nicename(), ENT_QUOTES)
216
+			);
217
+		}
218
+		return $schema;
219
+	}
220
+
221
+
222
+
223
+	/**
224
+	 * Used to figure out the route from the request when a `WP_REST_Request` object is not available
225
+	 *
226
+	 * @return string
227
+	 */
228
+	protected function getRouteFromRequest()
229
+	{
230
+		if (isset($GLOBALS['wp'])
231
+			&& $GLOBALS['wp'] instanceof \WP
232
+			&& isset($GLOBALS['wp']->query_vars['rest_route'])
233
+		) {
234
+			return $GLOBALS['wp']->query_vars['rest_route'];
235
+		} else {
236
+			return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
237
+		}
238
+	}
239
+
240
+
241
+
242
+	/**
243
+	 * Gets a single entity related to the model indicated in the path and its id
244
+	 *
247 245
 *@param WP_REST_Request $request
248
-     * @param string           $version
249
-     * @param string           $model_name
250
-     * @return \WP_REST_Response|WP_Error
251
-     */
252
-    public static function handleRequestGetOne(WP_REST_Request $request, $version, $model_name)
253
-    {
254
-        $controller = new Read();
255
-        try {
256
-            $controller->setRequestedVersion($version);
257
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
258
-                return $controller->sendResponse(
259
-                    new WP_Error(
260
-                        'endpoint_parsing_error',
261
-                        sprintf(
262
-                            __(
263
-                                'There is no model for endpoint %s. Please contact event espresso support',
264
-                                'event_espresso'
265
-                            ),
266
-                            $model_name
267
-                        )
268
-                    )
269
-                );
270
-            }
271
-            return $controller->sendResponse(
272
-                $controller->getEntityFromModel(
273
-                    $controller->getModelVersionInfo()->loadModel($model_name),
274
-                    $request
275
-                )
276
-            );
277
-        } catch (Exception $e) {
278
-            return $controller->sendResponse($e);
279
-        }
280
-    }
281
-
282
-
283
-
284
-    /**
285
-     * Gets all the related entities (or if its a belongs-to relation just the one)
286
-     * to the item with the given id
287
-
288
-     *
246
+	 * @param string           $version
247
+	 * @param string           $model_name
248
+	 * @return \WP_REST_Response|WP_Error
249
+	 */
250
+	public static function handleRequestGetOne(WP_REST_Request $request, $version, $model_name)
251
+	{
252
+		$controller = new Read();
253
+		try {
254
+			$controller->setRequestedVersion($version);
255
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
256
+				return $controller->sendResponse(
257
+					new WP_Error(
258
+						'endpoint_parsing_error',
259
+						sprintf(
260
+							__(
261
+								'There is no model for endpoint %s. Please contact event espresso support',
262
+								'event_espresso'
263
+							),
264
+							$model_name
265
+						)
266
+					)
267
+				);
268
+			}
269
+			return $controller->sendResponse(
270
+				$controller->getEntityFromModel(
271
+					$controller->getModelVersionInfo()->loadModel($model_name),
272
+					$request
273
+				)
274
+			);
275
+		} catch (Exception $e) {
276
+			return $controller->sendResponse($e);
277
+		}
278
+	}
279
+
280
+
281
+
282
+	/**
283
+	 * Gets all the related entities (or if its a belongs-to relation just the one)
284
+	 * to the item with the given id
285
+	 *
289 286
 *@param WP_REST_Request $request
290
-     * @param string           $version
291
-     * @param string           $model_name
292
-     * @param string           $related_model_name
293
-     * @return \WP_REST_Response|WP_Error
294
-     */
295
-    public static function handleRequestGetRelated(
296
-        WP_REST_Request $request,
297
-        $version,
298
-        $model_name,
299
-        $related_model_name
300
-    ) {
301
-        $controller = new Read();
302
-        try {
303
-            $controller->setRequestedVersion($version);
304
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
305
-                return $controller->sendResponse(
306
-                    new WP_Error(
307
-                        'endpoint_parsing_error',
308
-                        sprintf(
309
-                            __(
310
-                                'There is no model for endpoint %s. Please contact event espresso support',
311
-                                'event_espresso'
312
-                            ),
313
-                            $model_name
314
-                        )
315
-                    )
316
-                );
317
-            }
318
-            $main_model = $controller->getModelVersionInfo()->loadModel($model_name);
319
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($related_model_name)) {
320
-                return $controller->sendResponse(
321
-                    new WP_Error(
322
-                        'endpoint_parsing_error',
323
-                        sprintf(
324
-                            __(
325
-                                'There is no model for endpoint %s. Please contact event espresso support',
326
-                                'event_espresso'
327
-                            ),
328
-                            $related_model_name
329
-                        )
330
-                    )
331
-                );
332
-            }
333
-            return $controller->sendResponse(
334
-                $controller->getEntitiesFromRelation(
335
-                    $request->get_param('id'),
336
-                    $main_model->related_settings_for($related_model_name),
337
-                    $request
338
-                )
339
-            );
340
-        } catch (Exception $e) {
341
-            return $controller->sendResponse($e);
342
-        }
343
-    }
344
-
345
-
346
-
347
-    /**
348
-     * Gets a collection for the given model and filters
349
-
350
-     *
287
+	 * @param string           $version
288
+	 * @param string           $model_name
289
+	 * @param string           $related_model_name
290
+	 * @return \WP_REST_Response|WP_Error
291
+	 */
292
+	public static function handleRequestGetRelated(
293
+		WP_REST_Request $request,
294
+		$version,
295
+		$model_name,
296
+		$related_model_name
297
+	) {
298
+		$controller = new Read();
299
+		try {
300
+			$controller->setRequestedVersion($version);
301
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
302
+				return $controller->sendResponse(
303
+					new WP_Error(
304
+						'endpoint_parsing_error',
305
+						sprintf(
306
+							__(
307
+								'There is no model for endpoint %s. Please contact event espresso support',
308
+								'event_espresso'
309
+							),
310
+							$model_name
311
+						)
312
+					)
313
+				);
314
+			}
315
+			$main_model = $controller->getModelVersionInfo()->loadModel($model_name);
316
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($related_model_name)) {
317
+				return $controller->sendResponse(
318
+					new WP_Error(
319
+						'endpoint_parsing_error',
320
+						sprintf(
321
+							__(
322
+								'There is no model for endpoint %s. Please contact event espresso support',
323
+								'event_espresso'
324
+							),
325
+							$related_model_name
326
+						)
327
+					)
328
+				);
329
+			}
330
+			return $controller->sendResponse(
331
+				$controller->getEntitiesFromRelation(
332
+					$request->get_param('id'),
333
+					$main_model->related_settings_for($related_model_name),
334
+					$request
335
+				)
336
+			);
337
+		} catch (Exception $e) {
338
+			return $controller->sendResponse($e);
339
+		}
340
+	}
341
+
342
+
343
+
344
+	/**
345
+	 * Gets a collection for the given model and filters
346
+	 *
351 347
 *@param EEM_Base        $model
352
-     * @param WP_REST_Request $request
353
-     * @return array|WP_Error
354
-     */
355
-    public function getEntitiesFromModel($model, $request)
356
-    {
357
-        $query_params = $this->createModelQueryParams($model, $request->get_params());
358
-        if (! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
359
-            $model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
360
-            return new WP_Error(
361
-                sprintf('rest_%s_cannot_list', $model_name_plural),
362
-                sprintf(
363
-                    __('Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso'),
364
-                    $model_name_plural,
365
-                    Capabilities::getMissingPermissionsString($model, $query_params['caps'])
366
-                ),
367
-                array('status' => 403)
368
-            );
369
-        }
370
-        if (! $request->get_header('no_rest_headers')) {
371
-            $this->setHeadersFromQueryParams($model, $query_params);
372
-        }
373
-        /** @type array $results */
374
-        $results = $model->get_all_wpdb_results($query_params);
375
-        $nice_results = array();
376
-        foreach ($results as $result) {
377
-            $nice_results[] = $this->createEntityFromWpdbResult(
378
-                $model,
379
-                $result,
380
-                $request
381
-            );
382
-        }
383
-        return $nice_results;
384
-    }
385
-
386
-
387
-
388
-    /**
389
-     * Gets the collection for given relation object
390
-     * The same as Read::get_entities_from_model(), except if the relation
391
-     * is a HABTM relation, in which case it merges any non-foreign-key fields from
392
-     * the join-model-object into the results
393
-     *
394
-     * @param array                   $primary_model_query_params query params for finding the item from which
395
-     *                                                            relations will be based
396
-     * @param \EE_Model_Relation_Base $relation
397
-     * @param WP_REST_Request        $request
398
-     * @return WP_Error|array
399
-     * @throws RestException
400
-     */
401
-    protected function getEntitiesFromRelationUsingModelQueryParams($primary_model_query_params, $relation, $request)
402
-    {
403
-        $context = $this->validateContext($request->get_param('caps'));
404
-        $model = $relation->get_this_model();
405
-        $related_model = $relation->get_other_model();
406
-        if (! isset($primary_model_query_params[0])) {
407
-            $primary_model_query_params[0] = array();
408
-        }
409
-        //check if they can access the 1st model object
410
-        $primary_model_query_params = array(
411
-            0       => $primary_model_query_params[0],
412
-            'limit' => 1,
413
-        );
414
-        if ($model instanceof \EEM_Soft_Delete_Base) {
415
-            $primary_model_query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included(
416
-                $primary_model_query_params
417
-            );
418
-        }
419
-        $restricted_query_params = $primary_model_query_params;
420
-        $restricted_query_params['caps'] = $context;
421
-        $this->setDebugInfo('main model query params', $restricted_query_params);
422
-        $this->setDebugInfo('missing caps', Capabilities::getMissingPermissionsString($related_model, $context));
423
-        if (! (
424
-            Capabilities::currentUserHasPartialAccessTo($related_model, $context)
425
-            && $model->exists($restricted_query_params)
426
-        )
427
-        ) {
428
-            if ($relation instanceof EE_Belongs_To_Relation) {
429
-                $related_model_name_maybe_plural = strtolower($related_model->get_this_model_name());
430
-            } else {
431
-                $related_model_name_maybe_plural = EEH_Inflector::pluralize_and_lower(
432
-                    $related_model->get_this_model_name()
433
-                );
434
-            }
435
-            return new WP_Error(
436
-                sprintf('rest_%s_cannot_list', $related_model_name_maybe_plural),
437
-                sprintf(
438
-                    __(
439
-                        'Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s',
440
-                        'event_espresso'
441
-                    ),
442
-                    $related_model_name_maybe_plural,
443
-                    $relation->get_this_model()->get_this_model_name(),
444
-                    implode(
445
-                        ',',
446
-                        array_keys(
447
-                            Capabilities::getMissingPermissions($related_model, $context)
448
-                        )
449
-                    )
450
-                ),
451
-                array('status' => 403)
452
-            );
453
-        }
454
-        $query_params = $this->createModelQueryParams($relation->get_other_model(), $request->get_params());
455
-        foreach ($primary_model_query_params[0] as $where_condition_key => $where_condition_value) {
456
-            $query_params[0][$relation->get_this_model()->get_this_model_name()
457
-                             . '.'
458
-                             . $where_condition_key] = $where_condition_value;
459
-        }
460
-        $query_params['default_where_conditions'] = 'none';
461
-        $query_params['caps'] = $context;
462
-        if (! $request->get_header('no_rest_headers')) {
463
-            $this->setHeadersFromQueryParams($relation->get_other_model(), $query_params);
464
-        }
465
-        /** @type array $results */
466
-        $results = $relation->get_other_model()->get_all_wpdb_results($query_params);
467
-        $nice_results = array();
468
-        foreach ($results as $result) {
469
-            $nice_result = $this->createEntityFromWpdbResult(
470
-                $relation->get_other_model(),
471
-                $result,
472
-                $request
473
-            );
474
-            if ($relation instanceof \EE_HABTM_Relation) {
475
-                //put the unusual stuff (properties from the HABTM relation) first, and make sure
476
-                //if there are conflicts we prefer the properties from the main model
477
-                $join_model_result = $this->createEntityFromWpdbResult(
478
-                    $relation->get_join_model(),
479
-                    $result,
480
-                    $request
481
-                );
482
-                $joined_result = array_merge($nice_result, $join_model_result);
483
-                //but keep the meta stuff from the main model
484
-                if (isset($nice_result['meta'])) {
485
-                    $joined_result['meta'] = $nice_result['meta'];
486
-                }
487
-                $nice_result = $joined_result;
488
-            }
489
-            $nice_results[] = $nice_result;
490
-        }
491
-        if ($relation instanceof EE_Belongs_To_Relation) {
492
-            return array_shift($nice_results);
493
-        } else {
494
-            return $nice_results;
495
-        }
496
-    }
497
-
498
-
499
-
500
-    /**
501
-     * Gets the collection for given relation object
502
-     * The same as Read::get_entities_from_model(), except if the relation
503
-     * is a HABTM relation, in which case it merges any non-foreign-key fields from
504
-     * the join-model-object into the results
505
-
506
-     *
348
+	 * @param WP_REST_Request $request
349
+	 * @return array|WP_Error
350
+	 */
351
+	public function getEntitiesFromModel($model, $request)
352
+	{
353
+		$query_params = $this->createModelQueryParams($model, $request->get_params());
354
+		if (! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
355
+			$model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
356
+			return new WP_Error(
357
+				sprintf('rest_%s_cannot_list', $model_name_plural),
358
+				sprintf(
359
+					__('Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso'),
360
+					$model_name_plural,
361
+					Capabilities::getMissingPermissionsString($model, $query_params['caps'])
362
+				),
363
+				array('status' => 403)
364
+			);
365
+		}
366
+		if (! $request->get_header('no_rest_headers')) {
367
+			$this->setHeadersFromQueryParams($model, $query_params);
368
+		}
369
+		/** @type array $results */
370
+		$results = $model->get_all_wpdb_results($query_params);
371
+		$nice_results = array();
372
+		foreach ($results as $result) {
373
+			$nice_results[] = $this->createEntityFromWpdbResult(
374
+				$model,
375
+				$result,
376
+				$request
377
+			);
378
+		}
379
+		return $nice_results;
380
+	}
381
+
382
+
383
+
384
+	/**
385
+	 * Gets the collection for given relation object
386
+	 * The same as Read::get_entities_from_model(), except if the relation
387
+	 * is a HABTM relation, in which case it merges any non-foreign-key fields from
388
+	 * the join-model-object into the results
389
+	 *
390
+	 * @param array                   $primary_model_query_params query params for finding the item from which
391
+	 *                                                            relations will be based
392
+	 * @param \EE_Model_Relation_Base $relation
393
+	 * @param WP_REST_Request        $request
394
+	 * @return WP_Error|array
395
+	 * @throws RestException
396
+	 */
397
+	protected function getEntitiesFromRelationUsingModelQueryParams($primary_model_query_params, $relation, $request)
398
+	{
399
+		$context = $this->validateContext($request->get_param('caps'));
400
+		$model = $relation->get_this_model();
401
+		$related_model = $relation->get_other_model();
402
+		if (! isset($primary_model_query_params[0])) {
403
+			$primary_model_query_params[0] = array();
404
+		}
405
+		//check if they can access the 1st model object
406
+		$primary_model_query_params = array(
407
+			0       => $primary_model_query_params[0],
408
+			'limit' => 1,
409
+		);
410
+		if ($model instanceof \EEM_Soft_Delete_Base) {
411
+			$primary_model_query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included(
412
+				$primary_model_query_params
413
+			);
414
+		}
415
+		$restricted_query_params = $primary_model_query_params;
416
+		$restricted_query_params['caps'] = $context;
417
+		$this->setDebugInfo('main model query params', $restricted_query_params);
418
+		$this->setDebugInfo('missing caps', Capabilities::getMissingPermissionsString($related_model, $context));
419
+		if (! (
420
+			Capabilities::currentUserHasPartialAccessTo($related_model, $context)
421
+			&& $model->exists($restricted_query_params)
422
+		)
423
+		) {
424
+			if ($relation instanceof EE_Belongs_To_Relation) {
425
+				$related_model_name_maybe_plural = strtolower($related_model->get_this_model_name());
426
+			} else {
427
+				$related_model_name_maybe_plural = EEH_Inflector::pluralize_and_lower(
428
+					$related_model->get_this_model_name()
429
+				);
430
+			}
431
+			return new WP_Error(
432
+				sprintf('rest_%s_cannot_list', $related_model_name_maybe_plural),
433
+				sprintf(
434
+					__(
435
+						'Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s',
436
+						'event_espresso'
437
+					),
438
+					$related_model_name_maybe_plural,
439
+					$relation->get_this_model()->get_this_model_name(),
440
+					implode(
441
+						',',
442
+						array_keys(
443
+							Capabilities::getMissingPermissions($related_model, $context)
444
+						)
445
+					)
446
+				),
447
+				array('status' => 403)
448
+			);
449
+		}
450
+		$query_params = $this->createModelQueryParams($relation->get_other_model(), $request->get_params());
451
+		foreach ($primary_model_query_params[0] as $where_condition_key => $where_condition_value) {
452
+			$query_params[0][$relation->get_this_model()->get_this_model_name()
453
+							 . '.'
454
+							 . $where_condition_key] = $where_condition_value;
455
+		}
456
+		$query_params['default_where_conditions'] = 'none';
457
+		$query_params['caps'] = $context;
458
+		if (! $request->get_header('no_rest_headers')) {
459
+			$this->setHeadersFromQueryParams($relation->get_other_model(), $query_params);
460
+		}
461
+		/** @type array $results */
462
+		$results = $relation->get_other_model()->get_all_wpdb_results($query_params);
463
+		$nice_results = array();
464
+		foreach ($results as $result) {
465
+			$nice_result = $this->createEntityFromWpdbResult(
466
+				$relation->get_other_model(),
467
+				$result,
468
+				$request
469
+			);
470
+			if ($relation instanceof \EE_HABTM_Relation) {
471
+				//put the unusual stuff (properties from the HABTM relation) first, and make sure
472
+				//if there are conflicts we prefer the properties from the main model
473
+				$join_model_result = $this->createEntityFromWpdbResult(
474
+					$relation->get_join_model(),
475
+					$result,
476
+					$request
477
+				);
478
+				$joined_result = array_merge($nice_result, $join_model_result);
479
+				//but keep the meta stuff from the main model
480
+				if (isset($nice_result['meta'])) {
481
+					$joined_result['meta'] = $nice_result['meta'];
482
+				}
483
+				$nice_result = $joined_result;
484
+			}
485
+			$nice_results[] = $nice_result;
486
+		}
487
+		if ($relation instanceof EE_Belongs_To_Relation) {
488
+			return array_shift($nice_results);
489
+		} else {
490
+			return $nice_results;
491
+		}
492
+	}
493
+
494
+
495
+
496
+	/**
497
+	 * Gets the collection for given relation object
498
+	 * The same as Read::get_entities_from_model(), except if the relation
499
+	 * is a HABTM relation, in which case it merges any non-foreign-key fields from
500
+	 * the join-model-object into the results
501
+	 *
507 502
 *@param string                  $id the ID of the thing we are fetching related stuff from
508
-     * @param \EE_Model_Relation_Base $relation
509
-     * @param WP_REST_Request        $request
510
-     * @return array|WP_Error
511
-     * @throws EE_Error
512
-     */
513
-    public function getEntitiesFromRelation($id, $relation, $request)
514
-    {
515
-        if (! $relation->get_this_model()->has_primary_key_field()) {
516
-            throw new EE_Error(
517
-                sprintf(
518
-                    __(
519
-                        // @codingStandardsIgnoreStart
520
-                        'Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
521
-                        // @codingStandardsIgnoreEnd
522
-                        'event_espresso'
523
-                    ),
524
-                    $relation->get_this_model()->get_this_model_name()
525
-                )
526
-            );
527
-        }
528
-        return $this->getEntitiesFromRelationUsingModelQueryParams(
529
-            array(
530
-                array(
531
-                    $relation->get_this_model()->primary_key_name() => $id,
532
-                ),
533
-            ),
534
-            $relation,
535
-            $request
536
-        );
537
-    }
538
-
539
-
540
-
541
-    /**
542
-     * Sets the headers that are based on the model and query params,
543
-     * like the total records. This should only be called on the original request
544
-     * from the client, not on subsequent internal
545
-     *
546
-     * @param EEM_Base $model
547
-     * @param array     $query_params
548
-     * @return void
549
-     */
550
-    protected function setHeadersFromQueryParams($model, $query_params)
551
-    {
552
-        $this->setDebugInfo('model query params', $query_params);
553
-        $this->setDebugInfo(
554
-            'missing caps',
555
-            Capabilities::getMissingPermissionsString($model, $query_params['caps'])
556
-        );
557
-        //normally the limit to a 2-part array, where the 2nd item is the limit
558
-        if (! isset($query_params['limit'])) {
559
-            $query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
560
-        }
561
-        if (is_array($query_params['limit'])) {
562
-            $limit_parts = $query_params['limit'];
563
-        } else {
564
-            $limit_parts = explode(',', $query_params['limit']);
565
-            if (count($limit_parts) == 1) {
566
-                $limit_parts = array(0, $limit_parts[0]);
567
-            }
568
-        }
569
-        //remove the group by and having parts of the query, as those will
570
-        //make the sql query return an array of values, instead of just a single value
571
-        unset($query_params['group_by'], $query_params['having'], $query_params['limit']);
572
-        $count = $model->count($query_params, null, true);
573
-        $pages = $count / $limit_parts[1];
574
-        $this->setResponseHeader('Total', $count, false);
575
-        $this->setResponseHeader('PageSize', $limit_parts[1], false);
576
-        $this->setResponseHeader('TotalPages', ceil($pages), false);
577
-    }
578
-
579
-
580
-
581
-    /**
582
-     * Changes database results into REST API entities
583
-     *
584
-     * @param EEM_Base        $model
585
-     * @param array            $db_row     like results from $wpdb->get_results()
586
-     * @param WP_REST_Request $rest_request
587
-     * @param string           $deprecated no longer used
588
-     * @return array ready for being converted into json for sending to client
589
-     */
590
-    public function createEntityFromWpdbResult($model, $db_row, $rest_request, $deprecated = null)
591
-    {
592
-        if (! $rest_request instanceof WP_REST_Request) {
593
-            //ok so this was called in the old style, where the 3rd arg was
594
-            //$include, and the 4th arg was $context
595
-            //now setup the request just to avoid fatal errors, although we won't be able
596
-            //to truly make use of it because it's kinda devoid of info
597
-            $rest_request = new WP_REST_Request();
598
-            $rest_request->set_param('include', $rest_request);
599
-            $rest_request->set_param('caps', $deprecated);
600
-        }
601
-        if ($rest_request->get_param('caps') == null) {
602
-            $rest_request->set_param('caps', EEM_Base::caps_read);
603
-        }
604
-        $entity_array = $this->createBareEntityFromWpdbResults($model, $db_row);
605
-        $entity_array = $this->addExtraFields($model, $db_row, $entity_array);
606
-        $entity_array['_links'] = $this->getEntityLinks($model, $db_row, $entity_array);
607
-        $entity_array['_calculated_fields'] = $this->getEntityCalculations($model, $db_row, $rest_request);
608
-        $entity_array = apply_filters(
609
-            'FHEE__Read__create_entity_from_wpdb_results__entity_before_including_requested_models',
610
-            $entity_array,
611
-            $model,
612
-            $rest_request->get_param('caps'),
613
-            $rest_request,
614
-            $this
615
-        );
616
-        $entity_array = $this->includeRequestedModels($model, $rest_request, $entity_array, $db_row);
617
-        $entity_array = apply_filters(
618
-            'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
619
-            $entity_array,
620
-            $model,
621
-            $rest_request->get_param('caps'),
622
-            $rest_request,
623
-            $this
624
-        );
625
-        $result_without_inaccessible_fields = Capabilities::filterOutInaccessibleEntityFields(
626
-            $entity_array,
627
-            $model,
628
-            $rest_request->get_param('caps'),
629
-            $this->getModelVersionInfo(),
630
-            $model->get_index_primary_key_string(
631
-                $model->deduce_fields_n_values_from_cols_n_values($db_row)
632
-            )
633
-        );
634
-        $this->setDebugInfo(
635
-            'inaccessible fields',
636
-            array_keys(array_diff_key($entity_array, $result_without_inaccessible_fields))
637
-        );
638
-        return apply_filters(
639
-            'FHEE__Read__create_entity_from_wpdb_results__entity_return',
640
-            $result_without_inaccessible_fields,
641
-            $model,
642
-            $rest_request->get_param('caps')
643
-        );
644
-    }
645
-
646
-
647
-
648
-    /**
649
-     * Creates a REST entity array (JSON object we're going to return in the response, but
650
-     * for now still a PHP array, but soon enough we'll call json_encode on it, don't worry),
651
-     * from $wpdb->get_row( $sql, ARRAY_A)
652
-     *
653
-     * @param EEM_Base $model
654
-     * @param array     $db_row
655
-     * @return array entity mostly ready for converting to JSON and sending in the response
656
-     *
657
-     */
658
-    protected function createBareEntityFromWpdbResults(EEM_Base $model, $db_row)
659
-    {
660
-        $result = $model->deduce_fields_n_values_from_cols_n_values($db_row);
661
-        $result = array_intersect_key(
662
-            $result,
663
-            $this->getModelVersionInfo()->fieldsOnModelInThisVersion($model)
664
-        );
665
-        //if this is a CPT, we need to set the global $post to it,
666
-        //otherwise shortcodes etc won't work properly while rendering it
667
-        if ($model instanceof \EEM_CPT_Base) {
668
-            $do_chevy_shuffle = true;
669
-        } else {
670
-            $do_chevy_shuffle = false;
671
-        }
672
-        if ($do_chevy_shuffle) {
673
-            global $post;
674
-            $old_post = $post;
675
-            $post = get_post($result[$model->primary_key_name()]);
676
-            if (! $post instanceof \WP_Post) {
677
-                //well that's weird, because $result is what we JUST fetched from the database
678
-                throw new RestException(
679
-                    'error_fetching_post_from_database_results',
680
-                    esc_html__(
681
-                        'An item was retrieved from the database but it\'s not a WP_Post like it should be.',
682
-                        'event_espresso'
683
-                    )
684
-                );
685
-            }
686
-            $model_object_classname = 'EE_' . $model->get_this_model_name();
687
-            $post->{$model_object_classname} = \EE_Registry::instance()->load_class(
688
-                $model_object_classname,
689
-                $result,
690
-                false,
691
-                false
692
-            );
693
-        }
694
-        foreach ($result as $field_name => $field_value) {
695
-            $field_obj = $model->field_settings_for($field_name);
696
-            if ($this->isSubclassOfOne($field_obj, $this->getModelVersionInfo()->fieldsIgnored())) {
697
-                unset($result[$field_name]);
698
-            } elseif ($this->isSubclassOfOne(
699
-                $field_obj,
700
-                $this->getModelVersionInfo()->fieldsThatHaveRenderedFormat()
701
-            )
702
-            ) {
703
-                $result[$field_name] = array(
704
-                    'raw'      => $this->prepareFieldObjValueForJson($field_obj, $field_value),
705
-                    'rendered' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
706
-                );
707
-            } elseif ($this->isSubclassOfOne(
708
-                $field_obj,
709
-                $this->getModelVersionInfo()->fieldsThatHavePrettyFormat()
710
-            )
711
-            ) {
712
-                $result[$field_name] = array(
713
-                    'raw'    => $this->prepareFieldObjValueForJson($field_obj, $field_value),
714
-                    'pretty' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
715
-                );
716
-            } elseif ($field_obj instanceof \EE_Datetime_Field) {
717
-                $field_value = $field_obj->prepare_for_set_from_db($field_value);
718
-                $timezone = $field_value->getTimezone();
719
-                EEH_DTT_Helper::setTimezone($field_value, new DateTimeZone('UTC'));
720
-                $result[$field_name . '_gmt'] = ModelDataTranslator::prepareFieldValuesForJson(
721
-                    $field_obj,
722
-                    $field_value,
723
-                    $this->getModelVersionInfo()->requestedVersion()
724
-                );
725
-                EEH_DTT_Helper::setTimezone($field_value, $timezone);
726
-                $result[$field_name] = ModelDataTranslator::prepareFieldValuesForJson(
727
-                    $field_obj,
728
-                    $field_value,
729
-                    $this->getModelVersionInfo()->requestedVersion()
730
-                );
731
-            } else {
732
-                $result[$field_name] = $this->prepareFieldObjValueForJson($field_obj, $field_value);
733
-            }
734
-        }
735
-        if ($do_chevy_shuffle) {
736
-            $post = $old_post;
737
-        }
738
-        return $result;
739
-    }
740
-
741
-
742
-
743
-    /**
744
-     * Takes a value all the way from the DB representation, to the model object's representation, to the
745
-     * user-facing PHP representation, to the REST API representation. (Assumes you've already taken from the DB
746
-     * representation using $field_obj->prepare_for_set_from_db())
747
-     *
748
-     * @param EE_Model_Field_Base $field_obj
749
-     * @param mixed $value as it's stored on a model object
750
-     * @param string $format valid values are 'normal' (default), 'pretty', 'datetime_obj'
751
-     * @return mixed
752
-     * @throws ObjectDetectedException if $value contains a PHP object
753
-     */
754
-    protected function prepareFieldObjValueForJson(EE_Model_Field_Base $field_obj, $value, $format = 'normal')
755
-    {
756
-        $value = $field_obj->prepare_for_set_from_db($value);
757
-        switch ($format) {
758
-            case 'pretty':
759
-                $value = $field_obj->prepare_for_pretty_echoing($value);
760
-                break;
761
-            case 'normal':
762
-            default:
763
-                $value = $field_obj->prepare_for_get($value);
764
-                break;
765
-        }
766
-        return ModelDataTranslator::prepareFieldValuesForJson(
767
-            $field_obj,
768
-            $value,
769
-            $this->getModelVersionInfo()->requestedVersion()
770
-        );
771
-    }
772
-
773
-
774
-
775
-    /**
776
-     * Adds a few extra fields to the entity response
777
-     *
778
-     * @param EEM_Base $model
779
-     * @param array     $db_row
780
-     * @param array     $entity_array
781
-     * @return array modified entity
782
-     */
783
-    protected function addExtraFields(EEM_Base $model, $db_row, $entity_array)
784
-    {
785
-        if ($model instanceof EEM_CPT_Base) {
786
-            $entity_array['link'] = get_permalink($db_row[$model->get_primary_key_field()->get_qualified_column()]);
787
-        }
788
-        return $entity_array;
789
-    }
790
-
791
-
792
-
793
-    /**
794
-     * Gets links we want to add to the response
795
-     *
796
-     * @global \WP_REST_Server $wp_rest_server
797
-     * @param EEM_Base        $model
798
-     * @param array            $db_row
799
-     * @param array            $entity_array
800
-     * @return array the _links item in the entity
801
-     */
802
-    protected function getEntityLinks($model, $db_row, $entity_array)
803
-    {
804
-        //add basic links
805
-        $links = array();
806
-        if ($model->has_primary_key_field()) {
807
-            $links['self'] = array(
808
-                array(
809
-                    'href' => $this->getVersionedLinkTo(
810
-                        EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
811
-                        . '/'
812
-                        . $entity_array[$model->primary_key_name()]
813
-                    ),
814
-                ),
815
-            );
816
-        }
817
-        $links['collection'] = array(
818
-            array(
819
-                'href' => $this->getVersionedLinkTo(
820
-                    EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
821
-                ),
822
-            ),
823
-        );
824
-        //add links to related models
825
-        if ($model->has_primary_key_field()) {
826
-            foreach ($this->getModelVersionInfo()->relationSettings($model) as $relation_name => $relation_obj) {
827
-                $related_model_part = Read::getRelatedEntityName($relation_name, $relation_obj);
828
-                $links[EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part] = array(
829
-                    array(
830
-                        'href'   => $this->getVersionedLinkTo(
831
-                            EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
832
-                            . '/'
833
-                            . $entity_array[$model->primary_key_name()]
834
-                            . '/'
835
-                            . $related_model_part
836
-                        ),
837
-                        'single' => $relation_obj instanceof EE_Belongs_To_Relation ? true : false,
838
-                    ),
839
-                );
840
-            }
841
-        }
842
-        return $links;
843
-    }
844
-
845
-
846
-
847
-    /**
848
-     * Adds the included models indicated in the request to the entity provided
849
-     *
850
-     * @param EEM_Base        $model
851
-     * @param WP_REST_Request $rest_request
852
-     * @param array            $entity_array
853
-     * @param array            $db_row
854
-     * @return array the modified entity
855
-     */
856
-    protected function includeRequestedModels(
857
-        EEM_Base $model,
858
-        WP_REST_Request $rest_request,
859
-        $entity_array,
860
-        $db_row = array()
861
-    ) {
862
-        //if $db_row not included, hope the entity array has what we need
863
-        if (! $db_row) {
864
-            $db_row = $entity_array;
865
-        }
866
-        $includes_for_this_model = $this->explodeAndGetItemsPrefixedWith($rest_request->get_param('include'), '');
867
-        $includes_for_this_model = $this->removeModelNamesFromArray($includes_for_this_model);
868
-        //if they passed in * or didn't specify any includes, return everything
869
-        if (! in_array('*', $includes_for_this_model)
870
-            && ! empty($includes_for_this_model)
871
-        ) {
872
-            if ($model->has_primary_key_field()) {
873
-                //always include the primary key. ya just gotta know that at least
874
-                $includes_for_this_model[] = $model->primary_key_name();
875
-            }
876
-            if ($this->explodeAndGetItemsPrefixedWith($rest_request->get_param('calculate'), '')) {
877
-                $includes_for_this_model[] = '_calculated_fields';
878
-            }
879
-            $entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model));
880
-        }
881
-        $relation_settings = $this->getModelVersionInfo()->relationSettings($model);
882
-        foreach ($relation_settings as $relation_name => $relation_obj) {
883
-            $related_fields_to_include = $this->explodeAndGetItemsPrefixedWith(
884
-                $rest_request->get_param('include'),
885
-                $relation_name
886
-            );
887
-            $related_fields_to_calculate = $this->explodeAndGetItemsPrefixedWith(
888
-                $rest_request->get_param('calculate'),
889
-                $relation_name
890
-            );
891
-            //did they specify they wanted to include a related model, or
892
-            //specific fields from a related model?
893
-            //or did they specify to calculate a field from a related model?
894
-            if ($related_fields_to_include || $related_fields_to_calculate) {
895
-                //if so, we should include at least some part of the related model
896
-                $pretend_related_request = new WP_REST_Request();
897
-                $pretend_related_request->set_query_params(
898
-                    array(
899
-                        'caps'      => $rest_request->get_param('caps'),
900
-                        'include'   => $related_fields_to_include,
901
-                        'calculate' => $related_fields_to_calculate,
902
-                    )
903
-                );
904
-                $pretend_related_request->add_header('no_rest_headers', true);
905
-                $primary_model_query_params = $model->alter_query_params_to_restrict_by_ID(
906
-                    $model->get_index_primary_key_string(
907
-                        $model->deduce_fields_n_values_from_cols_n_values($db_row)
908
-                    )
909
-                );
910
-                $related_results = $this->getEntitiesFromRelationUsingModelQueryParams(
911
-                    $primary_model_query_params,
912
-                    $relation_obj,
913
-                    $pretend_related_request
914
-                );
915
-                $entity_array[Read::getRelatedEntityName($relation_name, $relation_obj)] = $related_results
916
-                                                                                           instanceof
917
-                                                                                           WP_Error
918
-                    ? null
919
-                    : $related_results;
920
-            }
921
-        }
922
-        return $entity_array;
923
-    }
924
-
925
-
926
-
927
-    /**
928
-     * Returns a new array with all the names of models removed. Eg
929
-     * array( 'Event', 'Datetime.*', 'foobar' ) would become array( 'Datetime.*', 'foobar' )
930
-     *
931
-     * @param array $arr
932
-     * @return array
933
-     */
934
-    private function removeModelNamesFromArray($arr)
935
-    {
936
-        return array_diff($arr, array_keys(EE_Registry::instance()->non_abstract_db_models));
937
-    }
938
-
939
-
940
-
941
-    /**
942
-     * Gets the calculated fields for the response
943
-     *
944
-     * @param EEM_Base        $model
945
-     * @param array            $wpdb_row
946
-     * @param WP_REST_Request $rest_request
947
-     * @return \stdClass the _calculations item in the entity
948
-     * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
949
-     * did, let's know about it ASAP, so let the exception bubble up)
950
-     */
951
-    protected function getEntityCalculations($model, $wpdb_row, $rest_request)
952
-    {
953
-        $calculated_fields = $this->explodeAndGetItemsPrefixedWith(
954
-            $rest_request->get_param('calculate'),
955
-            ''
956
-        );
957
-        //note: setting calculate=* doesn't do anything
958
-        $calculated_fields_to_return = new \stdClass();
959
-        foreach ($calculated_fields as $field_to_calculate) {
960
-            try {
961
-                $calculated_fields_to_return->$field_to_calculate = ModelDataTranslator::prepareFieldValueForJson(
962
-                    null,
963
-                    $this->fields_calculator->retrieveCalculatedFieldValue(
964
-                        $model,
965
-                        $field_to_calculate,
966
-                        $wpdb_row,
967
-                        $rest_request,
968
-                        $this
969
-                    ),
970
-                    $this->getModelVersionInfo()->requestedVersion()
971
-                );
972
-            } catch (RestException $e) {
973
-                //if we don't have permission to read it, just leave it out. but let devs know about the problem
974
-                $this->setResponseHeader(
975
-                    'Notices-Field-Calculation-Errors['
976
-                    . $e->getStringCode()
977
-                    . ']['
978
-                    . $model->get_this_model_name()
979
-                    . ']['
980
-                    . $field_to_calculate
981
-                    . ']',
982
-                    $e->getMessage(),
983
-                    true
984
-                );
985
-            }
986
-        }
987
-        return $calculated_fields_to_return;
988
-    }
989
-
990
-
991
-
992
-    /**
993
-     * Gets the full URL to the resource, taking the requested version into account
994
-     *
995
-     * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
996
-     * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
997
-     */
998
-    public function getVersionedLinkTo($link_part_after_version_and_slash)
999
-    {
1000
-        return rest_url(
1001
-            EED_Core_Rest_Api::get_versioned_route_to(
1002
-                $link_part_after_version_and_slash,
1003
-                $this->getModelVersionInfo()->requestedVersion()
1004
-            )
1005
-        );
1006
-    }
1007
-
1008
-
1009
-
1010
-    /**
1011
-     * Gets the correct lowercase name for the relation in the API according
1012
-     * to the relation's type
1013
-     *
1014
-     * @param string                  $relation_name
1015
-     * @param \EE_Model_Relation_Base $relation_obj
1016
-     * @return string
1017
-     */
1018
-    public static function getRelatedEntityName($relation_name, $relation_obj)
1019
-    {
1020
-        if ($relation_obj instanceof EE_Belongs_To_Relation) {
1021
-            return strtolower($relation_name);
1022
-        } else {
1023
-            return EEH_Inflector::pluralize_and_lower($relation_name);
1024
-        }
1025
-    }
1026
-
1027
-
1028
-
1029
-    /**
1030
-     * Gets the one model object with the specified id for the specified model
1031
-     *
1032
-     * @param EEM_Base        $model
1033
-     * @param WP_REST_Request $request
1034
-     * @return array|WP_Error
1035
-     */
1036
-    public function getEntityFromModel($model, $request)
1037
-    {
1038
-        $context = $this->validateContext($request->get_param('caps'));
1039
-        return $this->getOneOrReportPermissionError($model, $request, $context);
1040
-    }
1041
-
1042
-
1043
-
1044
-    /**
1045
-     * If a context is provided which isn't valid, maybe it was added in a future
1046
-     * version so just treat it as a default read
1047
-     *
1048
-     * @param string $context
1049
-     * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
1050
-     */
1051
-    public function validateContext($context)
1052
-    {
1053
-        if (! $context) {
1054
-            $context = EEM_Base::caps_read;
1055
-        }
1056
-        $valid_contexts = EEM_Base::valid_cap_contexts();
1057
-        if (in_array($context, $valid_contexts)) {
1058
-            return $context;
1059
-        } else {
1060
-            return EEM_Base::caps_read;
1061
-        }
1062
-    }
1063
-
1064
-
1065
-
1066
-    /**
1067
-     * Verifies the passed in value is an allowable default where conditions value.
1068
-     *
1069
-     * @param $default_query_params
1070
-     * @return string
1071
-     */
1072
-    public function validateDefaultQueryParams($default_query_params)
1073
-    {
1074
-        $valid_default_where_conditions_for_api_calls = array(
1075
-            EEM_Base::default_where_conditions_all,
1076
-            EEM_Base::default_where_conditions_minimum_all,
1077
-            EEM_Base::default_where_conditions_minimum_others,
1078
-        );
1079
-        if (! $default_query_params) {
1080
-            $default_query_params = EEM_Base::default_where_conditions_all;
1081
-        }
1082
-        if (in_array(
1083
-            $default_query_params,
1084
-            $valid_default_where_conditions_for_api_calls,
1085
-            true
1086
-        )) {
1087
-            return $default_query_params;
1088
-        } else {
1089
-            return EEM_Base::default_where_conditions_all;
1090
-        }
1091
-    }
1092
-
1093
-
1094
-
1095
-    /**
1096
-     * Translates API filter get parameter into $query_params array used by EEM_Base::get_all().
1097
-     * Note: right now the query parameter keys for fields (and related fields)
1098
-     * can be left as-is, but it's quite possible this will change someday.
1099
-     * Also, this method's contents might be candidate for moving to Model_Data_Translator
1100
-     *
1101
-     * @param EEM_Base $model
1102
-     * @param array     $query_parameters from $_GET parameter @see Read:handle_request_get_all
1103
-     * @return array like what EEM_Base::get_all() expects or FALSE to indicate
1104
-     *                                    that absolutely no results should be returned
1105
-     * @throws EE_Error
1106
-     * @throws RestException
1107
-     */
1108
-    public function createModelQueryParams($model, $query_parameters)
1109
-    {
1110
-        $model_query_params = array();
1111
-        if (isset($query_parameters['where'])) {
1112
-            $model_query_params[0] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1113
-                $query_parameters['where'],
1114
-                $model,
1115
-                $this->getModelVersionInfo()->requestedVersion()
1116
-            );
1117
-        }
1118
-        if (isset($query_parameters['order_by'])) {
1119
-            $order_by = $query_parameters['order_by'];
1120
-        } elseif (isset($query_parameters['orderby'])) {
1121
-            $order_by = $query_parameters['orderby'];
1122
-        } else {
1123
-            $order_by = null;
1124
-        }
1125
-        if ($order_by !== null) {
1126
-            if (is_array($order_by)) {
1127
-                $order_by = ModelDataTranslator::prepareFieldNamesInArrayKeysFromJson($order_by);
1128
-            } else {
1129
-                //it's a single item
1130
-                $order_by = ModelDataTranslator::prepareFieldNameFromJson($order_by);
1131
-            }
1132
-            $model_query_params['order_by'] = $order_by;
1133
-        }
1134
-        if (isset($query_parameters['group_by'])) {
1135
-            $group_by = $query_parameters['group_by'];
1136
-        } elseif (isset($query_parameters['groupby'])) {
1137
-            $group_by = $query_parameters['groupby'];
1138
-        } else {
1139
-            $group_by = array_keys($model->get_combined_primary_key_fields());
1140
-        }
1141
-        //make sure they're all real names
1142
-        if (is_array($group_by)) {
1143
-            $group_by = ModelDataTranslator::prepareFieldNamesFromJson($group_by);
1144
-        }
1145
-        if ($group_by !== null) {
1146
-            $model_query_params['group_by'] = $group_by;
1147
-        }
1148
-        if (isset($query_parameters['having'])) {
1149
-            $model_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1150
-                $query_parameters['having'],
1151
-                $model,
1152
-                $this->getModelVersionInfo()->requestedVersion()
1153
-            );
1154
-        }
1155
-        if (isset($query_parameters['order'])) {
1156
-            $model_query_params['order'] = $query_parameters['order'];
1157
-        }
1158
-        if (isset($query_parameters['mine'])) {
1159
-            $model_query_params = $model->alter_query_params_to_only_include_mine($model_query_params);
1160
-        }
1161
-        if (isset($query_parameters['limit'])) {
1162
-            //limit should be either a string like '23' or '23,43', or an array with two items in it
1163
-            if (! is_array($query_parameters['limit'])) {
1164
-                $limit_array = explode(',', (string)$query_parameters['limit']);
1165
-            } else {
1166
-                $limit_array = $query_parameters['limit'];
1167
-            }
1168
-            $sanitized_limit = array();
1169
-            foreach ($limit_array as $key => $limit_part) {
1170
-                if ($this->debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1171
-                    throw new EE_Error(
1172
-                        sprintf(
1173
-                            __(
1174
-                                // @codingStandardsIgnoreStart
1175
-                                'An invalid limit filter was provided. It was: %s. If the EE4 JSON REST API weren\'t in debug mode, this message would not appear.',
1176
-                                // @codingStandardsIgnoreEnd
1177
-                                'event_espresso'
1178
-                            ),
1179
-                            wp_json_encode($query_parameters['limit'])
1180
-                        )
1181
-                    );
1182
-                }
1183
-                $sanitized_limit[] = (int)$limit_part;
1184
-            }
1185
-            $model_query_params['limit'] = implode(',', $sanitized_limit);
1186
-        } else {
1187
-            $model_query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
1188
-        }
1189
-        if (isset($query_parameters['caps'])) {
1190
-            $model_query_params['caps'] = $this->validateContext($query_parameters['caps']);
1191
-        } else {
1192
-            $model_query_params['caps'] = EEM_Base::caps_read;
1193
-        }
1194
-        if (isset($query_parameters['default_where_conditions'])) {
1195
-            $model_query_params['default_where_conditions'] = $this->validateDefaultQueryParams(
1196
-                $query_parameters['default_where_conditions']
1197
-            );
1198
-        }
1199
-        return apply_filters('FHEE__Read__create_model_query_params', $model_query_params, $query_parameters, $model);
1200
-    }
1201
-
1202
-
1203
-
1204
-    /**
1205
-     * Changes the REST-style query params for use in the models
1206
-     *
1207
-     * @deprecated
1208
-     * @param EEM_Base $model
1209
-     * @param array     $query_params sub-array from @see EEM_Base::get_all()
1210
-     * @return array
1211
-     */
1212
-    public function prepareRestQueryParamsKeyForModels($model, $query_params)
1213
-    {
1214
-        $model_ready_query_params = array();
1215
-        foreach ($query_params as $key => $value) {
1216
-            if (is_array($value)) {
1217
-                $model_ready_query_params[$key] = $this->prepareRestQueryParamsKeyForModels($model, $value);
1218
-            } else {
1219
-                $model_ready_query_params[$key] = $value;
1220
-            }
1221
-        }
1222
-        return $model_ready_query_params;
1223
-    }
1224
-
1225
-
1226
-
1227
-    /**
1228
-     * @deprecated instead use ModelDataTranslator::prepareFieldValuesFromJson()
1229
-     * @param $model
1230
-     * @param $query_params
1231
-     * @return array
1232
-     */
1233
-    public function prepareRestQueryParamsValuesForModels($model, $query_params)
1234
-    {
1235
-        $model_ready_query_params = array();
1236
-        foreach ($query_params as $key => $value) {
1237
-            if (is_array($value)) {
1238
-                $model_ready_query_params[$key] = $this->prepareRestQueryParamsValuesForModels($model, $value);
1239
-            } else {
1240
-                $model_ready_query_params[$key] = $value;
1241
-            }
1242
-        }
1243
-        return $model_ready_query_params;
1244
-    }
1245
-
1246
-
1247
-
1248
-    /**
1249
-     * Explodes the string on commas, and only returns items with $prefix followed by a period.
1250
-     * If no prefix is specified, returns items with no period.
1251
-     *
1252
-     * @param string|array $string_to_explode eg "jibba,jabba, blah, blah, blah" or array('jibba', 'jabba' )
1253
-     * @param string       $prefix            "Event" or "foobar"
1254
-     * @return array $string_to_exploded exploded on COMMAS, and if a prefix was specified
1255
-     *                                        we only return strings starting with that and a period; if no prefix was
1256
-     *                                        specified we return all items containing NO periods
1257
-     */
1258
-    public function explodeAndGetItemsPrefixedWith($string_to_explode, $prefix)
1259
-    {
1260
-        if (is_string($string_to_explode)) {
1261
-            $exploded_contents = explode(',', $string_to_explode);
1262
-        } elseif (is_array($string_to_explode)) {
1263
-            $exploded_contents = $string_to_explode;
1264
-        } else {
1265
-            $exploded_contents = array();
1266
-        }
1267
-        //if the string was empty, we want an empty array
1268
-        $exploded_contents = array_filter($exploded_contents);
1269
-        $contents_with_prefix = array();
1270
-        foreach ($exploded_contents as $item) {
1271
-            $item = trim($item);
1272
-            //if no prefix was provided, so we look for items with no "." in them
1273
-            if (! $prefix) {
1274
-                //does this item have a period?
1275
-                if (strpos($item, '.') === false) {
1276
-                    //if not, then its what we're looking for
1277
-                    $contents_with_prefix[] = $item;
1278
-                }
1279
-            } elseif (strpos($item, $prefix . '.') === 0) {
1280
-                //this item has the prefix and a period, grab it
1281
-                $contents_with_prefix[] = substr(
1282
-                    $item,
1283
-                    strpos($item, $prefix . '.') + strlen($prefix . '.')
1284
-                );
1285
-            } elseif ($item === $prefix) {
1286
-                //this item is JUST the prefix
1287
-                //so let's grab everything after, which is a blank string
1288
-                $contents_with_prefix[] = '';
1289
-            }
1290
-        }
1291
-        return $contents_with_prefix;
1292
-    }
1293
-
1294
-
1295
-
1296
-    /**
1297
-     * @deprecated since 4.8.36.rc.001 You should instead use Read::explode_and_get_items_prefixed_with.
1298
-     * Deprecated because its return values were really quite confusing- sometimes it returned
1299
-     * an empty array (when the include string was blank or '*') or sometimes it returned
1300
-     * array('*') (when you provided a model and a model of that kind was found).
1301
-     * Parses the $include_string so we fetch all the field names relating to THIS model
1302
-     * (ie have NO period in them), or for the provided model (ie start with the model
1303
-     * name and then a period).
1304
-     * @param string $include_string @see Read:handle_request_get_all
1305
-     * @param string $model_name
1306
-     * @return array of fields for this model. If $model_name is provided, then
1307
-     *                               the fields for that model, with the model's name removed from each.
1308
-     *                               If $include_string was blank or '*' returns an empty array
1309
-     */
1310
-    public function extractIncludesForThisModel($include_string, $model_name = null)
1311
-    {
1312
-        if (is_array($include_string)) {
1313
-            $include_string = implode(',', $include_string);
1314
-        }
1315
-        if ($include_string === '*' || $include_string === '') {
1316
-            return array();
1317
-        }
1318
-        $includes = explode(',', $include_string);
1319
-        $extracted_fields_to_include = array();
1320
-        if ($model_name) {
1321
-            foreach ($includes as $field_to_include) {
1322
-                $field_to_include = trim($field_to_include);
1323
-                if (strpos($field_to_include, $model_name . '.') === 0) {
1324
-                    //found the model name at the exact start
1325
-                    $field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1326
-                    $extracted_fields_to_include[] = $field_sans_model_name;
1327
-                } elseif ($field_to_include == $model_name) {
1328
-                    $extracted_fields_to_include[] = '*';
1329
-                }
1330
-            }
1331
-        } else {
1332
-            //look for ones with no period
1333
-            foreach ($includes as $field_to_include) {
1334
-                $field_to_include = trim($field_to_include);
1335
-                if (strpos($field_to_include, '.') === false
1336
-                    && ! $this->getModelVersionInfo()->isModelNameInThisVersion($field_to_include)
1337
-                ) {
1338
-                    $extracted_fields_to_include[] = $field_to_include;
1339
-                }
1340
-            }
1341
-        }
1342
-        return $extracted_fields_to_include;
1343
-    }
1344
-
1345
-
1346
-
1347
-    /**
1348
-     * Gets the single item using the model according to the request in the context given, otherwise
1349
-     * returns that it's inaccessible to the current user
1350
-
1351
-     *
503
+	 * @param \EE_Model_Relation_Base $relation
504
+	 * @param WP_REST_Request        $request
505
+	 * @return array|WP_Error
506
+	 * @throws EE_Error
507
+	 */
508
+	public function getEntitiesFromRelation($id, $relation, $request)
509
+	{
510
+		if (! $relation->get_this_model()->has_primary_key_field()) {
511
+			throw new EE_Error(
512
+				sprintf(
513
+					__(
514
+						// @codingStandardsIgnoreStart
515
+						'Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
516
+						// @codingStandardsIgnoreEnd
517
+						'event_espresso'
518
+					),
519
+					$relation->get_this_model()->get_this_model_name()
520
+				)
521
+			);
522
+		}
523
+		return $this->getEntitiesFromRelationUsingModelQueryParams(
524
+			array(
525
+				array(
526
+					$relation->get_this_model()->primary_key_name() => $id,
527
+				),
528
+			),
529
+			$relation,
530
+			$request
531
+		);
532
+	}
533
+
534
+
535
+
536
+	/**
537
+	 * Sets the headers that are based on the model and query params,
538
+	 * like the total records. This should only be called on the original request
539
+	 * from the client, not on subsequent internal
540
+	 *
541
+	 * @param EEM_Base $model
542
+	 * @param array     $query_params
543
+	 * @return void
544
+	 */
545
+	protected function setHeadersFromQueryParams($model, $query_params)
546
+	{
547
+		$this->setDebugInfo('model query params', $query_params);
548
+		$this->setDebugInfo(
549
+			'missing caps',
550
+			Capabilities::getMissingPermissionsString($model, $query_params['caps'])
551
+		);
552
+		//normally the limit to a 2-part array, where the 2nd item is the limit
553
+		if (! isset($query_params['limit'])) {
554
+			$query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
555
+		}
556
+		if (is_array($query_params['limit'])) {
557
+			$limit_parts = $query_params['limit'];
558
+		} else {
559
+			$limit_parts = explode(',', $query_params['limit']);
560
+			if (count($limit_parts) == 1) {
561
+				$limit_parts = array(0, $limit_parts[0]);
562
+			}
563
+		}
564
+		//remove the group by and having parts of the query, as those will
565
+		//make the sql query return an array of values, instead of just a single value
566
+		unset($query_params['group_by'], $query_params['having'], $query_params['limit']);
567
+		$count = $model->count($query_params, null, true);
568
+		$pages = $count / $limit_parts[1];
569
+		$this->setResponseHeader('Total', $count, false);
570
+		$this->setResponseHeader('PageSize', $limit_parts[1], false);
571
+		$this->setResponseHeader('TotalPages', ceil($pages), false);
572
+	}
573
+
574
+
575
+
576
+	/**
577
+	 * Changes database results into REST API entities
578
+	 *
579
+	 * @param EEM_Base        $model
580
+	 * @param array            $db_row     like results from $wpdb->get_results()
581
+	 * @param WP_REST_Request $rest_request
582
+	 * @param string           $deprecated no longer used
583
+	 * @return array ready for being converted into json for sending to client
584
+	 */
585
+	public function createEntityFromWpdbResult($model, $db_row, $rest_request, $deprecated = null)
586
+	{
587
+		if (! $rest_request instanceof WP_REST_Request) {
588
+			//ok so this was called in the old style, where the 3rd arg was
589
+			//$include, and the 4th arg was $context
590
+			//now setup the request just to avoid fatal errors, although we won't be able
591
+			//to truly make use of it because it's kinda devoid of info
592
+			$rest_request = new WP_REST_Request();
593
+			$rest_request->set_param('include', $rest_request);
594
+			$rest_request->set_param('caps', $deprecated);
595
+		}
596
+		if ($rest_request->get_param('caps') == null) {
597
+			$rest_request->set_param('caps', EEM_Base::caps_read);
598
+		}
599
+		$entity_array = $this->createBareEntityFromWpdbResults($model, $db_row);
600
+		$entity_array = $this->addExtraFields($model, $db_row, $entity_array);
601
+		$entity_array['_links'] = $this->getEntityLinks($model, $db_row, $entity_array);
602
+		$entity_array['_calculated_fields'] = $this->getEntityCalculations($model, $db_row, $rest_request);
603
+		$entity_array = apply_filters(
604
+			'FHEE__Read__create_entity_from_wpdb_results__entity_before_including_requested_models',
605
+			$entity_array,
606
+			$model,
607
+			$rest_request->get_param('caps'),
608
+			$rest_request,
609
+			$this
610
+		);
611
+		$entity_array = $this->includeRequestedModels($model, $rest_request, $entity_array, $db_row);
612
+		$entity_array = apply_filters(
613
+			'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
614
+			$entity_array,
615
+			$model,
616
+			$rest_request->get_param('caps'),
617
+			$rest_request,
618
+			$this
619
+		);
620
+		$result_without_inaccessible_fields = Capabilities::filterOutInaccessibleEntityFields(
621
+			$entity_array,
622
+			$model,
623
+			$rest_request->get_param('caps'),
624
+			$this->getModelVersionInfo(),
625
+			$model->get_index_primary_key_string(
626
+				$model->deduce_fields_n_values_from_cols_n_values($db_row)
627
+			)
628
+		);
629
+		$this->setDebugInfo(
630
+			'inaccessible fields',
631
+			array_keys(array_diff_key($entity_array, $result_without_inaccessible_fields))
632
+		);
633
+		return apply_filters(
634
+			'FHEE__Read__create_entity_from_wpdb_results__entity_return',
635
+			$result_without_inaccessible_fields,
636
+			$model,
637
+			$rest_request->get_param('caps')
638
+		);
639
+	}
640
+
641
+
642
+
643
+	/**
644
+	 * Creates a REST entity array (JSON object we're going to return in the response, but
645
+	 * for now still a PHP array, but soon enough we'll call json_encode on it, don't worry),
646
+	 * from $wpdb->get_row( $sql, ARRAY_A)
647
+	 *
648
+	 * @param EEM_Base $model
649
+	 * @param array     $db_row
650
+	 * @return array entity mostly ready for converting to JSON and sending in the response
651
+	 *
652
+	 */
653
+	protected function createBareEntityFromWpdbResults(EEM_Base $model, $db_row)
654
+	{
655
+		$result = $model->deduce_fields_n_values_from_cols_n_values($db_row);
656
+		$result = array_intersect_key(
657
+			$result,
658
+			$this->getModelVersionInfo()->fieldsOnModelInThisVersion($model)
659
+		);
660
+		//if this is a CPT, we need to set the global $post to it,
661
+		//otherwise shortcodes etc won't work properly while rendering it
662
+		if ($model instanceof \EEM_CPT_Base) {
663
+			$do_chevy_shuffle = true;
664
+		} else {
665
+			$do_chevy_shuffle = false;
666
+		}
667
+		if ($do_chevy_shuffle) {
668
+			global $post;
669
+			$old_post = $post;
670
+			$post = get_post($result[$model->primary_key_name()]);
671
+			if (! $post instanceof \WP_Post) {
672
+				//well that's weird, because $result is what we JUST fetched from the database
673
+				throw new RestException(
674
+					'error_fetching_post_from_database_results',
675
+					esc_html__(
676
+						'An item was retrieved from the database but it\'s not a WP_Post like it should be.',
677
+						'event_espresso'
678
+					)
679
+				);
680
+			}
681
+			$model_object_classname = 'EE_' . $model->get_this_model_name();
682
+			$post->{$model_object_classname} = \EE_Registry::instance()->load_class(
683
+				$model_object_classname,
684
+				$result,
685
+				false,
686
+				false
687
+			);
688
+		}
689
+		foreach ($result as $field_name => $field_value) {
690
+			$field_obj = $model->field_settings_for($field_name);
691
+			if ($this->isSubclassOfOne($field_obj, $this->getModelVersionInfo()->fieldsIgnored())) {
692
+				unset($result[$field_name]);
693
+			} elseif ($this->isSubclassOfOne(
694
+				$field_obj,
695
+				$this->getModelVersionInfo()->fieldsThatHaveRenderedFormat()
696
+			)
697
+			) {
698
+				$result[$field_name] = array(
699
+					'raw'      => $this->prepareFieldObjValueForJson($field_obj, $field_value),
700
+					'rendered' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
701
+				);
702
+			} elseif ($this->isSubclassOfOne(
703
+				$field_obj,
704
+				$this->getModelVersionInfo()->fieldsThatHavePrettyFormat()
705
+			)
706
+			) {
707
+				$result[$field_name] = array(
708
+					'raw'    => $this->prepareFieldObjValueForJson($field_obj, $field_value),
709
+					'pretty' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
710
+				);
711
+			} elseif ($field_obj instanceof \EE_Datetime_Field) {
712
+				$field_value = $field_obj->prepare_for_set_from_db($field_value);
713
+				$timezone = $field_value->getTimezone();
714
+				EEH_DTT_Helper::setTimezone($field_value, new DateTimeZone('UTC'));
715
+				$result[$field_name . '_gmt'] = ModelDataTranslator::prepareFieldValuesForJson(
716
+					$field_obj,
717
+					$field_value,
718
+					$this->getModelVersionInfo()->requestedVersion()
719
+				);
720
+				EEH_DTT_Helper::setTimezone($field_value, $timezone);
721
+				$result[$field_name] = ModelDataTranslator::prepareFieldValuesForJson(
722
+					$field_obj,
723
+					$field_value,
724
+					$this->getModelVersionInfo()->requestedVersion()
725
+				);
726
+			} else {
727
+				$result[$field_name] = $this->prepareFieldObjValueForJson($field_obj, $field_value);
728
+			}
729
+		}
730
+		if ($do_chevy_shuffle) {
731
+			$post = $old_post;
732
+		}
733
+		return $result;
734
+	}
735
+
736
+
737
+
738
+	/**
739
+	 * Takes a value all the way from the DB representation, to the model object's representation, to the
740
+	 * user-facing PHP representation, to the REST API representation. (Assumes you've already taken from the DB
741
+	 * representation using $field_obj->prepare_for_set_from_db())
742
+	 *
743
+	 * @param EE_Model_Field_Base $field_obj
744
+	 * @param mixed $value as it's stored on a model object
745
+	 * @param string $format valid values are 'normal' (default), 'pretty', 'datetime_obj'
746
+	 * @return mixed
747
+	 * @throws ObjectDetectedException if $value contains a PHP object
748
+	 */
749
+	protected function prepareFieldObjValueForJson(EE_Model_Field_Base $field_obj, $value, $format = 'normal')
750
+	{
751
+		$value = $field_obj->prepare_for_set_from_db($value);
752
+		switch ($format) {
753
+			case 'pretty':
754
+				$value = $field_obj->prepare_for_pretty_echoing($value);
755
+				break;
756
+			case 'normal':
757
+			default:
758
+				$value = $field_obj->prepare_for_get($value);
759
+				break;
760
+		}
761
+		return ModelDataTranslator::prepareFieldValuesForJson(
762
+			$field_obj,
763
+			$value,
764
+			$this->getModelVersionInfo()->requestedVersion()
765
+		);
766
+	}
767
+
768
+
769
+
770
+	/**
771
+	 * Adds a few extra fields to the entity response
772
+	 *
773
+	 * @param EEM_Base $model
774
+	 * @param array     $db_row
775
+	 * @param array     $entity_array
776
+	 * @return array modified entity
777
+	 */
778
+	protected function addExtraFields(EEM_Base $model, $db_row, $entity_array)
779
+	{
780
+		if ($model instanceof EEM_CPT_Base) {
781
+			$entity_array['link'] = get_permalink($db_row[$model->get_primary_key_field()->get_qualified_column()]);
782
+		}
783
+		return $entity_array;
784
+	}
785
+
786
+
787
+
788
+	/**
789
+	 * Gets links we want to add to the response
790
+	 *
791
+	 * @global \WP_REST_Server $wp_rest_server
792
+	 * @param EEM_Base        $model
793
+	 * @param array            $db_row
794
+	 * @param array            $entity_array
795
+	 * @return array the _links item in the entity
796
+	 */
797
+	protected function getEntityLinks($model, $db_row, $entity_array)
798
+	{
799
+		//add basic links
800
+		$links = array();
801
+		if ($model->has_primary_key_field()) {
802
+			$links['self'] = array(
803
+				array(
804
+					'href' => $this->getVersionedLinkTo(
805
+						EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
806
+						. '/'
807
+						. $entity_array[$model->primary_key_name()]
808
+					),
809
+				),
810
+			);
811
+		}
812
+		$links['collection'] = array(
813
+			array(
814
+				'href' => $this->getVersionedLinkTo(
815
+					EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
816
+				),
817
+			),
818
+		);
819
+		//add links to related models
820
+		if ($model->has_primary_key_field()) {
821
+			foreach ($this->getModelVersionInfo()->relationSettings($model) as $relation_name => $relation_obj) {
822
+				$related_model_part = Read::getRelatedEntityName($relation_name, $relation_obj);
823
+				$links[EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part] = array(
824
+					array(
825
+						'href'   => $this->getVersionedLinkTo(
826
+							EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
827
+							. '/'
828
+							. $entity_array[$model->primary_key_name()]
829
+							. '/'
830
+							. $related_model_part
831
+						),
832
+						'single' => $relation_obj instanceof EE_Belongs_To_Relation ? true : false,
833
+					),
834
+				);
835
+			}
836
+		}
837
+		return $links;
838
+	}
839
+
840
+
841
+
842
+	/**
843
+	 * Adds the included models indicated in the request to the entity provided
844
+	 *
845
+	 * @param EEM_Base        $model
846
+	 * @param WP_REST_Request $rest_request
847
+	 * @param array            $entity_array
848
+	 * @param array            $db_row
849
+	 * @return array the modified entity
850
+	 */
851
+	protected function includeRequestedModels(
852
+		EEM_Base $model,
853
+		WP_REST_Request $rest_request,
854
+		$entity_array,
855
+		$db_row = array()
856
+	) {
857
+		//if $db_row not included, hope the entity array has what we need
858
+		if (! $db_row) {
859
+			$db_row = $entity_array;
860
+		}
861
+		$includes_for_this_model = $this->explodeAndGetItemsPrefixedWith($rest_request->get_param('include'), '');
862
+		$includes_for_this_model = $this->removeModelNamesFromArray($includes_for_this_model);
863
+		//if they passed in * or didn't specify any includes, return everything
864
+		if (! in_array('*', $includes_for_this_model)
865
+			&& ! empty($includes_for_this_model)
866
+		) {
867
+			if ($model->has_primary_key_field()) {
868
+				//always include the primary key. ya just gotta know that at least
869
+				$includes_for_this_model[] = $model->primary_key_name();
870
+			}
871
+			if ($this->explodeAndGetItemsPrefixedWith($rest_request->get_param('calculate'), '')) {
872
+				$includes_for_this_model[] = '_calculated_fields';
873
+			}
874
+			$entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model));
875
+		}
876
+		$relation_settings = $this->getModelVersionInfo()->relationSettings($model);
877
+		foreach ($relation_settings as $relation_name => $relation_obj) {
878
+			$related_fields_to_include = $this->explodeAndGetItemsPrefixedWith(
879
+				$rest_request->get_param('include'),
880
+				$relation_name
881
+			);
882
+			$related_fields_to_calculate = $this->explodeAndGetItemsPrefixedWith(
883
+				$rest_request->get_param('calculate'),
884
+				$relation_name
885
+			);
886
+			//did they specify they wanted to include a related model, or
887
+			//specific fields from a related model?
888
+			//or did they specify to calculate a field from a related model?
889
+			if ($related_fields_to_include || $related_fields_to_calculate) {
890
+				//if so, we should include at least some part of the related model
891
+				$pretend_related_request = new WP_REST_Request();
892
+				$pretend_related_request->set_query_params(
893
+					array(
894
+						'caps'      => $rest_request->get_param('caps'),
895
+						'include'   => $related_fields_to_include,
896
+						'calculate' => $related_fields_to_calculate,
897
+					)
898
+				);
899
+				$pretend_related_request->add_header('no_rest_headers', true);
900
+				$primary_model_query_params = $model->alter_query_params_to_restrict_by_ID(
901
+					$model->get_index_primary_key_string(
902
+						$model->deduce_fields_n_values_from_cols_n_values($db_row)
903
+					)
904
+				);
905
+				$related_results = $this->getEntitiesFromRelationUsingModelQueryParams(
906
+					$primary_model_query_params,
907
+					$relation_obj,
908
+					$pretend_related_request
909
+				);
910
+				$entity_array[Read::getRelatedEntityName($relation_name, $relation_obj)] = $related_results
911
+																						   instanceof
912
+																						   WP_Error
913
+					? null
914
+					: $related_results;
915
+			}
916
+		}
917
+		return $entity_array;
918
+	}
919
+
920
+
921
+
922
+	/**
923
+	 * Returns a new array with all the names of models removed. Eg
924
+	 * array( 'Event', 'Datetime.*', 'foobar' ) would become array( 'Datetime.*', 'foobar' )
925
+	 *
926
+	 * @param array $arr
927
+	 * @return array
928
+	 */
929
+	private function removeModelNamesFromArray($arr)
930
+	{
931
+		return array_diff($arr, array_keys(EE_Registry::instance()->non_abstract_db_models));
932
+	}
933
+
934
+
935
+
936
+	/**
937
+	 * Gets the calculated fields for the response
938
+	 *
939
+	 * @param EEM_Base        $model
940
+	 * @param array            $wpdb_row
941
+	 * @param WP_REST_Request $rest_request
942
+	 * @return \stdClass the _calculations item in the entity
943
+	 * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
944
+	 * did, let's know about it ASAP, so let the exception bubble up)
945
+	 */
946
+	protected function getEntityCalculations($model, $wpdb_row, $rest_request)
947
+	{
948
+		$calculated_fields = $this->explodeAndGetItemsPrefixedWith(
949
+			$rest_request->get_param('calculate'),
950
+			''
951
+		);
952
+		//note: setting calculate=* doesn't do anything
953
+		$calculated_fields_to_return = new \stdClass();
954
+		foreach ($calculated_fields as $field_to_calculate) {
955
+			try {
956
+				$calculated_fields_to_return->$field_to_calculate = ModelDataTranslator::prepareFieldValueForJson(
957
+					null,
958
+					$this->fields_calculator->retrieveCalculatedFieldValue(
959
+						$model,
960
+						$field_to_calculate,
961
+						$wpdb_row,
962
+						$rest_request,
963
+						$this
964
+					),
965
+					$this->getModelVersionInfo()->requestedVersion()
966
+				);
967
+			} catch (RestException $e) {
968
+				//if we don't have permission to read it, just leave it out. but let devs know about the problem
969
+				$this->setResponseHeader(
970
+					'Notices-Field-Calculation-Errors['
971
+					. $e->getStringCode()
972
+					. ']['
973
+					. $model->get_this_model_name()
974
+					. ']['
975
+					. $field_to_calculate
976
+					. ']',
977
+					$e->getMessage(),
978
+					true
979
+				);
980
+			}
981
+		}
982
+		return $calculated_fields_to_return;
983
+	}
984
+
985
+
986
+
987
+	/**
988
+	 * Gets the full URL to the resource, taking the requested version into account
989
+	 *
990
+	 * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
991
+	 * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
992
+	 */
993
+	public function getVersionedLinkTo($link_part_after_version_and_slash)
994
+	{
995
+		return rest_url(
996
+			EED_Core_Rest_Api::get_versioned_route_to(
997
+				$link_part_after_version_and_slash,
998
+				$this->getModelVersionInfo()->requestedVersion()
999
+			)
1000
+		);
1001
+	}
1002
+
1003
+
1004
+
1005
+	/**
1006
+	 * Gets the correct lowercase name for the relation in the API according
1007
+	 * to the relation's type
1008
+	 *
1009
+	 * @param string                  $relation_name
1010
+	 * @param \EE_Model_Relation_Base $relation_obj
1011
+	 * @return string
1012
+	 */
1013
+	public static function getRelatedEntityName($relation_name, $relation_obj)
1014
+	{
1015
+		if ($relation_obj instanceof EE_Belongs_To_Relation) {
1016
+			return strtolower($relation_name);
1017
+		} else {
1018
+			return EEH_Inflector::pluralize_and_lower($relation_name);
1019
+		}
1020
+	}
1021
+
1022
+
1023
+
1024
+	/**
1025
+	 * Gets the one model object with the specified id for the specified model
1026
+	 *
1027
+	 * @param EEM_Base        $model
1028
+	 * @param WP_REST_Request $request
1029
+	 * @return array|WP_Error
1030
+	 */
1031
+	public function getEntityFromModel($model, $request)
1032
+	{
1033
+		$context = $this->validateContext($request->get_param('caps'));
1034
+		return $this->getOneOrReportPermissionError($model, $request, $context);
1035
+	}
1036
+
1037
+
1038
+
1039
+	/**
1040
+	 * If a context is provided which isn't valid, maybe it was added in a future
1041
+	 * version so just treat it as a default read
1042
+	 *
1043
+	 * @param string $context
1044
+	 * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
1045
+	 */
1046
+	public function validateContext($context)
1047
+	{
1048
+		if (! $context) {
1049
+			$context = EEM_Base::caps_read;
1050
+		}
1051
+		$valid_contexts = EEM_Base::valid_cap_contexts();
1052
+		if (in_array($context, $valid_contexts)) {
1053
+			return $context;
1054
+		} else {
1055
+			return EEM_Base::caps_read;
1056
+		}
1057
+	}
1058
+
1059
+
1060
+
1061
+	/**
1062
+	 * Verifies the passed in value is an allowable default where conditions value.
1063
+	 *
1064
+	 * @param $default_query_params
1065
+	 * @return string
1066
+	 */
1067
+	public function validateDefaultQueryParams($default_query_params)
1068
+	{
1069
+		$valid_default_where_conditions_for_api_calls = array(
1070
+			EEM_Base::default_where_conditions_all,
1071
+			EEM_Base::default_where_conditions_minimum_all,
1072
+			EEM_Base::default_where_conditions_minimum_others,
1073
+		);
1074
+		if (! $default_query_params) {
1075
+			$default_query_params = EEM_Base::default_where_conditions_all;
1076
+		}
1077
+		if (in_array(
1078
+			$default_query_params,
1079
+			$valid_default_where_conditions_for_api_calls,
1080
+			true
1081
+		)) {
1082
+			return $default_query_params;
1083
+		} else {
1084
+			return EEM_Base::default_where_conditions_all;
1085
+		}
1086
+	}
1087
+
1088
+
1089
+
1090
+	/**
1091
+	 * Translates API filter get parameter into $query_params array used by EEM_Base::get_all().
1092
+	 * Note: right now the query parameter keys for fields (and related fields)
1093
+	 * can be left as-is, but it's quite possible this will change someday.
1094
+	 * Also, this method's contents might be candidate for moving to Model_Data_Translator
1095
+	 *
1096
+	 * @param EEM_Base $model
1097
+	 * @param array     $query_parameters from $_GET parameter @see Read:handle_request_get_all
1098
+	 * @return array like what EEM_Base::get_all() expects or FALSE to indicate
1099
+	 *                                    that absolutely no results should be returned
1100
+	 * @throws EE_Error
1101
+	 * @throws RestException
1102
+	 */
1103
+	public function createModelQueryParams($model, $query_parameters)
1104
+	{
1105
+		$model_query_params = array();
1106
+		if (isset($query_parameters['where'])) {
1107
+			$model_query_params[0] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1108
+				$query_parameters['where'],
1109
+				$model,
1110
+				$this->getModelVersionInfo()->requestedVersion()
1111
+			);
1112
+		}
1113
+		if (isset($query_parameters['order_by'])) {
1114
+			$order_by = $query_parameters['order_by'];
1115
+		} elseif (isset($query_parameters['orderby'])) {
1116
+			$order_by = $query_parameters['orderby'];
1117
+		} else {
1118
+			$order_by = null;
1119
+		}
1120
+		if ($order_by !== null) {
1121
+			if (is_array($order_by)) {
1122
+				$order_by = ModelDataTranslator::prepareFieldNamesInArrayKeysFromJson($order_by);
1123
+			} else {
1124
+				//it's a single item
1125
+				$order_by = ModelDataTranslator::prepareFieldNameFromJson($order_by);
1126
+			}
1127
+			$model_query_params['order_by'] = $order_by;
1128
+		}
1129
+		if (isset($query_parameters['group_by'])) {
1130
+			$group_by = $query_parameters['group_by'];
1131
+		} elseif (isset($query_parameters['groupby'])) {
1132
+			$group_by = $query_parameters['groupby'];
1133
+		} else {
1134
+			$group_by = array_keys($model->get_combined_primary_key_fields());
1135
+		}
1136
+		//make sure they're all real names
1137
+		if (is_array($group_by)) {
1138
+			$group_by = ModelDataTranslator::prepareFieldNamesFromJson($group_by);
1139
+		}
1140
+		if ($group_by !== null) {
1141
+			$model_query_params['group_by'] = $group_by;
1142
+		}
1143
+		if (isset($query_parameters['having'])) {
1144
+			$model_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1145
+				$query_parameters['having'],
1146
+				$model,
1147
+				$this->getModelVersionInfo()->requestedVersion()
1148
+			);
1149
+		}
1150
+		if (isset($query_parameters['order'])) {
1151
+			$model_query_params['order'] = $query_parameters['order'];
1152
+		}
1153
+		if (isset($query_parameters['mine'])) {
1154
+			$model_query_params = $model->alter_query_params_to_only_include_mine($model_query_params);
1155
+		}
1156
+		if (isset($query_parameters['limit'])) {
1157
+			//limit should be either a string like '23' or '23,43', or an array with two items in it
1158
+			if (! is_array($query_parameters['limit'])) {
1159
+				$limit_array = explode(',', (string)$query_parameters['limit']);
1160
+			} else {
1161
+				$limit_array = $query_parameters['limit'];
1162
+			}
1163
+			$sanitized_limit = array();
1164
+			foreach ($limit_array as $key => $limit_part) {
1165
+				if ($this->debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1166
+					throw new EE_Error(
1167
+						sprintf(
1168
+							__(
1169
+								// @codingStandardsIgnoreStart
1170
+								'An invalid limit filter was provided. It was: %s. If the EE4 JSON REST API weren\'t in debug mode, this message would not appear.',
1171
+								// @codingStandardsIgnoreEnd
1172
+								'event_espresso'
1173
+							),
1174
+							wp_json_encode($query_parameters['limit'])
1175
+						)
1176
+					);
1177
+				}
1178
+				$sanitized_limit[] = (int)$limit_part;
1179
+			}
1180
+			$model_query_params['limit'] = implode(',', $sanitized_limit);
1181
+		} else {
1182
+			$model_query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
1183
+		}
1184
+		if (isset($query_parameters['caps'])) {
1185
+			$model_query_params['caps'] = $this->validateContext($query_parameters['caps']);
1186
+		} else {
1187
+			$model_query_params['caps'] = EEM_Base::caps_read;
1188
+		}
1189
+		if (isset($query_parameters['default_where_conditions'])) {
1190
+			$model_query_params['default_where_conditions'] = $this->validateDefaultQueryParams(
1191
+				$query_parameters['default_where_conditions']
1192
+			);
1193
+		}
1194
+		return apply_filters('FHEE__Read__create_model_query_params', $model_query_params, $query_parameters, $model);
1195
+	}
1196
+
1197
+
1198
+
1199
+	/**
1200
+	 * Changes the REST-style query params for use in the models
1201
+	 *
1202
+	 * @deprecated
1203
+	 * @param EEM_Base $model
1204
+	 * @param array     $query_params sub-array from @see EEM_Base::get_all()
1205
+	 * @return array
1206
+	 */
1207
+	public function prepareRestQueryParamsKeyForModels($model, $query_params)
1208
+	{
1209
+		$model_ready_query_params = array();
1210
+		foreach ($query_params as $key => $value) {
1211
+			if (is_array($value)) {
1212
+				$model_ready_query_params[$key] = $this->prepareRestQueryParamsKeyForModels($model, $value);
1213
+			} else {
1214
+				$model_ready_query_params[$key] = $value;
1215
+			}
1216
+		}
1217
+		return $model_ready_query_params;
1218
+	}
1219
+
1220
+
1221
+
1222
+	/**
1223
+	 * @deprecated instead use ModelDataTranslator::prepareFieldValuesFromJson()
1224
+	 * @param $model
1225
+	 * @param $query_params
1226
+	 * @return array
1227
+	 */
1228
+	public function prepareRestQueryParamsValuesForModels($model, $query_params)
1229
+	{
1230
+		$model_ready_query_params = array();
1231
+		foreach ($query_params as $key => $value) {
1232
+			if (is_array($value)) {
1233
+				$model_ready_query_params[$key] = $this->prepareRestQueryParamsValuesForModels($model, $value);
1234
+			} else {
1235
+				$model_ready_query_params[$key] = $value;
1236
+			}
1237
+		}
1238
+		return $model_ready_query_params;
1239
+	}
1240
+
1241
+
1242
+
1243
+	/**
1244
+	 * Explodes the string on commas, and only returns items with $prefix followed by a period.
1245
+	 * If no prefix is specified, returns items with no period.
1246
+	 *
1247
+	 * @param string|array $string_to_explode eg "jibba,jabba, blah, blah, blah" or array('jibba', 'jabba' )
1248
+	 * @param string       $prefix            "Event" or "foobar"
1249
+	 * @return array $string_to_exploded exploded on COMMAS, and if a prefix was specified
1250
+	 *                                        we only return strings starting with that and a period; if no prefix was
1251
+	 *                                        specified we return all items containing NO periods
1252
+	 */
1253
+	public function explodeAndGetItemsPrefixedWith($string_to_explode, $prefix)
1254
+	{
1255
+		if (is_string($string_to_explode)) {
1256
+			$exploded_contents = explode(',', $string_to_explode);
1257
+		} elseif (is_array($string_to_explode)) {
1258
+			$exploded_contents = $string_to_explode;
1259
+		} else {
1260
+			$exploded_contents = array();
1261
+		}
1262
+		//if the string was empty, we want an empty array
1263
+		$exploded_contents = array_filter($exploded_contents);
1264
+		$contents_with_prefix = array();
1265
+		foreach ($exploded_contents as $item) {
1266
+			$item = trim($item);
1267
+			//if no prefix was provided, so we look for items with no "." in them
1268
+			if (! $prefix) {
1269
+				//does this item have a period?
1270
+				if (strpos($item, '.') === false) {
1271
+					//if not, then its what we're looking for
1272
+					$contents_with_prefix[] = $item;
1273
+				}
1274
+			} elseif (strpos($item, $prefix . '.') === 0) {
1275
+				//this item has the prefix and a period, grab it
1276
+				$contents_with_prefix[] = substr(
1277
+					$item,
1278
+					strpos($item, $prefix . '.') + strlen($prefix . '.')
1279
+				);
1280
+			} elseif ($item === $prefix) {
1281
+				//this item is JUST the prefix
1282
+				//so let's grab everything after, which is a blank string
1283
+				$contents_with_prefix[] = '';
1284
+			}
1285
+		}
1286
+		return $contents_with_prefix;
1287
+	}
1288
+
1289
+
1290
+
1291
+	/**
1292
+	 * @deprecated since 4.8.36.rc.001 You should instead use Read::explode_and_get_items_prefixed_with.
1293
+	 * Deprecated because its return values were really quite confusing- sometimes it returned
1294
+	 * an empty array (when the include string was blank or '*') or sometimes it returned
1295
+	 * array('*') (when you provided a model and a model of that kind was found).
1296
+	 * Parses the $include_string so we fetch all the field names relating to THIS model
1297
+	 * (ie have NO period in them), or for the provided model (ie start with the model
1298
+	 * name and then a period).
1299
+	 * @param string $include_string @see Read:handle_request_get_all
1300
+	 * @param string $model_name
1301
+	 * @return array of fields for this model. If $model_name is provided, then
1302
+	 *                               the fields for that model, with the model's name removed from each.
1303
+	 *                               If $include_string was blank or '*' returns an empty array
1304
+	 */
1305
+	public function extractIncludesForThisModel($include_string, $model_name = null)
1306
+	{
1307
+		if (is_array($include_string)) {
1308
+			$include_string = implode(',', $include_string);
1309
+		}
1310
+		if ($include_string === '*' || $include_string === '') {
1311
+			return array();
1312
+		}
1313
+		$includes = explode(',', $include_string);
1314
+		$extracted_fields_to_include = array();
1315
+		if ($model_name) {
1316
+			foreach ($includes as $field_to_include) {
1317
+				$field_to_include = trim($field_to_include);
1318
+				if (strpos($field_to_include, $model_name . '.') === 0) {
1319
+					//found the model name at the exact start
1320
+					$field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1321
+					$extracted_fields_to_include[] = $field_sans_model_name;
1322
+				} elseif ($field_to_include == $model_name) {
1323
+					$extracted_fields_to_include[] = '*';
1324
+				}
1325
+			}
1326
+		} else {
1327
+			//look for ones with no period
1328
+			foreach ($includes as $field_to_include) {
1329
+				$field_to_include = trim($field_to_include);
1330
+				if (strpos($field_to_include, '.') === false
1331
+					&& ! $this->getModelVersionInfo()->isModelNameInThisVersion($field_to_include)
1332
+				) {
1333
+					$extracted_fields_to_include[] = $field_to_include;
1334
+				}
1335
+			}
1336
+		}
1337
+		return $extracted_fields_to_include;
1338
+	}
1339
+
1340
+
1341
+
1342
+	/**
1343
+	 * Gets the single item using the model according to the request in the context given, otherwise
1344
+	 * returns that it's inaccessible to the current user
1345
+	 *
1352 1346
 *@param EEM_Base        $model
1353
-     * @param WP_REST_Request $request
1354
-     * @param null             $context
1355
-     * @return array|WP_Error
1356
-     */
1357
-    public function getOneOrReportPermissionError(EEM_Base $model, WP_REST_Request $request, $context = null)
1358
-    {
1359
-        $query_params = array(array($model->primary_key_name() => $request->get_param('id')), 'limit' => 1);
1360
-        if ($model instanceof \EEM_Soft_Delete_Base) {
1361
-            $query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
1362
-        }
1363
-        $restricted_query_params = $query_params;
1364
-        $restricted_query_params['caps'] = $context;
1365
-        $this->setDebugInfo('model query params', $restricted_query_params);
1366
-        $model_rows = $model->get_all_wpdb_results($restricted_query_params);
1367
-        if (! empty($model_rows)) {
1368
-            return $this->createEntityFromWpdbResult(
1369
-                $model,
1370
-                array_shift($model_rows),
1371
-                $request
1372
-            );
1373
-        } else {
1374
-            //ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
1375
-            $lowercase_model_name = strtolower($model->get_this_model_name());
1376
-            $model_rows_found_sans_restrictions = $model->get_all_wpdb_results($query_params);
1377
-            if (! empty($model_rows_found_sans_restrictions)) {
1378
-                //you got shafted- it existed but we didn't want to tell you!
1379
-                return new WP_Error(
1380
-                    'rest_user_cannot_' . $context,
1381
-                    sprintf(
1382
-                        __('Sorry, you cannot %1$s this %2$s. Missing permissions are: %3$s', 'event_espresso'),
1383
-                        $context,
1384
-                        strtolower($model->get_this_model_name()),
1385
-                        Capabilities::getMissingPermissionsString(
1386
-                            $model,
1387
-                            $context
1388
-                        )
1389
-                    ),
1390
-                    array('status' => 403)
1391
-                );
1392
-            } else {
1393
-                //it's not you. It just doesn't exist
1394
-                return new WP_Error(
1395
-                    sprintf('rest_%s_invalid_id', $lowercase_model_name),
1396
-                    sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
1397
-                    array('status' => 404)
1398
-                );
1399
-            }
1400
-        }
1401
-    }
1347
+	 * @param WP_REST_Request $request
1348
+	 * @param null             $context
1349
+	 * @return array|WP_Error
1350
+	 */
1351
+	public function getOneOrReportPermissionError(EEM_Base $model, WP_REST_Request $request, $context = null)
1352
+	{
1353
+		$query_params = array(array($model->primary_key_name() => $request->get_param('id')), 'limit' => 1);
1354
+		if ($model instanceof \EEM_Soft_Delete_Base) {
1355
+			$query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
1356
+		}
1357
+		$restricted_query_params = $query_params;
1358
+		$restricted_query_params['caps'] = $context;
1359
+		$this->setDebugInfo('model query params', $restricted_query_params);
1360
+		$model_rows = $model->get_all_wpdb_results($restricted_query_params);
1361
+		if (! empty($model_rows)) {
1362
+			return $this->createEntityFromWpdbResult(
1363
+				$model,
1364
+				array_shift($model_rows),
1365
+				$request
1366
+			);
1367
+		} else {
1368
+			//ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
1369
+			$lowercase_model_name = strtolower($model->get_this_model_name());
1370
+			$model_rows_found_sans_restrictions = $model->get_all_wpdb_results($query_params);
1371
+			if (! empty($model_rows_found_sans_restrictions)) {
1372
+				//you got shafted- it existed but we didn't want to tell you!
1373
+				return new WP_Error(
1374
+					'rest_user_cannot_' . $context,
1375
+					sprintf(
1376
+						__('Sorry, you cannot %1$s this %2$s. Missing permissions are: %3$s', 'event_espresso'),
1377
+						$context,
1378
+						strtolower($model->get_this_model_name()),
1379
+						Capabilities::getMissingPermissionsString(
1380
+							$model,
1381
+							$context
1382
+						)
1383
+					),
1384
+					array('status' => 403)
1385
+				);
1386
+			} else {
1387
+				//it's not you. It just doesn't exist
1388
+				return new WP_Error(
1389
+					sprintf('rest_%s_invalid_id', $lowercase_model_name),
1390
+					sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
1391
+					array('status' => 404)
1392
+				);
1393
+			}
1394
+		}
1395
+	}
1402 1396
 }
1403 1397
 
1404 1398
 
Please login to merge, or discard this patch.
Spacing   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -22,7 +22,7 @@  discard block
 block discarded – undo
22 22
 use EEM_Base;
23 23
 use EEM_CPT_Base;
24 24
 
25
-if (! defined('EVENT_ESPRESSO_VERSION')) {
25
+if ( ! defined('EVENT_ESPRESSO_VERSION')) {
26 26
     exit('No direct script access allowed');
27 27
 }
28 28
 
@@ -73,7 +73,7 @@  discard block
 block discarded – undo
73 73
         $controller = new Read();
74 74
         try {
75 75
             $controller->setRequestedVersion($version);
76
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
76
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
77 77
                 return $controller->sendResponse(
78 78
                     new WP_Error(
79 79
                         'endpoint_parsing_error',
@@ -112,7 +112,7 @@  discard block
 block discarded – undo
112 112
         $controller = new Read();
113 113
         try {
114 114
             $controller->setRequestedVersion($version);
115
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
115
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
116 116
                 return array();
117 117
             }
118 118
             //get the model for this version
@@ -209,9 +209,9 @@  discard block
 block discarded – undo
209 209
     protected function maybeAddExtraFieldsToSchema($field_name, EE_Model_Field_Base $field, array $schema)
210 210
     {
211 211
         if ($field instanceof EE_Datetime_Field) {
212
-            $schema['properties'][$field_name . '_gmt'] = $field->getSchema();
212
+            $schema['properties'][$field_name.'_gmt'] = $field->getSchema();
213 213
             //modify the description
214
-            $schema['properties'][$field_name . '_gmt']['description'] = sprintf(
214
+            $schema['properties'][$field_name.'_gmt']['description'] = sprintf(
215 215
                 esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
216 216
                 wp_specialchars_decode($field->get_nicename(), ENT_QUOTES)
217 217
             );
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
         $controller = new Read();
255 255
         try {
256 256
             $controller->setRequestedVersion($version);
257
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
257
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
258 258
                 return $controller->sendResponse(
259 259
                     new WP_Error(
260 260
                         'endpoint_parsing_error',
@@ -301,7 +301,7 @@  discard block
 block discarded – undo
301 301
         $controller = new Read();
302 302
         try {
303 303
             $controller->setRequestedVersion($version);
304
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
304
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
305 305
                 return $controller->sendResponse(
306 306
                     new WP_Error(
307 307
                         'endpoint_parsing_error',
@@ -316,7 +316,7 @@  discard block
 block discarded – undo
316 316
                 );
317 317
             }
318 318
             $main_model = $controller->getModelVersionInfo()->loadModel($model_name);
319
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($related_model_name)) {
319
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($related_model_name)) {
320 320
                 return $controller->sendResponse(
321 321
                     new WP_Error(
322 322
                         'endpoint_parsing_error',
@@ -355,7 +355,7 @@  discard block
 block discarded – undo
355 355
     public function getEntitiesFromModel($model, $request)
356 356
     {
357 357
         $query_params = $this->createModelQueryParams($model, $request->get_params());
358
-        if (! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
358
+        if ( ! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
359 359
             $model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
360 360
             return new WP_Error(
361 361
                 sprintf('rest_%s_cannot_list', $model_name_plural),
@@ -367,7 +367,7 @@  discard block
 block discarded – undo
367 367
                 array('status' => 403)
368 368
             );
369 369
         }
370
-        if (! $request->get_header('no_rest_headers')) {
370
+        if ( ! $request->get_header('no_rest_headers')) {
371 371
             $this->setHeadersFromQueryParams($model, $query_params);
372 372
         }
373 373
         /** @type array $results */
@@ -403,7 +403,7 @@  discard block
 block discarded – undo
403 403
         $context = $this->validateContext($request->get_param('caps'));
404 404
         $model = $relation->get_this_model();
405 405
         $related_model = $relation->get_other_model();
406
-        if (! isset($primary_model_query_params[0])) {
406
+        if ( ! isset($primary_model_query_params[0])) {
407 407
             $primary_model_query_params[0] = array();
408 408
         }
409 409
         //check if they can access the 1st model object
@@ -420,7 +420,7 @@  discard block
 block discarded – undo
420 420
         $restricted_query_params['caps'] = $context;
421 421
         $this->setDebugInfo('main model query params', $restricted_query_params);
422 422
         $this->setDebugInfo('missing caps', Capabilities::getMissingPermissionsString($related_model, $context));
423
-        if (! (
423
+        if ( ! (
424 424
             Capabilities::currentUserHasPartialAccessTo($related_model, $context)
425 425
             && $model->exists($restricted_query_params)
426 426
         )
@@ -459,7 +459,7 @@  discard block
 block discarded – undo
459 459
         }
460 460
         $query_params['default_where_conditions'] = 'none';
461 461
         $query_params['caps'] = $context;
462
-        if (! $request->get_header('no_rest_headers')) {
462
+        if ( ! $request->get_header('no_rest_headers')) {
463 463
             $this->setHeadersFromQueryParams($relation->get_other_model(), $query_params);
464 464
         }
465 465
         /** @type array $results */
@@ -512,7 +512,7 @@  discard block
 block discarded – undo
512 512
      */
513 513
     public function getEntitiesFromRelation($id, $relation, $request)
514 514
     {
515
-        if (! $relation->get_this_model()->has_primary_key_field()) {
515
+        if ( ! $relation->get_this_model()->has_primary_key_field()) {
516 516
             throw new EE_Error(
517 517
                 sprintf(
518 518
                     __(
@@ -555,7 +555,7 @@  discard block
 block discarded – undo
555 555
             Capabilities::getMissingPermissionsString($model, $query_params['caps'])
556 556
         );
557 557
         //normally the limit to a 2-part array, where the 2nd item is the limit
558
-        if (! isset($query_params['limit'])) {
558
+        if ( ! isset($query_params['limit'])) {
559 559
             $query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
560 560
         }
561 561
         if (is_array($query_params['limit'])) {
@@ -589,7 +589,7 @@  discard block
 block discarded – undo
589 589
      */
590 590
     public function createEntityFromWpdbResult($model, $db_row, $rest_request, $deprecated = null)
591 591
     {
592
-        if (! $rest_request instanceof WP_REST_Request) {
592
+        if ( ! $rest_request instanceof WP_REST_Request) {
593 593
             //ok so this was called in the old style, where the 3rd arg was
594 594
             //$include, and the 4th arg was $context
595 595
             //now setup the request just to avoid fatal errors, although we won't be able
@@ -673,7 +673,7 @@  discard block
 block discarded – undo
673 673
             global $post;
674 674
             $old_post = $post;
675 675
             $post = get_post($result[$model->primary_key_name()]);
676
-            if (! $post instanceof \WP_Post) {
676
+            if ( ! $post instanceof \WP_Post) {
677 677
                 //well that's weird, because $result is what we JUST fetched from the database
678 678
                 throw new RestException(
679 679
                     'error_fetching_post_from_database_results',
@@ -683,7 +683,7 @@  discard block
 block discarded – undo
683 683
                     )
684 684
                 );
685 685
             }
686
-            $model_object_classname = 'EE_' . $model->get_this_model_name();
686
+            $model_object_classname = 'EE_'.$model->get_this_model_name();
687 687
             $post->{$model_object_classname} = \EE_Registry::instance()->load_class(
688 688
                 $model_object_classname,
689 689
                 $result,
@@ -717,7 +717,7 @@  discard block
 block discarded – undo
717 717
                 $field_value = $field_obj->prepare_for_set_from_db($field_value);
718 718
                 $timezone = $field_value->getTimezone();
719 719
                 EEH_DTT_Helper::setTimezone($field_value, new DateTimeZone('UTC'));
720
-                $result[$field_name . '_gmt'] = ModelDataTranslator::prepareFieldValuesForJson(
720
+                $result[$field_name.'_gmt'] = ModelDataTranslator::prepareFieldValuesForJson(
721 721
                     $field_obj,
722 722
                     $field_value,
723 723
                     $this->getModelVersionInfo()->requestedVersion()
@@ -825,7 +825,7 @@  discard block
 block discarded – undo
825 825
         if ($model->has_primary_key_field()) {
826 826
             foreach ($this->getModelVersionInfo()->relationSettings($model) as $relation_name => $relation_obj) {
827 827
                 $related_model_part = Read::getRelatedEntityName($relation_name, $relation_obj);
828
-                $links[EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part] = array(
828
+                $links[EED_Core_Rest_Api::ee_api_link_namespace.$related_model_part] = array(
829 829
                     array(
830 830
                         'href'   => $this->getVersionedLinkTo(
831 831
                             EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
@@ -860,13 +860,13 @@  discard block
 block discarded – undo
860 860
         $db_row = array()
861 861
     ) {
862 862
         //if $db_row not included, hope the entity array has what we need
863
-        if (! $db_row) {
863
+        if ( ! $db_row) {
864 864
             $db_row = $entity_array;
865 865
         }
866 866
         $includes_for_this_model = $this->explodeAndGetItemsPrefixedWith($rest_request->get_param('include'), '');
867 867
         $includes_for_this_model = $this->removeModelNamesFromArray($includes_for_this_model);
868 868
         //if they passed in * or didn't specify any includes, return everything
869
-        if (! in_array('*', $includes_for_this_model)
869
+        if ( ! in_array('*', $includes_for_this_model)
870 870
             && ! empty($includes_for_this_model)
871 871
         ) {
872 872
             if ($model->has_primary_key_field()) {
@@ -1050,7 +1050,7 @@  discard block
 block discarded – undo
1050 1050
      */
1051 1051
     public function validateContext($context)
1052 1052
     {
1053
-        if (! $context) {
1053
+        if ( ! $context) {
1054 1054
             $context = EEM_Base::caps_read;
1055 1055
         }
1056 1056
         $valid_contexts = EEM_Base::valid_cap_contexts();
@@ -1076,7 +1076,7 @@  discard block
 block discarded – undo
1076 1076
             EEM_Base::default_where_conditions_minimum_all,
1077 1077
             EEM_Base::default_where_conditions_minimum_others,
1078 1078
         );
1079
-        if (! $default_query_params) {
1079
+        if ( ! $default_query_params) {
1080 1080
             $default_query_params = EEM_Base::default_where_conditions_all;
1081 1081
         }
1082 1082
         if (in_array(
@@ -1160,14 +1160,14 @@  discard block
 block discarded – undo
1160 1160
         }
1161 1161
         if (isset($query_parameters['limit'])) {
1162 1162
             //limit should be either a string like '23' or '23,43', or an array with two items in it
1163
-            if (! is_array($query_parameters['limit'])) {
1164
-                $limit_array = explode(',', (string)$query_parameters['limit']);
1163
+            if ( ! is_array($query_parameters['limit'])) {
1164
+                $limit_array = explode(',', (string) $query_parameters['limit']);
1165 1165
             } else {
1166 1166
                 $limit_array = $query_parameters['limit'];
1167 1167
             }
1168 1168
             $sanitized_limit = array();
1169 1169
             foreach ($limit_array as $key => $limit_part) {
1170
-                if ($this->debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1170
+                if ($this->debug_mode && ( ! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1171 1171
                     throw new EE_Error(
1172 1172
                         sprintf(
1173 1173
                             __(
@@ -1180,7 +1180,7 @@  discard block
 block discarded – undo
1180 1180
                         )
1181 1181
                     );
1182 1182
                 }
1183
-                $sanitized_limit[] = (int)$limit_part;
1183
+                $sanitized_limit[] = (int) $limit_part;
1184 1184
             }
1185 1185
             $model_query_params['limit'] = implode(',', $sanitized_limit);
1186 1186
         } else {
@@ -1270,17 +1270,17 @@  discard block
 block discarded – undo
1270 1270
         foreach ($exploded_contents as $item) {
1271 1271
             $item = trim($item);
1272 1272
             //if no prefix was provided, so we look for items with no "." in them
1273
-            if (! $prefix) {
1273
+            if ( ! $prefix) {
1274 1274
                 //does this item have a period?
1275 1275
                 if (strpos($item, '.') === false) {
1276 1276
                     //if not, then its what we're looking for
1277 1277
                     $contents_with_prefix[] = $item;
1278 1278
                 }
1279
-            } elseif (strpos($item, $prefix . '.') === 0) {
1279
+            } elseif (strpos($item, $prefix.'.') === 0) {
1280 1280
                 //this item has the prefix and a period, grab it
1281 1281
                 $contents_with_prefix[] = substr(
1282 1282
                     $item,
1283
-                    strpos($item, $prefix . '.') + strlen($prefix . '.')
1283
+                    strpos($item, $prefix.'.') + strlen($prefix.'.')
1284 1284
                 );
1285 1285
             } elseif ($item === $prefix) {
1286 1286
                 //this item is JUST the prefix
@@ -1320,9 +1320,9 @@  discard block
 block discarded – undo
1320 1320
         if ($model_name) {
1321 1321
             foreach ($includes as $field_to_include) {
1322 1322
                 $field_to_include = trim($field_to_include);
1323
-                if (strpos($field_to_include, $model_name . '.') === 0) {
1323
+                if (strpos($field_to_include, $model_name.'.') === 0) {
1324 1324
                     //found the model name at the exact start
1325
-                    $field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1325
+                    $field_sans_model_name = str_replace($model_name.'.', '', $field_to_include);
1326 1326
                     $extracted_fields_to_include[] = $field_sans_model_name;
1327 1327
                 } elseif ($field_to_include == $model_name) {
1328 1328
                     $extracted_fields_to_include[] = '*';
@@ -1364,7 +1364,7 @@  discard block
 block discarded – undo
1364 1364
         $restricted_query_params['caps'] = $context;
1365 1365
         $this->setDebugInfo('model query params', $restricted_query_params);
1366 1366
         $model_rows = $model->get_all_wpdb_results($restricted_query_params);
1367
-        if (! empty($model_rows)) {
1367
+        if ( ! empty($model_rows)) {
1368 1368
             return $this->createEntityFromWpdbResult(
1369 1369
                 $model,
1370 1370
                 array_shift($model_rows),
@@ -1374,10 +1374,10 @@  discard block
 block discarded – undo
1374 1374
             //ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
1375 1375
             $lowercase_model_name = strtolower($model->get_this_model_name());
1376 1376
             $model_rows_found_sans_restrictions = $model->get_all_wpdb_results($query_params);
1377
-            if (! empty($model_rows_found_sans_restrictions)) {
1377
+            if ( ! empty($model_rows_found_sans_restrictions)) {
1378 1378
                 //you got shafted- it existed but we didn't want to tell you!
1379 1379
                 return new WP_Error(
1380
-                    'rest_user_cannot_' . $context,
1380
+                    'rest_user_cannot_'.$context,
1381 1381
                     sprintf(
1382 1382
                         __('Sorry, you cannot %1$s this %2$s. Missing permissions are: %3$s', 'event_espresso'),
1383 1383
                         $context,
Please login to merge, or discard this patch.
core/db_classes/EE_Base_Class.class.php 2 patches
Indentation   +3141 added lines, -3141 removed lines patch added patch discarded remove patch
@@ -15,3147 +15,3147 @@
 block discarded – undo
15 15
 abstract class EE_Base_Class
16 16
 {
17 17
 
18
-    /**
19
-     * This is an array of the original properties and values provided during construction
20
-     * of this model object. (keys are model field names, values are their values).
21
-     * This list is important to remember so that when we are merging data from the db, we know
22
-     * which values to override and which to not override.
23
-     *
24
-     * @var array
25
-     */
26
-    protected $_props_n_values_provided_in_constructor;
27
-
28
-    /**
29
-     * Timezone
30
-     * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
31
-     * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
32
-     * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
33
-     * access to it.
34
-     *
35
-     * @var string
36
-     */
37
-    protected $_timezone;
38
-
39
-    /**
40
-     * date format
41
-     * pattern or format for displaying dates
42
-     *
43
-     * @var string $_dt_frmt
44
-     */
45
-    protected $_dt_frmt;
46
-
47
-    /**
48
-     * time format
49
-     * pattern or format for displaying time
50
-     *
51
-     * @var string $_tm_frmt
52
-     */
53
-    protected $_tm_frmt;
54
-
55
-    /**
56
-     * This property is for holding a cached array of object properties indexed by property name as the key.
57
-     * The purpose of this is for setting a cache on properties that may have calculated values after a
58
-     * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
59
-     * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
60
-     *
61
-     * @var array
62
-     */
63
-    protected $_cached_properties = array();
64
-
65
-    /**
66
-     * An array containing keys of the related model, and values are either an array of related mode objects or a
67
-     * single
68
-     * related model object. see the model's _model_relations. The keys should match those specified. And if the
69
-     * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
70
-     * all others have an array)
71
-     *
72
-     * @var array
73
-     */
74
-    protected $_model_relations = array();
75
-
76
-    /**
77
-     * Array where keys are field names (see the model's _fields property) and values are their values. To see what
78
-     * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
79
-     *
80
-     * @var array
81
-     */
82
-    protected $_fields = array();
83
-
84
-    /**
85
-     * @var boolean indicating whether or not this model object is intended to ever be saved
86
-     * For example, we might create model objects intended to only be used for the duration
87
-     * of this request and to be thrown away, and if they were accidentally saved
88
-     * it would be a bug.
89
-     */
90
-    protected $_allow_persist = true;
91
-
92
-    /**
93
-     * @var boolean indicating whether or not this model object's properties have changed since construction
94
-     */
95
-    protected $_has_changes = false;
96
-
97
-    /**
98
-     * @var EEM_Base
99
-     */
100
-    protected $_model;
101
-
102
-    /**
103
-     * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
104
-     * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
105
-     * the db.  They also do not automatically update if there are any changes to the data that produced their results.
106
-     * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
107
-     * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
108
-     * array as:
109
-     * array(
110
-     *  'Registration_Count' => 24
111
-     * );
112
-     * Note: if the custom select configuration for the query included a data type, the value will be in the data type
113
-     * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
114
-     * info)
115
-     *
116
-     * @var array
117
-     */
118
-    protected $custom_selection_results = array();
119
-
120
-
121
-    /**
122
-     * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
123
-     * play nice
124
-     *
125
-     * @param array   $fieldValues                             where each key is a field (ie, array key in the 2nd
126
-     *                                                         layer of the model's _fields array, (eg, EVT_ID,
127
-     *                                                         TXN_amount, QST_name, etc) and values are their values
128
-     * @param boolean $bydb                                    a flag for setting if the class is instantiated by the
129
-     *                                                         corresponding db model or not.
130
-     * @param string  $timezone                                indicate what timezone you want any datetime fields to
131
-     *                                                         be in when instantiating a EE_Base_Class object.
132
-     * @param array   $date_formats                            An array of date formats to set on construct where first
133
-     *                                                         value is the date_format and second value is the time
134
-     *                                                         format.
135
-     * @throws InvalidArgumentException
136
-     * @throws InvalidInterfaceException
137
-     * @throws InvalidDataTypeException
138
-     * @throws EE_Error
139
-     * @throws ReflectionException
140
-     */
141
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '', $date_formats = array())
142
-    {
143
-        $className = get_class($this);
144
-        do_action("AHEE__{$className}__construct", $this, $fieldValues);
145
-        $model        = $this->get_model();
146
-        $model_fields = $model->field_settings(false);
147
-        // ensure $fieldValues is an array
148
-        $fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
149
-        // verify client code has not passed any invalid field names
150
-        foreach ($fieldValues as $field_name => $field_value) {
151
-            if (! isset($model_fields[ $field_name ])) {
152
-                throw new EE_Error(
153
-                    sprintf(
154
-                        esc_html__(
155
-                            'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
156
-                            'event_espresso'
157
-                        ),
158
-                        $field_name,
159
-                        get_class($this),
160
-                        implode(', ', array_keys($model_fields))
161
-                    )
162
-                );
163
-            }
164
-        }
165
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
166
-        if (! empty($date_formats) && is_array($date_formats)) {
167
-            list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
168
-        } else {
169
-            //set default formats for date and time
170
-            $this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
171
-            $this->_tm_frmt = (string) get_option('time_format', 'g:i a');
172
-        }
173
-        //if db model is instantiating
174
-        if ($bydb) {
175
-            //client code has indicated these field values are from the database
176
-            foreach ($model_fields as $fieldName => $field) {
177
-                $this->set_from_db(
178
-                    $fieldName,
179
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
180
-                );
181
-            }
182
-        } else {
183
-            //we're constructing a brand
184
-            //new instance of the model object. Generally, this means we'll need to do more field validation
185
-            foreach ($model_fields as $fieldName => $field) {
186
-                $this->set(
187
-                    $fieldName,
188
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null, true
189
-                );
190
-            }
191
-        }
192
-        //remember what values were passed to this constructor
193
-        $this->_props_n_values_provided_in_constructor = $fieldValues;
194
-        //remember in entity mapper
195
-        if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
196
-            $model->add_to_entity_map($this);
197
-        }
198
-        //setup all the relations
199
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
200
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
201
-                $this->_model_relations[ $relation_name ] = null;
202
-            } else {
203
-                $this->_model_relations[ $relation_name ] = array();
204
-            }
205
-        }
206
-        /**
207
-         * Action done at the end of each model object construction
208
-         *
209
-         * @param EE_Base_Class $this the model object just created
210
-         */
211
-        do_action('AHEE__EE_Base_Class__construct__finished', $this);
212
-    }
213
-
214
-
215
-    /**
216
-     * Gets whether or not this model object is allowed to persist/be saved to the database.
217
-     *
218
-     * @return boolean
219
-     */
220
-    public function allow_persist()
221
-    {
222
-        return $this->_allow_persist;
223
-    }
224
-
225
-
226
-    /**
227
-     * Sets whether or not this model object should be allowed to be saved to the DB.
228
-     * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
229
-     * you got new information that somehow made you change your mind.
230
-     *
231
-     * @param boolean $allow_persist
232
-     * @return boolean
233
-     */
234
-    public function set_allow_persist($allow_persist)
235
-    {
236
-        return $this->_allow_persist = $allow_persist;
237
-    }
238
-
239
-
240
-    /**
241
-     * Gets the field's original value when this object was constructed during this request.
242
-     * This can be helpful when determining if a model object has changed or not
243
-     *
244
-     * @param string $field_name
245
-     * @return mixed|null
246
-     * @throws ReflectionException
247
-     * @throws InvalidArgumentException
248
-     * @throws InvalidInterfaceException
249
-     * @throws InvalidDataTypeException
250
-     * @throws EE_Error
251
-     */
252
-    public function get_original($field_name)
253
-    {
254
-        if (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
255
-            && $field_settings = $this->get_model()->field_settings_for($field_name)
256
-        ) {
257
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
258
-        }
259
-        return null;
260
-    }
261
-
262
-
263
-    /**
264
-     * @param EE_Base_Class $obj
265
-     * @return string
266
-     */
267
-    public function get_class($obj)
268
-    {
269
-        return get_class($obj);
270
-    }
271
-
272
-
273
-    /**
274
-     * Overrides parent because parent expects old models.
275
-     * This also doesn't do any validation, and won't work for serialized arrays
276
-     *
277
-     * @param    string $field_name
278
-     * @param    mixed  $field_value
279
-     * @param bool      $use_default
280
-     * @throws InvalidArgumentException
281
-     * @throws InvalidInterfaceException
282
-     * @throws InvalidDataTypeException
283
-     * @throws EE_Error
284
-     * @throws ReflectionException
285
-     * @throws ReflectionException
286
-     * @throws ReflectionException
287
-     */
288
-    public function set($field_name, $field_value, $use_default = false)
289
-    {
290
-        // if not using default and nothing has changed, and object has already been setup (has ID),
291
-        // then don't do anything
292
-        if (
293
-            ! $use_default
294
-            && $this->_fields[ $field_name ] === $field_value
295
-            && $this->ID()
296
-        ) {
297
-            return;
298
-        }
299
-        $model              = $this->get_model();
300
-        $this->_has_changes = true;
301
-        $field_obj          = $model->field_settings_for($field_name);
302
-        if ($field_obj instanceof EE_Model_Field_Base) {
303
-            //			if ( method_exists( $field_obj, 'set_timezone' )) {
304
-            if ($field_obj instanceof EE_Datetime_Field) {
305
-                $field_obj->set_timezone($this->_timezone);
306
-                $field_obj->set_date_format($this->_dt_frmt);
307
-                $field_obj->set_time_format($this->_tm_frmt);
308
-            }
309
-            $holder_of_value = $field_obj->prepare_for_set($field_value);
310
-            //should the value be null?
311
-            if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
313
-                /**
314
-                 * To save having to refactor all the models, if a default value is used for a
315
-                 * EE_Datetime_Field, and that value is not null nor is it a DateTime
316
-                 * object.  Then let's do a set again to ensure that it becomes a DateTime
317
-                 * object.
318
-                 *
319
-                 * @since 4.6.10+
320
-                 */
321
-                if (
322
-                    $field_obj instanceof EE_Datetime_Field
323
-                    && $this->_fields[ $field_name ] !== null
324
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
325
-                ) {
326
-                    empty($this->_fields[ $field_name ])
327
-                        ? $this->set($field_name, time())
328
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
329
-                }
330
-            } else {
331
-                $this->_fields[ $field_name ] = $holder_of_value;
332
-            }
333
-            //if we're not in the constructor...
334
-            //now check if what we set was a primary key
335
-            if (
336
-                //note: props_n_values_provided_in_constructor is only set at the END of the constructor
337
-                $this->_props_n_values_provided_in_constructor
338
-                && $field_value
339
-                && $field_name === $model->primary_key_name()
340
-            ) {
341
-                //if so, we want all this object's fields to be filled either with
342
-                //what we've explicitly set on this model
343
-                //or what we have in the db
344
-                // echo "setting primary key!";
345
-                $fields_on_model = self::_get_model(get_class($this))->field_settings();
346
-                $obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347
-                foreach ($fields_on_model as $field_obj) {
348
-                    if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
349
-                        && $field_obj->get_name() !== $field_name
350
-                    ) {
351
-                        $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
352
-                    }
353
-                }
354
-                //oh this model object has an ID? well make sure its in the entity mapper
355
-                $model->add_to_entity_map($this);
356
-            }
357
-            //let's unset any cache for this field_name from the $_cached_properties property.
358
-            $this->_clear_cached_property($field_name);
359
-        } else {
360
-            throw new EE_Error(
361
-                sprintf(
362
-                    esc_html__(
363
-                        'A valid EE_Model_Field_Base could not be found for the given field name: %s',
364
-                        'event_espresso'
365
-                    ),
366
-                    $field_name
367
-                )
368
-            );
369
-        }
370
-    }
371
-
372
-
373
-    /**
374
-     * Set custom select values for model.
375
-     *
376
-     * @param array $custom_select_values
377
-     */
378
-    public function setCustomSelectsValues(array $custom_select_values)
379
-    {
380
-        $this->custom_selection_results = $custom_select_values;
381
-    }
382
-
383
-
384
-    /**
385
-     * Returns the custom select value for the provided alias if its set.
386
-     * If not set, returns null.
387
-     *
388
-     * @param string $alias
389
-     * @return string|int|float|null
390
-     */
391
-    public function getCustomSelect($alias)
392
-    {
393
-        return isset($this->custom_selection_results[ $alias ])
394
-            ? $this->custom_selection_results[ $alias ]
395
-            : null;
396
-    }
397
-
398
-
399
-    /**
400
-     * This sets the field value on the db column if it exists for the given $column_name or
401
-     * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
402
-     *
403
-     * @see EE_message::get_column_value for related documentation on the necessity of this method.
404
-     * @param string $field_name  Must be the exact column name.
405
-     * @param mixed  $field_value The value to set.
406
-     * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
407
-     * @throws InvalidArgumentException
408
-     * @throws InvalidInterfaceException
409
-     * @throws InvalidDataTypeException
410
-     * @throws EE_Error
411
-     * @throws ReflectionException
412
-     */
413
-    public function set_field_or_extra_meta($field_name, $field_value)
414
-    {
415
-        if ($this->get_model()->has_field($field_name)) {
416
-            $this->set($field_name, $field_value);
417
-            return true;
418
-        }
419
-        //ensure this object is saved first so that extra meta can be properly related.
420
-        $this->save();
421
-        return $this->update_extra_meta($field_name, $field_value);
422
-    }
423
-
424
-
425
-    /**
426
-     * This retrieves the value of the db column set on this class or if that's not present
427
-     * it will attempt to retrieve from extra_meta if found.
428
-     * Example Usage:
429
-     * Via EE_Message child class:
430
-     * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
431
-     * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
432
-     * also have additional main fields specific to the messenger.  The system accommodates those extra
433
-     * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
434
-     * value for those extra fields dynamically via the EE_message object.
435
-     *
436
-     * @param  string $field_name expecting the fully qualified field name.
437
-     * @return mixed|null  value for the field if found.  null if not found.
438
-     * @throws ReflectionException
439
-     * @throws InvalidArgumentException
440
-     * @throws InvalidInterfaceException
441
-     * @throws InvalidDataTypeException
442
-     * @throws EE_Error
443
-     */
444
-    public function get_field_or_extra_meta($field_name)
445
-    {
446
-        if ($this->get_model()->has_field($field_name)) {
447
-            $column_value = $this->get($field_name);
448
-        } else {
449
-            //This isn't a column in the main table, let's see if it is in the extra meta.
450
-            $column_value = $this->get_extra_meta($field_name, true, null);
451
-        }
452
-        return $column_value;
453
-    }
454
-
455
-
456
-    /**
457
-     * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
458
-     * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
459
-     * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
460
-     * available to all child classes that may be using the EE_Datetime_Field for a field data type.
461
-     *
462
-     * @access public
463
-     * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
464
-     * @return void
465
-     * @throws InvalidArgumentException
466
-     * @throws InvalidInterfaceException
467
-     * @throws InvalidDataTypeException
468
-     * @throws EE_Error
469
-     * @throws ReflectionException
470
-     */
471
-    public function set_timezone($timezone = '')
472
-    {
473
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
474
-        //make sure we clear all cached properties because they won't be relevant now
475
-        $this->_clear_cached_properties();
476
-        //make sure we update field settings and the date for all EE_Datetime_Fields
477
-        $model_fields = $this->get_model()->field_settings(false);
478
-        foreach ($model_fields as $field_name => $field_obj) {
479
-            if ($field_obj instanceof EE_Datetime_Field) {
480
-                $field_obj->set_timezone($this->_timezone);
481
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
482
-                    EEH_DTT_Helper::setTimezone($this->_fields[$field_name], new DateTimeZone($this->_timezone));
483
-                }
484
-            }
485
-        }
486
-    }
487
-
488
-
489
-    /**
490
-     * This just returns whatever is set for the current timezone.
491
-     *
492
-     * @access public
493
-     * @return string timezone string
494
-     */
495
-    public function get_timezone()
496
-    {
497
-        return $this->_timezone;
498
-    }
499
-
500
-
501
-    /**
502
-     * This sets the internal date format to what is sent in to be used as the new default for the class
503
-     * internally instead of wp set date format options
504
-     *
505
-     * @since 4.6
506
-     * @param string $format should be a format recognizable by PHP date() functions.
507
-     */
508
-    public function set_date_format($format)
509
-    {
510
-        $this->_dt_frmt = $format;
511
-        //clear cached_properties because they won't be relevant now.
512
-        $this->_clear_cached_properties();
513
-    }
514
-
515
-
516
-    /**
517
-     * This sets the internal time format string to what is sent in to be used as the new default for the
518
-     * class internally instead of wp set time format options.
519
-     *
520
-     * @since 4.6
521
-     * @param string $format should be a format recognizable by PHP date() functions.
522
-     */
523
-    public function set_time_format($format)
524
-    {
525
-        $this->_tm_frmt = $format;
526
-        //clear cached_properties because they won't be relevant now.
527
-        $this->_clear_cached_properties();
528
-    }
529
-
530
-
531
-    /**
532
-     * This returns the current internal set format for the date and time formats.
533
-     *
534
-     * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
535
-     *                             where the first value is the date format and the second value is the time format.
536
-     * @return mixed string|array
537
-     */
538
-    public function get_format($full = true)
539
-    {
540
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
541
-    }
542
-
543
-
544
-    /**
545
-     * cache
546
-     * stores the passed model object on the current model object.
547
-     * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
548
-     *
549
-     * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
550
-     *                                       'Registration' associated with this model object
551
-     * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
552
-     *                                       that could be a payment or a registration)
553
-     * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
554
-     *                                       items which will be stored in an array on this object
555
-     * @throws ReflectionException
556
-     * @throws InvalidArgumentException
557
-     * @throws InvalidInterfaceException
558
-     * @throws InvalidDataTypeException
559
-     * @throws EE_Error
560
-     * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
561
-     *                                       related thing, no array)
562
-     */
563
-    public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
564
-    {
565
-        // its entirely possible that there IS no related object yet in which case there is nothing to cache.
566
-        if (! $object_to_cache instanceof EE_Base_Class) {
567
-            return false;
568
-        }
569
-        // also get "how" the object is related, or throw an error
570
-        if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
571
-            throw new EE_Error(
572
-                sprintf(
573
-                    esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
574
-                    $relationName,
575
-                    get_class($this)
576
-                )
577
-            );
578
-        }
579
-        // how many things are related ?
580
-        if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
581
-            // if it's a "belongs to" relationship, then there's only one related model object
582
-            // eg, if this is a registration, there's only 1 attendee for it
583
-            // so for these model objects just set it to be cached
584
-            $this->_model_relations[ $relationName ] = $object_to_cache;
585
-            $return                                  = true;
586
-        } else {
587
-            // otherwise, this is the "many" side of a one to many relationship,
588
-            // so we'll add the object to the array of related objects for that type.
589
-            // eg: if this is an event, there are many registrations for that event,
590
-            // so we cache the registrations in an array
591
-            if (! is_array($this->_model_relations[ $relationName ])) {
592
-                // if for some reason, the cached item is a model object,
593
-                // then stick that in the array, otherwise start with an empty array
594
-                $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
595
-                                                           instanceof
596
-                                                           EE_Base_Class
597
-                    ? array($this->_model_relations[ $relationName ]) : array();
598
-            }
599
-            // first check for a cache_id which is normally empty
600
-            if (! empty($cache_id)) {
601
-                // if the cache_id exists, then it means we are purposely trying to cache this
602
-                // with a known key that can then be used to retrieve the object later on
603
-                $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
604
-                $return                                               = $cache_id;
605
-            } elseif ($object_to_cache->ID()) {
606
-                // OR the cached object originally came from the db, so let's just use it's PK for an ID
607
-                $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
608
-                $return                                                            = $object_to_cache->ID();
609
-            } else {
610
-                // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
611
-                $this->_model_relations[ $relationName ][] = $object_to_cache;
612
-                // move the internal pointer to the end of the array
613
-                end($this->_model_relations[ $relationName ]);
614
-                // and grab the key so that we can return it
615
-                $return = key($this->_model_relations[ $relationName ]);
616
-            }
617
-        }
618
-        return $return;
619
-    }
620
-
621
-
622
-    /**
623
-     * For adding an item to the cached_properties property.
624
-     *
625
-     * @access protected
626
-     * @param string      $fieldname the property item the corresponding value is for.
627
-     * @param mixed       $value     The value we are caching.
628
-     * @param string|null $cache_type
629
-     * @return void
630
-     * @throws ReflectionException
631
-     * @throws InvalidArgumentException
632
-     * @throws InvalidInterfaceException
633
-     * @throws InvalidDataTypeException
634
-     * @throws EE_Error
635
-     */
636
-    protected function _set_cached_property($fieldname, $value, $cache_type = null)
637
-    {
638
-        //first make sure this property exists
639
-        $this->get_model()->field_settings_for($fieldname);
640
-        $cache_type                                            = empty($cache_type) ? 'standard' : $cache_type;
641
-        $this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
642
-    }
643
-
644
-
645
-    /**
646
-     * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
647
-     * This also SETS the cache if we return the actual property!
648
-     *
649
-     * @param string $fieldname        the name of the property we're trying to retrieve
650
-     * @param bool   $pretty
651
-     * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
652
-     *                                 (in cases where the same property may be used for different outputs
653
-     *                                 - i.e. datetime, money etc.)
654
-     *                                 It can also accept certain pre-defined "schema" strings
655
-     *                                 to define how to output the property.
656
-     *                                 see the field's prepare_for_pretty_echoing for what strings can be used
657
-     * @return mixed                   whatever the value for the property is we're retrieving
658
-     * @throws ReflectionException
659
-     * @throws InvalidArgumentException
660
-     * @throws InvalidInterfaceException
661
-     * @throws InvalidDataTypeException
662
-     * @throws EE_Error
663
-     */
664
-    protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
665
-    {
666
-        //verify the field exists
667
-        $model = $this->get_model();
668
-        $model->field_settings_for($fieldname);
669
-        $cache_type = $pretty ? 'pretty' : 'standard';
670
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
671
-        if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
672
-            return $this->_cached_properties[ $fieldname ][ $cache_type ];
673
-        }
674
-        $value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
675
-        $this->_set_cached_property($fieldname, $value, $cache_type);
676
-        return $value;
677
-    }
678
-
679
-
680
-    /**
681
-     * If the cache didn't fetch the needed item, this fetches it.
682
-     *
683
-     * @param string $fieldname
684
-     * @param bool   $pretty
685
-     * @param string $extra_cache_ref
686
-     * @return mixed
687
-     * @throws InvalidArgumentException
688
-     * @throws InvalidInterfaceException
689
-     * @throws InvalidDataTypeException
690
-     * @throws EE_Error
691
-     * @throws ReflectionException
692
-     */
693
-    protected function _get_fresh_property($fieldname, $pretty = false, $extra_cache_ref = null)
694
-    {
695
-        $field_obj = $this->get_model()->field_settings_for($fieldname);
696
-        // If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
697
-        if ($field_obj instanceof EE_Datetime_Field) {
698
-            $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
699
-        }
700
-        if (! isset($this->_fields[ $fieldname ])) {
701
-            $this->_fields[ $fieldname ] = null;
702
-        }
703
-        $value = $pretty
704
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
705
-            : $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
706
-        return $value;
707
-    }
708
-
709
-
710
-    /**
711
-     * set timezone, formats, and output for EE_Datetime_Field objects
712
-     *
713
-     * @param \EE_Datetime_Field $datetime_field
714
-     * @param bool               $pretty
715
-     * @param null               $date_or_time
716
-     * @return void
717
-     * @throws InvalidArgumentException
718
-     * @throws InvalidInterfaceException
719
-     * @throws InvalidDataTypeException
720
-     * @throws EE_Error
721
-     */
722
-    protected function _prepare_datetime_field(
723
-        EE_Datetime_Field $datetime_field,
724
-        $pretty = false,
725
-        $date_or_time = null
726
-    ) {
727
-        $datetime_field->set_timezone($this->_timezone);
728
-        $datetime_field->set_date_format($this->_dt_frmt, $pretty);
729
-        $datetime_field->set_time_format($this->_tm_frmt, $pretty);
730
-        //set the output returned
731
-        switch ($date_or_time) {
732
-            case 'D' :
733
-                $datetime_field->set_date_time_output('date');
734
-                break;
735
-            case 'T' :
736
-                $datetime_field->set_date_time_output('time');
737
-                break;
738
-            default :
739
-                $datetime_field->set_date_time_output();
740
-        }
741
-    }
742
-
743
-
744
-    /**
745
-     * This just takes care of clearing out the cached_properties
746
-     *
747
-     * @return void
748
-     */
749
-    protected function _clear_cached_properties()
750
-    {
751
-        $this->_cached_properties = array();
752
-    }
753
-
754
-
755
-    /**
756
-     * This just clears out ONE property if it exists in the cache
757
-     *
758
-     * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
759
-     * @return void
760
-     */
761
-    protected function _clear_cached_property($property_name)
762
-    {
763
-        if (isset($this->_cached_properties[ $property_name ])) {
764
-            unset($this->_cached_properties[ $property_name ]);
765
-        }
766
-    }
767
-
768
-
769
-    /**
770
-     * Ensures that this related thing is a model object.
771
-     *
772
-     * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
773
-     * @param string $model_name   name of the related thing, eg 'Attendee',
774
-     * @return EE_Base_Class
775
-     * @throws ReflectionException
776
-     * @throws InvalidArgumentException
777
-     * @throws InvalidInterfaceException
778
-     * @throws InvalidDataTypeException
779
-     * @throws EE_Error
780
-     */
781
-    protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
782
-    {
783
-        $other_model_instance = self::_get_model_instance_with_name(
784
-            self::_get_model_classname($model_name),
785
-            $this->_timezone
786
-        );
787
-        return $other_model_instance->ensure_is_obj($object_or_id);
788
-    }
789
-
790
-
791
-    /**
792
-     * Forgets the cached model of the given relation Name. So the next time we request it,
793
-     * we will fetch it again from the database. (Handy if you know it's changed somehow).
794
-     * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
795
-     * then only remove that one object from our cached array. Otherwise, clear the entire list
796
-     *
797
-     * @param string $relationName                         one of the keys in the _model_relations array on the model.
798
-     *                                                     Eg 'Registration'
799
-     * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
800
-     *                                                     if you intend to use $clear_all = TRUE, or the relation only
801
-     *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
802
-     * @param bool   $clear_all                            This flags clearing the entire cache relation property if
803
-     *                                                     this is HasMany or HABTM.
804
-     * @throws ReflectionException
805
-     * @throws InvalidArgumentException
806
-     * @throws InvalidInterfaceException
807
-     * @throws InvalidDataTypeException
808
-     * @throws EE_Error
809
-     * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
810
-     *                                                     relation from all
811
-     */
812
-    public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
813
-    {
814
-        $relationship_to_model = $this->get_model()->related_settings_for($relationName);
815
-        $index_in_cache        = '';
816
-        if (! $relationship_to_model) {
817
-            throw new EE_Error(
818
-                sprintf(
819
-                    esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
820
-                    $relationName,
821
-                    get_class($this)
822
-                )
823
-            );
824
-        }
825
-        if ($clear_all) {
826
-            $obj_removed                             = true;
827
-            $this->_model_relations[ $relationName ] = null;
828
-        } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
829
-            $obj_removed                             = $this->_model_relations[ $relationName ];
830
-            $this->_model_relations[ $relationName ] = null;
831
-        } else {
832
-            if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
833
-                && $object_to_remove_or_index_into_array->ID()
834
-            ) {
835
-                $index_in_cache = $object_to_remove_or_index_into_array->ID();
836
-                if (is_array($this->_model_relations[ $relationName ])
837
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
838
-                ) {
839
-                    $index_found_at = null;
840
-                    //find this object in the array even though it has a different key
841
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
842
-                        /** @noinspection TypeUnsafeComparisonInspection */
843
-                        if (
844
-                            $obj instanceof EE_Base_Class
845
-                            && (
846
-                                $obj == $object_to_remove_or_index_into_array
847
-                                || $obj->ID() === $object_to_remove_or_index_into_array->ID()
848
-                            )
849
-                        ) {
850
-                            $index_found_at = $index;
851
-                            break;
852
-                        }
853
-                    }
854
-                    if ($index_found_at) {
855
-                        $index_in_cache = $index_found_at;
856
-                    } else {
857
-                        //it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
858
-                        //if it wasn't in it to begin with. So we're done
859
-                        return $object_to_remove_or_index_into_array;
860
-                    }
861
-                }
862
-            } elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
863
-                //so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
864
-                foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
865
-                    /** @noinspection TypeUnsafeComparisonInspection */
866
-                    if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
867
-                        $index_in_cache = $index;
868
-                    }
869
-                }
870
-            } else {
871
-                $index_in_cache = $object_to_remove_or_index_into_array;
872
-            }
873
-            //supposedly we've found it. But it could just be that the client code
874
-            //provided a bad index/object
875
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
876
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
877
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
878
-            } else {
879
-                //that thing was never cached anyways.
880
-                $obj_removed = null;
881
-            }
882
-        }
883
-        return $obj_removed;
884
-    }
885
-
886
-
887
-    /**
888
-     * update_cache_after_object_save
889
-     * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
890
-     * obtained after being saved to the db
891
-     *
892
-     * @param string        $relationName       - the type of object that is cached
893
-     * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
894
-     * @param string        $current_cache_id   - the ID that was used when originally caching the object
895
-     * @return boolean TRUE on success, FALSE on fail
896
-     * @throws ReflectionException
897
-     * @throws InvalidArgumentException
898
-     * @throws InvalidInterfaceException
899
-     * @throws InvalidDataTypeException
900
-     * @throws EE_Error
901
-     */
902
-    public function update_cache_after_object_save(
903
-        $relationName,
904
-        EE_Base_Class $newly_saved_object,
905
-        $current_cache_id = ''
906
-    ) {
907
-        // verify that incoming object is of the correct type
908
-        $obj_class = 'EE_' . $relationName;
909
-        if ($newly_saved_object instanceof $obj_class) {
910
-            /* @type EE_Base_Class $newly_saved_object */
911
-            // now get the type of relation
912
-            $relationship_to_model = $this->get_model()->related_settings_for($relationName);
913
-            // if this is a 1:1 relationship
914
-            if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
915
-                // then just replace the cached object with the newly saved object
916
-                $this->_model_relations[ $relationName ] = $newly_saved_object;
917
-                return true;
918
-                // or if it's some kind of sordid feral polyamorous relationship...
919
-            }
920
-            if (is_array($this->_model_relations[ $relationName ])
921
-                      && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
922
-            ) {
923
-                // then remove the current cached item
924
-                unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
925
-                // and cache the newly saved object using it's new ID
926
-                $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
927
-                return true;
928
-            }
929
-        }
930
-        return false;
931
-    }
932
-
933
-
934
-    /**
935
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
936
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
937
-     *
938
-     * @param string $relationName
939
-     * @return EE_Base_Class
940
-     */
941
-    public function get_one_from_cache($relationName)
942
-    {
943
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
944
-            ? $this->_model_relations[ $relationName ]
945
-            : null;
946
-        if (is_array($cached_array_or_object)) {
947
-            return array_shift($cached_array_or_object);
948
-        }
949
-        return $cached_array_or_object;
950
-    }
951
-
952
-
953
-    /**
954
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
955
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
956
-     *
957
-     * @param string $relationName
958
-     * @throws ReflectionException
959
-     * @throws InvalidArgumentException
960
-     * @throws InvalidInterfaceException
961
-     * @throws InvalidDataTypeException
962
-     * @throws EE_Error
963
-     * @return EE_Base_Class[] NOT necessarily indexed by primary keys
964
-     */
965
-    public function get_all_from_cache($relationName)
966
-    {
967
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
968
-        // if the result is not an array, but exists, make it an array
969
-        $objects = is_array($objects) ? $objects : array($objects);
970
-        //bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
971
-        //basically, if this model object was stored in the session, and these cached model objects
972
-        //already have IDs, let's make sure they're in their model's entity mapper
973
-        //otherwise we will have duplicates next time we call
974
-        // EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
975
-        $model = EE_Registry::instance()->load_model($relationName);
976
-        foreach ($objects as $model_object) {
977
-            if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
978
-                //ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
979
-                if ($model_object->ID()) {
980
-                    $model->add_to_entity_map($model_object);
981
-                }
982
-            } else {
983
-                throw new EE_Error(
984
-                    sprintf(
985
-                        esc_html__(
986
-                            'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
987
-                            'event_espresso'
988
-                        ),
989
-                        $relationName,
990
-                        gettype($model_object)
991
-                    )
992
-                );
993
-            }
994
-        }
995
-        return $objects;
996
-    }
997
-
998
-
999
-    /**
1000
-     * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1001
-     * matching the given query conditions.
1002
-     *
1003
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1004
-     * @param int   $limit              How many objects to return.
1005
-     * @param array $query_params       Any additional conditions on the query.
1006
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1007
-     *                                  you can indicate just the columns you want returned
1008
-     * @return array|EE_Base_Class[]
1009
-     * @throws ReflectionException
1010
-     * @throws InvalidArgumentException
1011
-     * @throws InvalidInterfaceException
1012
-     * @throws InvalidDataTypeException
1013
-     * @throws EE_Error
1014
-     */
1015
-    public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
1016
-    {
1017
-        $model         = $this->get_model();
1018
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1019
-            ? $model->get_primary_key_field()->get_name()
1020
-            : $field_to_order_by;
1021
-        $current_value = ! empty($field) ? $this->get($field) : null;
1022
-        if (empty($field) || empty($current_value)) {
1023
-            return array();
1024
-        }
1025
-        return $model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1026
-    }
1027
-
1028
-
1029
-    /**
1030
-     * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1031
-     * matching the given query conditions.
1032
-     *
1033
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1034
-     * @param int   $limit              How many objects to return.
1035
-     * @param array $query_params       Any additional conditions on the query.
1036
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1037
-     *                                  you can indicate just the columns you want returned
1038
-     * @return array|EE_Base_Class[]
1039
-     * @throws ReflectionException
1040
-     * @throws InvalidArgumentException
1041
-     * @throws InvalidInterfaceException
1042
-     * @throws InvalidDataTypeException
1043
-     * @throws EE_Error
1044
-     */
1045
-    public function previous_x(
1046
-        $field_to_order_by = null,
1047
-        $limit = 1,
1048
-        $query_params = array(),
1049
-        $columns_to_select = null
1050
-    ) {
1051
-        $model         = $this->get_model();
1052
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1053
-            ? $model->get_primary_key_field()->get_name()
1054
-            : $field_to_order_by;
1055
-        $current_value = ! empty($field) ? $this->get($field) : null;
1056
-        if (empty($field) || empty($current_value)) {
1057
-            return array();
1058
-        }
1059
-        return $model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1060
-    }
1061
-
1062
-
1063
-    /**
1064
-     * Returns the next EE_Base_Class object in sequence from this object as found in the database
1065
-     * matching the given query conditions.
1066
-     *
1067
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1068
-     * @param array $query_params       Any additional conditions on the query.
1069
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1070
-     *                                  you can indicate just the columns you want returned
1071
-     * @return array|EE_Base_Class
1072
-     * @throws ReflectionException
1073
-     * @throws InvalidArgumentException
1074
-     * @throws InvalidInterfaceException
1075
-     * @throws InvalidDataTypeException
1076
-     * @throws EE_Error
1077
-     */
1078
-    public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1079
-    {
1080
-        $model         = $this->get_model();
1081
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1082
-            ? $model->get_primary_key_field()->get_name()
1083
-            : $field_to_order_by;
1084
-        $current_value = ! empty($field) ? $this->get($field) : null;
1085
-        if (empty($field) || empty($current_value)) {
1086
-            return array();
1087
-        }
1088
-        return $model->next($current_value, $field, $query_params, $columns_to_select);
1089
-    }
1090
-
1091
-
1092
-    /**
1093
-     * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1094
-     * matching the given query conditions.
1095
-     *
1096
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1097
-     * @param array $query_params       Any additional conditions on the query.
1098
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1099
-     *                                  you can indicate just the column you want returned
1100
-     * @return array|EE_Base_Class
1101
-     * @throws ReflectionException
1102
-     * @throws InvalidArgumentException
1103
-     * @throws InvalidInterfaceException
1104
-     * @throws InvalidDataTypeException
1105
-     * @throws EE_Error
1106
-     */
1107
-    public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1108
-    {
1109
-        $model         = $this->get_model();
1110
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1111
-            ? $model->get_primary_key_field()->get_name()
1112
-            : $field_to_order_by;
1113
-        $current_value = ! empty($field) ? $this->get($field) : null;
1114
-        if (empty($field) || empty($current_value)) {
1115
-            return array();
1116
-        }
1117
-        return $model->previous($current_value, $field, $query_params, $columns_to_select);
1118
-    }
1119
-
1120
-
1121
-    /**
1122
-     * Overrides parent because parent expects old models.
1123
-     * This also doesn't do any validation, and won't work for serialized arrays
1124
-     *
1125
-     * @param string $field_name
1126
-     * @param mixed  $field_value_from_db
1127
-     * @throws ReflectionException
1128
-     * @throws InvalidArgumentException
1129
-     * @throws InvalidInterfaceException
1130
-     * @throws InvalidDataTypeException
1131
-     * @throws EE_Error
1132
-     */
1133
-    public function set_from_db($field_name, $field_value_from_db)
1134
-    {
1135
-        $field_obj = $this->get_model()->field_settings_for($field_name);
1136
-        if ($field_obj instanceof EE_Model_Field_Base) {
1137
-            //you would think the DB has no NULLs for non-null label fields right? wrong!
1138
-            //eg, a CPT model object could have an entry in the posts table, but no
1139
-            //entry in the meta table. Meaning that all its columns in the meta table
1140
-            //are null! yikes! so when we find one like that, use defaults for its meta columns
1141
-            if ($field_value_from_db === null) {
1142
-                if ($field_obj->is_nullable()) {
1143
-                    //if the field allows nulls, then let it be null
1144
-                    $field_value = null;
1145
-                } else {
1146
-                    $field_value = $field_obj->get_default_value();
1147
-                }
1148
-            } else {
1149
-                $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1150
-            }
1151
-            $this->_fields[ $field_name ] = $field_value;
1152
-            $this->_clear_cached_property($field_name);
1153
-        }
1154
-    }
1155
-
1156
-
1157
-    /**
1158
-     * verifies that the specified field is of the correct type
1159
-     *
1160
-     * @param string $field_name
1161
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1162
-     *                                (in cases where the same property may be used for different outputs
1163
-     *                                - i.e. datetime, money etc.)
1164
-     * @return mixed
1165
-     * @throws ReflectionException
1166
-     * @throws InvalidArgumentException
1167
-     * @throws InvalidInterfaceException
1168
-     * @throws InvalidDataTypeException
1169
-     * @throws EE_Error
1170
-     */
1171
-    public function get($field_name, $extra_cache_ref = null)
1172
-    {
1173
-        return $this->_get_cached_property($field_name, false, $extra_cache_ref);
1174
-    }
1175
-
1176
-
1177
-    /**
1178
-     * This method simply returns the RAW unprocessed value for the given property in this class
1179
-     *
1180
-     * @param  string $field_name A valid fieldname
1181
-     * @return mixed              Whatever the raw value stored on the property is.
1182
-     * @throws ReflectionException
1183
-     * @throws InvalidArgumentException
1184
-     * @throws InvalidInterfaceException
1185
-     * @throws InvalidDataTypeException
1186
-     * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1187
-     */
1188
-    public function get_raw($field_name)
1189
-    {
1190
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1191
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1192
-            ? $this->_fields[ $field_name ]->format('U')
1193
-            : $this->_fields[ $field_name ];
1194
-    }
1195
-
1196
-
1197
-    /**
1198
-     * This is used to return the internal DateTime object used for a field that is a
1199
-     * EE_Datetime_Field.
1200
-     *
1201
-     * @param string $field_name               The field name retrieving the DateTime object.
1202
-     * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1203
-     * @throws EE_Error an error is set and false returned.  If the field IS an
1204
-     *                                         EE_Datetime_Field and but the field value is null, then
1205
-     *                                         just null is returned (because that indicates that likely
1206
-     *                                         this field is nullable).
1207
-     * @throws InvalidArgumentException
1208
-     * @throws InvalidDataTypeException
1209
-     * @throws InvalidInterfaceException
1210
-     * @throws ReflectionException
1211
-     */
1212
-    public function get_DateTime_object($field_name)
1213
-    {
1214
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1215
-        if (! $field_settings instanceof EE_Datetime_Field) {
1216
-            EE_Error::add_error(
1217
-                sprintf(
1218
-                    esc_html__(
1219
-                        'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1220
-                        'event_espresso'
1221
-                    ),
1222
-                    $field_name
1223
-                ),
1224
-                __FILE__,
1225
-                __FUNCTION__,
1226
-                __LINE__
1227
-            );
1228
-            return false;
1229
-        }
1230
-        return isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime
1231
-            ? clone $this->_fields[$field_name]
1232
-            : null;
1233
-    }
1234
-
1235
-
1236
-    /**
1237
-     * To be used in template to immediately echo out the value, and format it for output.
1238
-     * Eg, should call stripslashes and whatnot before echoing
1239
-     *
1240
-     * @param string $field_name      the name of the field as it appears in the DB
1241
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1242
-     *                                (in cases where the same property may be used for different outputs
1243
-     *                                - i.e. datetime, money etc.)
1244
-     * @return void
1245
-     * @throws ReflectionException
1246
-     * @throws InvalidArgumentException
1247
-     * @throws InvalidInterfaceException
1248
-     * @throws InvalidDataTypeException
1249
-     * @throws EE_Error
1250
-     */
1251
-    public function e($field_name, $extra_cache_ref = null)
1252
-    {
1253
-        echo $this->get_pretty($field_name, $extra_cache_ref);
1254
-    }
1255
-
1256
-
1257
-    /**
1258
-     * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1259
-     * can be easily used as the value of form input.
1260
-     *
1261
-     * @param string $field_name
1262
-     * @return void
1263
-     * @throws ReflectionException
1264
-     * @throws InvalidArgumentException
1265
-     * @throws InvalidInterfaceException
1266
-     * @throws InvalidDataTypeException
1267
-     * @throws EE_Error
1268
-     */
1269
-    public function f($field_name)
1270
-    {
1271
-        $this->e($field_name, 'form_input');
1272
-    }
1273
-
1274
-
1275
-    /**
1276
-     * Same as `f()` but just returns the value instead of echoing it
1277
-     *
1278
-     * @param string $field_name
1279
-     * @return string
1280
-     * @throws ReflectionException
1281
-     * @throws InvalidArgumentException
1282
-     * @throws InvalidInterfaceException
1283
-     * @throws InvalidDataTypeException
1284
-     * @throws EE_Error
1285
-     */
1286
-    public function get_f($field_name)
1287
-    {
1288
-        return (string) $this->get_pretty($field_name, 'form_input');
1289
-    }
1290
-
1291
-
1292
-    /**
1293
-     * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1294
-     * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1295
-     * to see what options are available.
1296
-     *
1297
-     * @param string $field_name
1298
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1299
-     *                                (in cases where the same property may be used for different outputs
1300
-     *                                - i.e. datetime, money etc.)
1301
-     * @return mixed
1302
-     * @throws ReflectionException
1303
-     * @throws InvalidArgumentException
1304
-     * @throws InvalidInterfaceException
1305
-     * @throws InvalidDataTypeException
1306
-     * @throws EE_Error
1307
-     */
1308
-    public function get_pretty($field_name, $extra_cache_ref = null)
1309
-    {
1310
-        return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1311
-    }
1312
-
1313
-
1314
-    /**
1315
-     * This simply returns the datetime for the given field name
1316
-     * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1317
-     * (and the equivalent e_date, e_time, e_datetime).
1318
-     *
1319
-     * @access   protected
1320
-     * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1321
-     * @param string   $dt_frmt      valid datetime format used for date
1322
-     *                               (if '' then we just use the default on the field,
1323
-     *                               if NULL we use the last-used format)
1324
-     * @param string   $tm_frmt      Same as above except this is for time format
1325
-     * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1326
-     * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1327
-     * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1328
-     *                               if field is not a valid dtt field, or void if echoing
1329
-     * @throws ReflectionException
1330
-     * @throws InvalidArgumentException
1331
-     * @throws InvalidInterfaceException
1332
-     * @throws InvalidDataTypeException
1333
-     * @throws EE_Error
1334
-     */
1335
-    protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1336
-    {
1337
-        // clear cached property
1338
-        $this->_clear_cached_property($field_name);
1339
-        //reset format properties because they are used in get()
1340
-        $this->_dt_frmt = $dt_frmt !== '' ? $dt_frmt : $this->_dt_frmt;
1341
-        $this->_tm_frmt = $tm_frmt !== '' ? $tm_frmt : $this->_tm_frmt;
1342
-        if ($echo) {
1343
-            $this->e($field_name, $date_or_time);
1344
-            return '';
1345
-        }
1346
-        return $this->get($field_name, $date_or_time);
1347
-    }
1348
-
1349
-
1350
-    /**
1351
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1352
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1353
-     * other echoes the pretty value for dtt)
1354
-     *
1355
-     * @param  string $field_name name of model object datetime field holding the value
1356
-     * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1357
-     * @return string            datetime value formatted
1358
-     * @throws ReflectionException
1359
-     * @throws InvalidArgumentException
1360
-     * @throws InvalidInterfaceException
1361
-     * @throws InvalidDataTypeException
1362
-     * @throws EE_Error
1363
-     */
1364
-    public function get_date($field_name, $format = '')
1365
-    {
1366
-        return $this->_get_datetime($field_name, $format, null, 'D');
1367
-    }
1368
-
1369
-
1370
-    /**
1371
-     * @param        $field_name
1372
-     * @param string $format
1373
-     * @throws ReflectionException
1374
-     * @throws InvalidArgumentException
1375
-     * @throws InvalidInterfaceException
1376
-     * @throws InvalidDataTypeException
1377
-     * @throws EE_Error
1378
-     */
1379
-    public function e_date($field_name, $format = '')
1380
-    {
1381
-        $this->_get_datetime($field_name, $format, null, 'D', true);
1382
-    }
1383
-
1384
-
1385
-    /**
1386
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1387
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1388
-     * other echoes the pretty value for dtt)
1389
-     *
1390
-     * @param  string $field_name name of model object datetime field holding the value
1391
-     * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1392
-     * @return string             datetime value formatted
1393
-     * @throws ReflectionException
1394
-     * @throws InvalidArgumentException
1395
-     * @throws InvalidInterfaceException
1396
-     * @throws InvalidDataTypeException
1397
-     * @throws EE_Error
1398
-     */
1399
-    public function get_time($field_name, $format = '')
1400
-    {
1401
-        return $this->_get_datetime($field_name, null, $format, 'T');
1402
-    }
1403
-
1404
-
1405
-    /**
1406
-     * @param        $field_name
1407
-     * @param string $format
1408
-     * @throws ReflectionException
1409
-     * @throws InvalidArgumentException
1410
-     * @throws InvalidInterfaceException
1411
-     * @throws InvalidDataTypeException
1412
-     * @throws EE_Error
1413
-     */
1414
-    public function e_time($field_name, $format = '')
1415
-    {
1416
-        $this->_get_datetime($field_name, null, $format, 'T', true);
1417
-    }
1418
-
1419
-
1420
-    /**
1421
-     * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1422
-     * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1423
-     * other echoes the pretty value for dtt)
1424
-     *
1425
-     * @param  string $field_name name of model object datetime field holding the value
1426
-     * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1427
-     * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1428
-     * @return string             datetime value formatted
1429
-     * @throws ReflectionException
1430
-     * @throws InvalidArgumentException
1431
-     * @throws InvalidInterfaceException
1432
-     * @throws InvalidDataTypeException
1433
-     * @throws EE_Error
1434
-     */
1435
-    public function get_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1436
-    {
1437
-        return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1438
-    }
1439
-
1440
-
1441
-    /**
1442
-     * @param string $field_name
1443
-     * @param string $dt_frmt
1444
-     * @param string $tm_frmt
1445
-     * @throws ReflectionException
1446
-     * @throws InvalidArgumentException
1447
-     * @throws InvalidInterfaceException
1448
-     * @throws InvalidDataTypeException
1449
-     * @throws EE_Error
1450
-     */
1451
-    public function e_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1452
-    {
1453
-        $this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1454
-    }
1455
-
1456
-
1457
-    /**
1458
-     * Get the i8ln value for a date using the WordPress @see date_i18n function.
1459
-     *
1460
-     * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1461
-     * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1462
-     *                           on the object will be used.
1463
-     * @return string Date and time string in set locale or false if no field exists for the given
1464
-     * @throws ReflectionException
1465
-     * @throws InvalidArgumentException
1466
-     * @throws InvalidInterfaceException
1467
-     * @throws InvalidDataTypeException
1468
-     * @throws EE_Error
1469
-     *                           field name.
1470
-     */
1471
-    public function get_i18n_datetime($field_name, $format = '')
1472
-    {
1473
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1474
-        return date_i18n(
1475
-            $format,
1476
-            EEH_DTT_Helper::get_timestamp_with_offset(
1477
-                $this->get_raw($field_name),
1478
-                $this->_timezone
1479
-            )
1480
-        );
1481
-    }
1482
-
1483
-
1484
-    /**
1485
-     * This method validates whether the given field name is a valid field on the model object as well as it is of a
1486
-     * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1487
-     * thrown.
1488
-     *
1489
-     * @param  string $field_name The field name being checked
1490
-     * @throws ReflectionException
1491
-     * @throws InvalidArgumentException
1492
-     * @throws InvalidInterfaceException
1493
-     * @throws InvalidDataTypeException
1494
-     * @throws EE_Error
1495
-     * @return EE_Datetime_Field
1496
-     */
1497
-    protected function _get_dtt_field_settings($field_name)
1498
-    {
1499
-        $field = $this->get_model()->field_settings_for($field_name);
1500
-        //check if field is dtt
1501
-        if ($field instanceof EE_Datetime_Field) {
1502
-            return $field;
1503
-        }
1504
-        throw new EE_Error(
1505
-            sprintf(
1506
-                esc_html__(
1507
-                    'The field name "%s" has been requested for the EE_Base_Class datetime functions and it is not a valid EE_Datetime_Field.  Please check the spelling of the field and make sure it has been setup as a EE_Datetime_Field in the %s model constructor',
1508
-                    'event_espresso'
1509
-                ),
1510
-                $field_name,
1511
-                self::_get_model_classname(get_class($this))
1512
-            )
1513
-        );
1514
-    }
1515
-
1516
-
1517
-
1518
-
1519
-    /**
1520
-     * NOTE ABOUT BELOW:
1521
-     * These convenience date and time setters are for setting date and time independently.  In other words you might
1522
-     * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1523
-     * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1524
-     * method and make sure you send the entire datetime value for setting.
1525
-     */
1526
-    /**
1527
-     * sets the time on a datetime property
1528
-     *
1529
-     * @access protected
1530
-     * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1531
-     * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1532
-     * @throws ReflectionException
1533
-     * @throws InvalidArgumentException
1534
-     * @throws InvalidInterfaceException
1535
-     * @throws InvalidDataTypeException
1536
-     * @throws EE_Error
1537
-     */
1538
-    protected function _set_time_for($time, $fieldname)
1539
-    {
1540
-        $this->_set_date_time('T', $time, $fieldname);
1541
-    }
1542
-
1543
-
1544
-    /**
1545
-     * sets the date on a datetime property
1546
-     *
1547
-     * @access protected
1548
-     * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1549
-     * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1550
-     * @throws ReflectionException
1551
-     * @throws InvalidArgumentException
1552
-     * @throws InvalidInterfaceException
1553
-     * @throws InvalidDataTypeException
1554
-     * @throws EE_Error
1555
-     */
1556
-    protected function _set_date_for($date, $fieldname)
1557
-    {
1558
-        $this->_set_date_time('D', $date, $fieldname);
1559
-    }
1560
-
1561
-
1562
-    /**
1563
-     * This takes care of setting a date or time independently on a given model object property. This method also
1564
-     * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1565
-     *
1566
-     * @access protected
1567
-     * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1568
-     * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1569
-     * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1570
-     *                                        EE_Datetime_Field property)
1571
-     * @throws ReflectionException
1572
-     * @throws InvalidArgumentException
1573
-     * @throws InvalidInterfaceException
1574
-     * @throws InvalidDataTypeException
1575
-     * @throws EE_Error
1576
-     */
1577
-    protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1578
-    {
1579
-        $field = $this->_get_dtt_field_settings($fieldname);
1580
-        $field->set_timezone($this->_timezone);
1581
-        $field->set_date_format($this->_dt_frmt);
1582
-        $field->set_time_format($this->_tm_frmt);
1583
-        switch ($what) {
1584
-            case 'T' :
1585
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1586
-                    $datetime_value,
1587
-                    $this->_fields[ $fieldname ]
1588
-                );
1589
-                break;
1590
-            case 'D' :
1591
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1592
-                    $datetime_value,
1593
-                    $this->_fields[ $fieldname ]
1594
-                );
1595
-                break;
1596
-            case 'B' :
1597
-                $this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1598
-                break;
1599
-        }
1600
-        $this->_clear_cached_property($fieldname);
1601
-    }
1602
-
1603
-
1604
-    /**
1605
-     * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1606
-     * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1607
-     * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1608
-     * that could lead to some unexpected results!
1609
-     *
1610
-     * @access public
1611
-     * @param string $field_name               This is the name of the field on the object that contains the date/time
1612
-     *                                         value being returned.
1613
-     * @param string $callback                 must match a valid method in this class (defaults to get_datetime)
1614
-     * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1615
-     * @param string $prepend                  You can include something to prepend on the timestamp
1616
-     * @param string $append                   You can include something to append on the timestamp
1617
-     * @throws ReflectionException
1618
-     * @throws InvalidArgumentException
1619
-     * @throws InvalidInterfaceException
1620
-     * @throws InvalidDataTypeException
1621
-     * @throws EE_Error
1622
-     * @return string timestamp
1623
-     */
1624
-    public function display_in_my_timezone(
1625
-        $field_name,
1626
-        $callback = 'get_datetime',
1627
-        $args = null,
1628
-        $prepend = '',
1629
-        $append = ''
1630
-    ) {
1631
-        $timezone = EEH_DTT_Helper::get_timezone();
1632
-        if ($timezone === $this->_timezone) {
1633
-            return '';
1634
-        }
1635
-        $original_timezone = $this->_timezone;
1636
-        $this->set_timezone($timezone);
1637
-        $fn   = (array) $field_name;
1638
-        $args = array_merge($fn, (array) $args);
1639
-        if (! method_exists($this, $callback)) {
1640
-            throw new EE_Error(
1641
-                sprintf(
1642
-                    esc_html__(
1643
-                        'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1644
-                        'event_espresso'
1645
-                    ),
1646
-                    $callback
1647
-                )
1648
-            );
1649
-        }
1650
-        $args   = (array) $args;
1651
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1652
-        $this->set_timezone($original_timezone);
1653
-        return $return;
1654
-    }
1655
-
1656
-
1657
-    /**
1658
-     * Deletes this model object.
1659
-     * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1660
-     * override
1661
-     * `EE_Base_Class::_delete` NOT this class.
1662
-     *
1663
-     * @return boolean | int
1664
-     * @throws ReflectionException
1665
-     * @throws InvalidArgumentException
1666
-     * @throws InvalidInterfaceException
1667
-     * @throws InvalidDataTypeException
1668
-     * @throws EE_Error
1669
-     */
1670
-    public function delete()
1671
-    {
1672
-        /**
1673
-         * Called just before the `EE_Base_Class::_delete` method call.
1674
-         * Note:
1675
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1676
-         * should be aware that `_delete` may not always result in a permanent delete.
1677
-         * For example, `EE_Soft_Delete_Base_Class::_delete`
1678
-         * soft deletes (trash) the object and does not permanently delete it.
1679
-         *
1680
-         * @param EE_Base_Class $model_object about to be 'deleted'
1681
-         */
1682
-        do_action('AHEE__EE_Base_Class__delete__before', $this);
1683
-        $result = $this->_delete();
1684
-        /**
1685
-         * Called just after the `EE_Base_Class::_delete` method call.
1686
-         * Note:
1687
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1688
-         * should be aware that `_delete` may not always result in a permanent delete.
1689
-         * For example `EE_Soft_Base_Class::_delete`
1690
-         * soft deletes (trash) the object and does not permanently delete it.
1691
-         *
1692
-         * @param EE_Base_Class $model_object that was just 'deleted'
1693
-         * @param boolean       $result
1694
-         */
1695
-        do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1696
-        return $result;
1697
-    }
1698
-
1699
-
1700
-    /**
1701
-     * Calls the specific delete method for the instantiated class.
1702
-     * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1703
-     * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1704
-     * `EE_Base_Class::delete`
1705
-     *
1706
-     * @return bool|int
1707
-     * @throws ReflectionException
1708
-     * @throws InvalidArgumentException
1709
-     * @throws InvalidInterfaceException
1710
-     * @throws InvalidDataTypeException
1711
-     * @throws EE_Error
1712
-     */
1713
-    protected function _delete()
1714
-    {
1715
-        return $this->delete_permanently();
1716
-    }
1717
-
1718
-
1719
-    /**
1720
-     * Deletes this model object permanently from db
1721
-     * (but keep in mind related models may block the delete and return an error)
1722
-     *
1723
-     * @return bool | int
1724
-     * @throws ReflectionException
1725
-     * @throws InvalidArgumentException
1726
-     * @throws InvalidInterfaceException
1727
-     * @throws InvalidDataTypeException
1728
-     * @throws EE_Error
1729
-     */
1730
-    public function delete_permanently()
1731
-    {
1732
-        /**
1733
-         * Called just before HARD deleting a model object
1734
-         *
1735
-         * @param EE_Base_Class $model_object about to be 'deleted'
1736
-         */
1737
-        do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1738
-        $model  = $this->get_model();
1739
-        $result = $model->delete_permanently_by_ID($this->ID());
1740
-        $this->refresh_cache_of_related_objects();
1741
-        /**
1742
-         * Called just after HARD deleting a model object
1743
-         *
1744
-         * @param EE_Base_Class $model_object that was just 'deleted'
1745
-         * @param boolean       $result
1746
-         */
1747
-        do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1748
-        return $result;
1749
-    }
1750
-
1751
-
1752
-    /**
1753
-     * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1754
-     * related model objects
1755
-     *
1756
-     * @throws ReflectionException
1757
-     * @throws InvalidArgumentException
1758
-     * @throws InvalidInterfaceException
1759
-     * @throws InvalidDataTypeException
1760
-     * @throws EE_Error
1761
-     */
1762
-    public function refresh_cache_of_related_objects()
1763
-    {
1764
-        $model = $this->get_model();
1765
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1766
-            if (! empty($this->_model_relations[ $relation_name ])) {
1767
-                $related_objects = $this->_model_relations[ $relation_name ];
1768
-                if ($relation_obj instanceof EE_Belongs_To_Relation) {
1769
-                    //this relation only stores a single model object, not an array
1770
-                    //but let's make it consistent
1771
-                    $related_objects = array($related_objects);
1772
-                }
1773
-                foreach ($related_objects as $related_object) {
1774
-                    //only refresh their cache if they're in memory
1775
-                    if ($related_object instanceof EE_Base_Class) {
1776
-                        $related_object->clear_cache(
1777
-                            $model->get_this_model_name(),
1778
-                            $this
1779
-                        );
1780
-                    }
1781
-                }
1782
-            }
1783
-        }
1784
-    }
1785
-
1786
-
1787
-    /**
1788
-     *        Saves this object to the database. An array may be supplied to set some values on this
1789
-     * object just before saving.
1790
-     *
1791
-     * @access public
1792
-     * @param array $set_cols_n_values keys are field names, values are their new values,
1793
-     *                                 if provided during the save() method (often client code will change the fields'
1794
-     *                                 values before calling save)
1795
-     * @throws InvalidArgumentException
1796
-     * @throws InvalidInterfaceException
1797
-     * @throws InvalidDataTypeException
1798
-     * @throws EE_Error
1799
-     * @return int , 1 on a successful update, the ID of the new entry on insert; 0 on failure or if the model object
1800
-     *                                 isn't allowed to persist (as determined by EE_Base_Class::allow_persist())
1801
-     * @throws ReflectionException
1802
-     * @throws ReflectionException
1803
-     * @throws ReflectionException
1804
-     */
1805
-    public function save($set_cols_n_values = array())
1806
-    {
1807
-        $model = $this->get_model();
1808
-        /**
1809
-         * Filters the fields we're about to save on the model object
1810
-         *
1811
-         * @param array         $set_cols_n_values
1812
-         * @param EE_Base_Class $model_object
1813
-         */
1814
-        $set_cols_n_values = (array) apply_filters(
1815
-            'FHEE__EE_Base_Class__save__set_cols_n_values',
1816
-            $set_cols_n_values,
1817
-            $this
1818
-        );
1819
-        //set attributes as provided in $set_cols_n_values
1820
-        foreach ($set_cols_n_values as $column => $value) {
1821
-            $this->set($column, $value);
1822
-        }
1823
-        // no changes ? then don't do anything
1824
-        if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1825
-            return 0;
1826
-        }
1827
-        /**
1828
-         * Saving a model object.
1829
-         * Before we perform a save, this action is fired.
1830
-         *
1831
-         * @param EE_Base_Class $model_object the model object about to be saved.
1832
-         */
1833
-        do_action('AHEE__EE_Base_Class__save__begin', $this);
1834
-        if (! $this->allow_persist()) {
1835
-            return 0;
1836
-        }
1837
-        // now get current attribute values
1838
-        $save_cols_n_values = $this->_fields;
1839
-        // if the object already has an ID, update it. Otherwise, insert it
1840
-        // also: change the assumption about values passed to the model NOT being prepare dby the model object.
1841
-        // They have been
1842
-        $old_assumption_concerning_value_preparation = $model
1843
-            ->get_assumption_concerning_values_already_prepared_by_model_object();
1844
-        $model->assume_values_already_prepared_by_model_object(true);
1845
-        //does this model have an autoincrement PK?
1846
-        if ($model->has_primary_key_field()) {
1847
-            if ($model->get_primary_key_field()->is_auto_increment()) {
1848
-                //ok check if it's set, if so: update; if not, insert
1849
-                if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1850
-                    $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1851
-                } else {
1852
-                    unset($save_cols_n_values[ $model->primary_key_name() ]);
1853
-                    $results = $model->insert($save_cols_n_values);
1854
-                    if ($results) {
1855
-                        //if successful, set the primary key
1856
-                        //but don't use the normal SET method, because it will check if
1857
-                        //an item with the same ID exists in the mapper & db, then
1858
-                        //will find it in the db (because we just added it) and THAT object
1859
-                        //will get added to the mapper before we can add this one!
1860
-                        //but if we just avoid using the SET method, all that headache can be avoided
1861
-                        $pk_field_name                   = $model->primary_key_name();
1862
-                        $this->_fields[ $pk_field_name ] = $results;
1863
-                        $this->_clear_cached_property($pk_field_name);
1864
-                        $model->add_to_entity_map($this);
1865
-                        $this->_update_cached_related_model_objs_fks();
1866
-                    }
1867
-                }
1868
-            } else {//PK is NOT auto-increment
1869
-                //so check if one like it already exists in the db
1870
-                if ($model->exists_by_ID($this->ID())) {
1871
-                    if (WP_DEBUG && ! $this->in_entity_map()) {
1872
-                        throw new EE_Error(
1873
-                            sprintf(
1874
-                                esc_html__(
1875
-                                    'Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1876
-                                    'event_espresso'
1877
-                                ),
1878
-                                get_class($this),
1879
-                                get_class($model) . '::instance()->add_to_entity_map()',
1880
-                                get_class($model) . '::instance()->get_one_by_ID()',
1881
-                                '<br />'
1882
-                            )
1883
-                        );
1884
-                    }
1885
-                    $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1886
-                } else {
1887
-                    $results = $model->insert($save_cols_n_values);
1888
-                    $this->_update_cached_related_model_objs_fks();
1889
-                }
1890
-            }
1891
-        } else {//there is NO primary key
1892
-            $already_in_db = false;
1893
-            foreach ($model->unique_indexes() as $index) {
1894
-                $uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1895
-                if ($model->exists(array($uniqueness_where_params))) {
1896
-                    $already_in_db = true;
1897
-                }
1898
-            }
1899
-            if ($already_in_db) {
1900
-                $combined_pk_fields_n_values = array_intersect_key($save_cols_n_values,
1901
-                    $model->get_combined_primary_key_fields());
1902
-                $results                     = $model->update(
1903
-                    $save_cols_n_values,
1904
-                    $combined_pk_fields_n_values
1905
-                );
1906
-            } else {
1907
-                $results = $model->insert($save_cols_n_values);
1908
-            }
1909
-        }
1910
-        //restore the old assumption about values being prepared by the model object
1911
-        $model->assume_values_already_prepared_by_model_object(
1912
-                $old_assumption_concerning_value_preparation
1913
-            );
1914
-        /**
1915
-         * After saving the model object this action is called
1916
-         *
1917
-         * @param EE_Base_Class $model_object which was just saved
1918
-         * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1919
-         *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1920
-         */
1921
-        do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1922
-        $this->_has_changes = false;
1923
-        return $results;
1924
-    }
1925
-
1926
-
1927
-    /**
1928
-     * Updates the foreign key on related models objects pointing to this to have this model object's ID
1929
-     * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1930
-     * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1931
-     * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1932
-     * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1933
-     * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1934
-     * or not they exist in the DB (if they do, their DB records will be automatically updated)
1935
-     *
1936
-     * @return void
1937
-     * @throws ReflectionException
1938
-     * @throws InvalidArgumentException
1939
-     * @throws InvalidInterfaceException
1940
-     * @throws InvalidDataTypeException
1941
-     * @throws EE_Error
1942
-     */
1943
-    protected function _update_cached_related_model_objs_fks()
1944
-    {
1945
-        $model = $this->get_model();
1946
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1947
-            if ($relation_obj instanceof EE_Has_Many_Relation) {
1948
-                foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1949
-                    $fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1950
-                        $model->get_this_model_name()
1951
-                    );
1952
-                    $related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1953
-                    if ($related_model_obj_in_cache->ID()) {
1954
-                        $related_model_obj_in_cache->save();
1955
-                    }
1956
-                }
1957
-            }
1958
-        }
1959
-    }
1960
-
1961
-
1962
-    /**
1963
-     * Saves this model object and its NEW cached relations to the database.
1964
-     * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1965
-     * In order for that to work, we would need to mark model objects as dirty/clean...
1966
-     * because otherwise, there's a potential for infinite looping of saving
1967
-     * Saves the cached related model objects, and ensures the relation between them
1968
-     * and this object and properly setup
1969
-     *
1970
-     * @return int ID of new model object on save; 0 on failure+
1971
-     * @throws ReflectionException
1972
-     * @throws InvalidArgumentException
1973
-     * @throws InvalidInterfaceException
1974
-     * @throws InvalidDataTypeException
1975
-     * @throws EE_Error
1976
-     */
1977
-    public function save_new_cached_related_model_objs()
1978
-    {
1979
-        //make sure this has been saved
1980
-        if (! $this->ID()) {
1981
-            $id = $this->save();
1982
-        } else {
1983
-            $id = $this->ID();
1984
-        }
1985
-        //now save all the NEW cached model objects  (ie they don't exist in the DB)
1986
-        foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1987
-            if ($this->_model_relations[ $relationName ]) {
1988
-                //is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1989
-                //or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1990
-                /* @var $related_model_obj EE_Base_Class */
1991
-                if ($relationObj instanceof EE_Belongs_To_Relation) {
1992
-                    //add a relation to that relation type (which saves the appropriate thing in the process)
1993
-                    //but ONLY if it DOES NOT exist in the DB
1994
-                    $related_model_obj = $this->_model_relations[ $relationName ];
1995
-                    //					if( ! $related_model_obj->ID()){
1996
-                    $this->_add_relation_to($related_model_obj, $relationName);
1997
-                    $related_model_obj->save_new_cached_related_model_objs();
1998
-                    //					}
1999
-                } else {
2000
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2001
-                        //add a relation to that relation type (which saves the appropriate thing in the process)
2002
-                        //but ONLY if it DOES NOT exist in the DB
2003
-                        //						if( ! $related_model_obj->ID()){
2004
-                        $this->_add_relation_to($related_model_obj, $relationName);
2005
-                        $related_model_obj->save_new_cached_related_model_objs();
2006
-                        //						}
2007
-                    }
2008
-                }
2009
-            }
2010
-        }
2011
-        return $id;
2012
-    }
2013
-
2014
-
2015
-    /**
2016
-     * for getting a model while instantiated.
2017
-     *
2018
-     * @return EEM_Base | EEM_CPT_Base
2019
-     * @throws ReflectionException
2020
-     * @throws InvalidArgumentException
2021
-     * @throws InvalidInterfaceException
2022
-     * @throws InvalidDataTypeException
2023
-     * @throws EE_Error
2024
-     */
2025
-    public function get_model()
2026
-    {
2027
-        if (! $this->_model) {
2028
-            $modelName    = self::_get_model_classname(get_class($this));
2029
-            $this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2030
-        } else {
2031
-            $this->_model->set_timezone($this->_timezone);
2032
-        }
2033
-        return $this->_model;
2034
-    }
2035
-
2036
-
2037
-    /**
2038
-     * @param $props_n_values
2039
-     * @param $classname
2040
-     * @return mixed bool|EE_Base_Class|EEM_CPT_Base
2041
-     * @throws ReflectionException
2042
-     * @throws InvalidArgumentException
2043
-     * @throws InvalidInterfaceException
2044
-     * @throws InvalidDataTypeException
2045
-     * @throws EE_Error
2046
-     */
2047
-    protected static function _get_object_from_entity_mapper($props_n_values, $classname)
2048
-    {
2049
-        //TODO: will not work for Term_Relationships because they have no PK!
2050
-        $primary_id_ref = self::_get_primary_key_name($classname);
2051
-        if (
2052
-            array_key_exists($primary_id_ref, $props_n_values)
2053
-            && ! empty($props_n_values[ $primary_id_ref ])
2054
-        ) {
2055
-            $id = $props_n_values[ $primary_id_ref ];
2056
-            return self::_get_model($classname)->get_from_entity_map($id);
2057
-        }
2058
-        return false;
2059
-    }
2060
-
2061
-
2062
-    /**
2063
-     * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
2064
-     * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
2065
-     * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
2066
-     * we return false.
2067
-     *
2068
-     * @param  array  $props_n_values   incoming array of properties and their values
2069
-     * @param  string $classname        the classname of the child class
2070
-     * @param null    $timezone
2071
-     * @param array   $date_formats     incoming date_formats in an array where the first value is the
2072
-     *                                  date_format and the second value is the time format
2073
-     * @return mixed (EE_Base_Class|bool)
2074
-     * @throws InvalidArgumentException
2075
-     * @throws InvalidInterfaceException
2076
-     * @throws InvalidDataTypeException
2077
-     * @throws EE_Error
2078
-     * @throws ReflectionException
2079
-     * @throws ReflectionException
2080
-     * @throws ReflectionException
2081
-     */
2082
-    protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
2083
-    {
2084
-        $existing = null;
2085
-        $model    = self::_get_model($classname, $timezone);
2086
-        if ($model->has_primary_key_field()) {
2087
-            $primary_id_ref = self::_get_primary_key_name($classname);
2088
-            if (array_key_exists($primary_id_ref, $props_n_values)
2089
-                && ! empty($props_n_values[ $primary_id_ref ])
2090
-            ) {
2091
-                $existing = $model->get_one_by_ID(
2092
-                    $props_n_values[ $primary_id_ref ]
2093
-                );
2094
-            }
2095
-        } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
2096
-            //no primary key on this model, but there's still a matching item in the DB
2097
-            $existing = self::_get_model($classname, $timezone)->get_one_by_ID(
2098
-                self::_get_model($classname, $timezone)
2099
-                    ->get_index_primary_key_string($props_n_values)
2100
-            );
2101
-        }
2102
-        if ($existing) {
2103
-            //set date formats if present before setting values
2104
-            if (! empty($date_formats) && is_array($date_formats)) {
2105
-                $existing->set_date_format($date_formats[0]);
2106
-                $existing->set_time_format($date_formats[1]);
2107
-            } else {
2108
-                //set default formats for date and time
2109
-                $existing->set_date_format(get_option('date_format'));
2110
-                $existing->set_time_format(get_option('time_format'));
2111
-            }
2112
-            foreach ($props_n_values as $property => $field_value) {
2113
-                $existing->set($property, $field_value);
2114
-            }
2115
-            return $existing;
2116
-        }
2117
-        return false;
2118
-    }
2119
-
2120
-
2121
-    /**
2122
-     * Gets the EEM_*_Model for this class
2123
-     *
2124
-     * @access public now, as this is more convenient
2125
-     * @param      $classname
2126
-     * @param null $timezone
2127
-     * @throws ReflectionException
2128
-     * @throws InvalidArgumentException
2129
-     * @throws InvalidInterfaceException
2130
-     * @throws InvalidDataTypeException
2131
-     * @throws EE_Error
2132
-     * @return EEM_Base
2133
-     */
2134
-    protected static function _get_model($classname, $timezone = null)
2135
-    {
2136
-        //find model for this class
2137
-        if (! $classname) {
2138
-            throw new EE_Error(
2139
-                sprintf(
2140
-                    esc_html__(
2141
-                        'What were you thinking calling _get_model(%s)?? You need to specify the class name',
2142
-                        'event_espresso'
2143
-                    ),
2144
-                    $classname
2145
-                )
2146
-            );
2147
-        }
2148
-        $modelName = self::_get_model_classname($classname);
2149
-        return self::_get_model_instance_with_name($modelName, $timezone);
2150
-    }
2151
-
2152
-
2153
-    /**
2154
-     * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2155
-     *
2156
-     * @param string $model_classname
2157
-     * @param null   $timezone
2158
-     * @return EEM_Base
2159
-     * @throws ReflectionException
2160
-     * @throws InvalidArgumentException
2161
-     * @throws InvalidInterfaceException
2162
-     * @throws InvalidDataTypeException
2163
-     * @throws EE_Error
2164
-     */
2165
-    protected static function _get_model_instance_with_name($model_classname, $timezone = null)
2166
-    {
2167
-        $model_classname = str_replace('EEM_', '', $model_classname);
2168
-        $model           = EE_Registry::instance()->load_model($model_classname);
2169
-        $model->set_timezone($timezone);
2170
-        return $model;
2171
-    }
2172
-
2173
-
2174
-    /**
2175
-     * If a model name is provided (eg Registration), gets the model classname for that model.
2176
-     * Also works if a model class's classname is provided (eg EE_Registration).
2177
-     *
2178
-     * @param null $model_name
2179
-     * @return string like EEM_Attendee
2180
-     */
2181
-    private static function _get_model_classname($model_name = null)
2182
-    {
2183
-        if (strpos($model_name, 'EE_') === 0) {
2184
-            $model_classname = str_replace('EE_', 'EEM_', $model_name);
2185
-        } else {
2186
-            $model_classname = 'EEM_' . $model_name;
2187
-        }
2188
-        return $model_classname;
2189
-    }
2190
-
2191
-
2192
-    /**
2193
-     * returns the name of the primary key attribute
2194
-     *
2195
-     * @param null $classname
2196
-     * @throws ReflectionException
2197
-     * @throws InvalidArgumentException
2198
-     * @throws InvalidInterfaceException
2199
-     * @throws InvalidDataTypeException
2200
-     * @throws EE_Error
2201
-     * @return string
2202
-     */
2203
-    protected static function _get_primary_key_name($classname = null)
2204
-    {
2205
-        if (! $classname) {
2206
-            throw new EE_Error(
2207
-                sprintf(
2208
-                    esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
2209
-                    $classname
2210
-                )
2211
-            );
2212
-        }
2213
-        return self::_get_model($classname)->get_primary_key_field()->get_name();
2214
-    }
2215
-
2216
-
2217
-    /**
2218
-     * Gets the value of the primary key.
2219
-     * If the object hasn't yet been saved, it should be whatever the model field's default was
2220
-     * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
2221
-     * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
2222
-     *
2223
-     * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
2224
-     * @throws ReflectionException
2225
-     * @throws InvalidArgumentException
2226
-     * @throws InvalidInterfaceException
2227
-     * @throws InvalidDataTypeException
2228
-     * @throws EE_Error
2229
-     */
2230
-    public function ID()
2231
-    {
2232
-        $model = $this->get_model();
2233
-        //now that we know the name of the variable, use a variable variable to get its value and return its
2234
-        if ($model->has_primary_key_field()) {
2235
-            return $this->_fields[ $model->primary_key_name() ];
2236
-        }
2237
-        return $model->get_index_primary_key_string($this->_fields);
2238
-    }
2239
-
2240
-
2241
-    /**
2242
-     * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2243
-     * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2244
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2245
-     *
2246
-     * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2247
-     * @param string $relationName                     eg 'Events','Question',etc.
2248
-     *                                                 an attendee to a group, you also want to specify which role they
2249
-     *                                                 will have in that group. So you would use this parameter to
2250
-     *                                                 specify array('role-column-name'=>'role-id')
2251
-     * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2252
-     *                                                 allow you to further constrict the relation to being added.
2253
-     *                                                 However, keep in mind that the columns (keys) given must match a
2254
-     *                                                 column on the JOIN table and currently only the HABTM models
2255
-     *                                                 accept these additional conditions.  Also remember that if an
2256
-     *                                                 exact match isn't found for these extra cols/val pairs, then a
2257
-     *                                                 NEW row is created in the join table.
2258
-     * @param null   $cache_id
2259
-     * @throws ReflectionException
2260
-     * @throws InvalidArgumentException
2261
-     * @throws InvalidInterfaceException
2262
-     * @throws InvalidDataTypeException
2263
-     * @throws EE_Error
2264
-     * @return EE_Base_Class the object the relation was added to
2265
-     */
2266
-    public function _add_relation_to(
2267
-        $otherObjectModelObjectOrID,
2268
-        $relationName,
2269
-        $extra_join_model_fields_n_values = array(),
2270
-        $cache_id = null
2271
-    ) {
2272
-        $model = $this->get_model();
2273
-        //if this thing exists in the DB, save the relation to the DB
2274
-        if ($this->ID()) {
2275
-            $otherObject = $model->add_relationship_to(
2276
-                $this,
2277
-                $otherObjectModelObjectOrID,
2278
-                $relationName,
2279
-                $extra_join_model_fields_n_values
2280
-            );
2281
-            //clear cache so future get_many_related and get_first_related() return new results.
2282
-            $this->clear_cache($relationName, $otherObject, true);
2283
-            if ($otherObject instanceof EE_Base_Class) {
2284
-                $otherObject->clear_cache($model->get_this_model_name(), $this);
2285
-            }
2286
-        } else {
2287
-            //this thing doesn't exist in the DB,  so just cache it
2288
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2289
-                throw new EE_Error(
2290
-                    sprintf(
2291
-                        esc_html__(
2292
-                            'Before a model object is saved to the database, calls to _add_relation_to must be passed an actual object, not just an ID. You provided %s as the model object to a %s',
2293
-                            'event_espresso'
2294
-                        ),
2295
-                        $otherObjectModelObjectOrID,
2296
-                        get_class($this)
2297
-                    )
2298
-                );
2299
-            }
2300
-            $otherObject = $otherObjectModelObjectOrID;
2301
-            $this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2302
-        }
2303
-        if ($otherObject instanceof EE_Base_Class) {
2304
-            //fix the reciprocal relation too
2305
-            if ($otherObject->ID()) {
2306
-                //its saved so assumed relations exist in the DB, so we can just
2307
-                //clear the cache so future queries use the updated info in the DB
2308
-                $otherObject->clear_cache(
2309
-                    $model->get_this_model_name(),
2310
-                    null,
2311
-                    true
2312
-                );
2313
-            } else {
2314
-                //it's not saved, so it caches relations like this
2315
-                $otherObject->cache($model->get_this_model_name(), $this);
2316
-            }
2317
-        }
2318
-        return $otherObject;
2319
-    }
2320
-
2321
-
2322
-    /**
2323
-     * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2324
-     * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2325
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2326
-     * from the cache
2327
-     *
2328
-     * @param mixed  $otherObjectModelObjectOrID
2329
-     *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2330
-     *                to the DB yet
2331
-     * @param string $relationName
2332
-     * @param array  $where_query
2333
-     *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2334
-     *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2335
-     *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2336
-     *                remember that if an exact match isn't found for these extra cols/val pairs, then a NEW row is
2337
-     *                created in the join table.
2338
-     * @return EE_Base_Class the relation was removed from
2339
-     * @throws ReflectionException
2340
-     * @throws InvalidArgumentException
2341
-     * @throws InvalidInterfaceException
2342
-     * @throws InvalidDataTypeException
2343
-     * @throws EE_Error
2344
-     */
2345
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2346
-    {
2347
-        if ($this->ID()) {
2348
-            //if this exists in the DB, save the relation change to the DB too
2349
-            $otherObject = $this->get_model()->remove_relationship_to(
2350
-                $this,
2351
-                $otherObjectModelObjectOrID,
2352
-                $relationName,
2353
-                $where_query
2354
-            );
2355
-            $this->clear_cache(
2356
-                $relationName,
2357
-                $otherObject
2358
-            );
2359
-        } else {
2360
-            //this doesn't exist in the DB, just remove it from the cache
2361
-            $otherObject = $this->clear_cache(
2362
-                $relationName,
2363
-                $otherObjectModelObjectOrID
2364
-            );
2365
-        }
2366
-        if ($otherObject instanceof EE_Base_Class) {
2367
-            $otherObject->clear_cache(
2368
-                $this->get_model()->get_this_model_name(),
2369
-                $this
2370
-            );
2371
-        }
2372
-        return $otherObject;
2373
-    }
2374
-
2375
-
2376
-    /**
2377
-     * Removes ALL the related things for the $relationName.
2378
-     *
2379
-     * @param string $relationName
2380
-     * @param array  $where_query_params like EEM_Base::get_all's $query_params[0] (where conditions)
2381
-     * @return EE_Base_Class
2382
-     * @throws ReflectionException
2383
-     * @throws InvalidArgumentException
2384
-     * @throws InvalidInterfaceException
2385
-     * @throws InvalidDataTypeException
2386
-     * @throws EE_Error
2387
-     */
2388
-    public function _remove_relations($relationName, $where_query_params = array())
2389
-    {
2390
-        if ($this->ID()) {
2391
-            //if this exists in the DB, save the relation change to the DB too
2392
-            $otherObjects = $this->get_model()->remove_relations(
2393
-                $this,
2394
-                $relationName,
2395
-                $where_query_params
2396
-            );
2397
-            $this->clear_cache(
2398
-                $relationName,
2399
-                null,
2400
-                true
2401
-            );
2402
-        } else {
2403
-            //this doesn't exist in the DB, just remove it from the cache
2404
-            $otherObjects = $this->clear_cache(
2405
-                $relationName,
2406
-                null,
2407
-                true
2408
-            );
2409
-        }
2410
-        if (is_array($otherObjects)) {
2411
-            foreach ($otherObjects as $otherObject) {
2412
-                $otherObject->clear_cache(
2413
-                    $this->get_model()->get_this_model_name(),
2414
-                    $this
2415
-                );
2416
-            }
2417
-        }
2418
-        return $otherObjects;
2419
-    }
2420
-
2421
-
2422
-    /**
2423
-     * Gets all the related model objects of the specified type. Eg, if the current class if
2424
-     * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2425
-     * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2426
-     * because we want to get even deleted items etc.
2427
-     *
2428
-     * @param string $relationName key in the model's _model_relations array
2429
-     * @param array  $query_params like EEM_Base::get_all
2430
-     * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
2431
-     *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
2432
-     *                             results if you want IDs
2433
-     * @throws ReflectionException
2434
-     * @throws InvalidArgumentException
2435
-     * @throws InvalidInterfaceException
2436
-     * @throws InvalidDataTypeException
2437
-     * @throws EE_Error
2438
-     */
2439
-    public function get_many_related($relationName, $query_params = array())
2440
-    {
2441
-        if ($this->ID()) {
2442
-            //this exists in the DB, so get the related things from either the cache or the DB
2443
-            //if there are query parameters, forget about caching the related model objects.
2444
-            if ($query_params) {
2445
-                $related_model_objects = $this->get_model()->get_all_related(
2446
-                    $this,
2447
-                    $relationName,
2448
-                    $query_params
2449
-                );
2450
-            } else {
2451
-                //did we already cache the result of this query?
2452
-                $cached_results = $this->get_all_from_cache($relationName);
2453
-                if (! $cached_results) {
2454
-                    $related_model_objects = $this->get_model()->get_all_related(
2455
-                        $this,
2456
-                        $relationName,
2457
-                        $query_params
2458
-                    );
2459
-                    //if no query parameters were passed, then we got all the related model objects
2460
-                    //for that relation. We can cache them then.
2461
-                    foreach ($related_model_objects as $related_model_object) {
2462
-                        $this->cache($relationName, $related_model_object);
2463
-                    }
2464
-                } else {
2465
-                    $related_model_objects = $cached_results;
2466
-                }
2467
-            }
2468
-        } else {
2469
-            //this doesn't exist in the DB, so just get the related things from the cache
2470
-            $related_model_objects = $this->get_all_from_cache($relationName);
2471
-        }
2472
-        return $related_model_objects;
2473
-    }
2474
-
2475
-
2476
-    /**
2477
-     * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2478
-     * unless otherwise specified in the $query_params
2479
-     *
2480
-     * @param string $relation_name  model_name like 'Event', or 'Registration'
2481
-     * @param array  $query_params   like EEM_Base::get_all's
2482
-     * @param string $field_to_count name of field to count by. By default, uses primary key
2483
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2484
-     *                               that by the setting $distinct to TRUE;
2485
-     * @return int
2486
-     * @throws ReflectionException
2487
-     * @throws InvalidArgumentException
2488
-     * @throws InvalidInterfaceException
2489
-     * @throws InvalidDataTypeException
2490
-     * @throws EE_Error
2491
-     */
2492
-    public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2493
-    {
2494
-        return $this->get_model()->count_related(
2495
-            $this,
2496
-            $relation_name,
2497
-            $query_params,
2498
-            $field_to_count,
2499
-            $distinct
2500
-        );
2501
-    }
2502
-
2503
-
2504
-    /**
2505
-     * Instead of getting the related model objects, simply sums up the values of the specified field.
2506
-     * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2507
-     *
2508
-     * @param string $relation_name model_name like 'Event', or 'Registration'
2509
-     * @param array  $query_params  like EEM_Base::get_all's
2510
-     * @param string $field_to_sum  name of field to count by.
2511
-     *                              By default, uses primary key
2512
-     *                              (which doesn't make much sense, so you should probably change it)
2513
-     * @return int
2514
-     * @throws ReflectionException
2515
-     * @throws InvalidArgumentException
2516
-     * @throws InvalidInterfaceException
2517
-     * @throws InvalidDataTypeException
2518
-     * @throws EE_Error
2519
-     */
2520
-    public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2521
-    {
2522
-        return $this->get_model()->sum_related(
2523
-            $this,
2524
-            $relation_name,
2525
-            $query_params,
2526
-            $field_to_sum
2527
-        );
2528
-    }
2529
-
2530
-
2531
-    /**
2532
-     * Gets the first (ie, one) related model object of the specified type.
2533
-     *
2534
-     * @param string $relationName key in the model's _model_relations array
2535
-     * @param array  $query_params like EEM_Base::get_all
2536
-     * @return EE_Base_Class (not an array, a single object)
2537
-     * @throws ReflectionException
2538
-     * @throws InvalidArgumentException
2539
-     * @throws InvalidInterfaceException
2540
-     * @throws InvalidDataTypeException
2541
-     * @throws EE_Error
2542
-     */
2543
-    public function get_first_related($relationName, $query_params = array())
2544
-    {
2545
-        $model = $this->get_model();
2546
-        if ($this->ID()) {//this exists in the DB, get from the cache OR the DB
2547
-            //if they've provided some query parameters, don't bother trying to cache the result
2548
-            //also make sure we're not caching the result of get_first_related
2549
-            //on a relation which should have an array of objects (because the cache might have an array of objects)
2550
-            if ($query_params
2551
-                || ! $model->related_settings_for($relationName)
2552
-                     instanceof
2553
-                     EE_Belongs_To_Relation
2554
-            ) {
2555
-                $related_model_object = $model->get_first_related(
2556
-                    $this,
2557
-                    $relationName,
2558
-                    $query_params
2559
-                );
2560
-            } else {
2561
-                //first, check if we've already cached the result of this query
2562
-                $cached_result = $this->get_one_from_cache($relationName);
2563
-                if (! $cached_result) {
2564
-                    $related_model_object = $model->get_first_related(
2565
-                        $this,
2566
-                        $relationName,
2567
-                        $query_params
2568
-                    );
2569
-                    $this->cache($relationName, $related_model_object);
2570
-                } else {
2571
-                    $related_model_object = $cached_result;
2572
-                }
2573
-            }
2574
-        } else {
2575
-            $related_model_object = null;
2576
-            // this doesn't exist in the Db,
2577
-            // but maybe the relation is of type belongs to, and so the related thing might
2578
-            if ($model->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2579
-                $related_model_object = $model->get_first_related(
2580
-                    $this,
2581
-                    $relationName,
2582
-                    $query_params
2583
-                );
2584
-            }
2585
-            // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2586
-            // just get what's cached on this object
2587
-            if (! $related_model_object) {
2588
-                $related_model_object = $this->get_one_from_cache($relationName);
2589
-            }
2590
-        }
2591
-        return $related_model_object;
2592
-    }
2593
-
2594
-
2595
-    /**
2596
-     * Does a delete on all related objects of type $relationName and removes
2597
-     * the current model object's relation to them. If they can't be deleted (because
2598
-     * of blocking related model objects) does nothing. If the related model objects are
2599
-     * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2600
-     * If this model object doesn't exist yet in the DB, just removes its related things
2601
-     *
2602
-     * @param string $relationName
2603
-     * @param array  $query_params like EEM_Base::get_all's
2604
-     * @return int how many deleted
2605
-     * @throws ReflectionException
2606
-     * @throws InvalidArgumentException
2607
-     * @throws InvalidInterfaceException
2608
-     * @throws InvalidDataTypeException
2609
-     * @throws EE_Error
2610
-     */
2611
-    public function delete_related($relationName, $query_params = array())
2612
-    {
2613
-        if ($this->ID()) {
2614
-            $count = $this->get_model()->delete_related(
2615
-                $this,
2616
-                $relationName,
2617
-                $query_params
2618
-            );
2619
-        } else {
2620
-            $count = count($this->get_all_from_cache($relationName));
2621
-            $this->clear_cache($relationName, null, true);
2622
-        }
2623
-        return $count;
2624
-    }
2625
-
2626
-
2627
-    /**
2628
-     * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2629
-     * the current model object's relation to them. If they can't be deleted (because
2630
-     * of blocking related model objects) just does a soft delete on it instead, if possible.
2631
-     * If the related thing isn't a soft-deletable model object, this function is identical
2632
-     * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2633
-     *
2634
-     * @param string $relationName
2635
-     * @param array  $query_params like EEM_Base::get_all's
2636
-     * @return int how many deleted (including those soft deleted)
2637
-     * @throws ReflectionException
2638
-     * @throws InvalidArgumentException
2639
-     * @throws InvalidInterfaceException
2640
-     * @throws InvalidDataTypeException
2641
-     * @throws EE_Error
2642
-     */
2643
-    public function delete_related_permanently($relationName, $query_params = array())
2644
-    {
2645
-        if ($this->ID()) {
2646
-            $count = $this->get_model()->delete_related_permanently(
2647
-                $this,
2648
-                $relationName,
2649
-                $query_params
2650
-            );
2651
-        } else {
2652
-            $count = count($this->get_all_from_cache($relationName));
2653
-        }
2654
-        $this->clear_cache($relationName, null, true);
2655
-        return $count;
2656
-    }
2657
-
2658
-
2659
-    /**
2660
-     * is_set
2661
-     * Just a simple utility function children can use for checking if property exists
2662
-     *
2663
-     * @access  public
2664
-     * @param  string $field_name property to check
2665
-     * @return bool                              TRUE if existing,FALSE if not.
2666
-     */
2667
-    public function is_set($field_name)
2668
-    {
2669
-        return isset($this->_fields[ $field_name ]);
2670
-    }
2671
-
2672
-
2673
-    /**
2674
-     * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2675
-     * EE_Error exception if they don't
2676
-     *
2677
-     * @param  mixed (string|array) $properties properties to check
2678
-     * @throws EE_Error
2679
-     * @return bool                              TRUE if existing, throw EE_Error if not.
2680
-     */
2681
-    protected function _property_exists($properties)
2682
-    {
2683
-        foreach ((array) $properties as $property_name) {
2684
-            //first make sure this property exists
2685
-            if (! $this->_fields[ $property_name ]) {
2686
-                throw new EE_Error(
2687
-                    sprintf(
2688
-                        esc_html__(
2689
-                            'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2690
-                            'event_espresso'
2691
-                        ),
2692
-                        $property_name
2693
-                    )
2694
-                );
2695
-            }
2696
-        }
2697
-        return true;
2698
-    }
2699
-
2700
-
2701
-    /**
2702
-     * This simply returns an array of model fields for this object
2703
-     *
2704
-     * @return array
2705
-     * @throws ReflectionException
2706
-     * @throws InvalidArgumentException
2707
-     * @throws InvalidInterfaceException
2708
-     * @throws InvalidDataTypeException
2709
-     * @throws EE_Error
2710
-     */
2711
-    public function model_field_array()
2712
-    {
2713
-        $fields     = $this->get_model()->field_settings(false);
2714
-        $properties = array();
2715
-        //remove prepended underscore
2716
-        foreach ($fields as $field_name => $settings) {
2717
-            $properties[ $field_name ] = $this->get($field_name);
2718
-        }
2719
-        return $properties;
2720
-    }
2721
-
2722
-
2723
-    /**
2724
-     * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2725
-     * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2726
-     * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2727
-     * Instead of requiring a plugin to extend the EE_Base_Class
2728
-     * (which works fine is there's only 1 plugin, but when will that happen?)
2729
-     * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2730
-     * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2731
-     * and accepts 2 arguments: the object on which the function was called,
2732
-     * and an array of the original arguments passed to the function.
2733
-     * Whatever their callback function returns will be returned by this function.
2734
-     * Example: in functions.php (or in a plugin):
2735
-     *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2736
-     *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2737
-     *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2738
-     *          return $previousReturnValue.$returnString;
2739
-     *      }
2740
-     * require('EE_Answer.class.php');
2741
-     * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2742
-     * echo $answer->my_callback('monkeys',100);
2743
-     * //will output "you called my_callback! and passed args:monkeys,100"
2744
-     *
2745
-     * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2746
-     * @param array  $args       array of original arguments passed to the function
2747
-     * @throws EE_Error
2748
-     * @return mixed whatever the plugin which calls add_filter decides
2749
-     */
2750
-    public function __call($methodName, $args)
2751
-    {
2752
-        $className = get_class($this);
2753
-        $tagName   = "FHEE__{$className}__{$methodName}";
2754
-        if (! has_filter($tagName)) {
2755
-            throw new EE_Error(
2756
-                sprintf(
2757
-                    esc_html__(
2758
-                        "Method %s on class %s does not exist! You can create one with the following code in functions.php or in a plugin: add_filter('%s','my_callback',10,3);function my_callback(\$previousReturnValue,EE_Base_Class \$object, \$argsArray){/*function body*/return \$whatever;}",
2759
-                        'event_espresso'
2760
-                    ),
2761
-                    $methodName,
2762
-                    $className,
2763
-                    $tagName
2764
-                )
2765
-            );
2766
-        }
2767
-        return apply_filters($tagName, null, $this, $args);
2768
-    }
2769
-
2770
-
2771
-    /**
2772
-     * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2773
-     * A $previous_value can be specified in case there are many meta rows with the same key
2774
-     *
2775
-     * @param string $meta_key
2776
-     * @param mixed  $meta_value
2777
-     * @param mixed  $previous_value
2778
-     * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2779
-     *                  NOTE: if the values haven't changed, returns 0
2780
-     * @throws InvalidArgumentException
2781
-     * @throws InvalidInterfaceException
2782
-     * @throws InvalidDataTypeException
2783
-     * @throws EE_Error
2784
-     * @throws ReflectionException
2785
-     */
2786
-    public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2787
-    {
2788
-        $query_params = array(
2789
-            array(
2790
-                'EXM_key'  => $meta_key,
2791
-                'OBJ_ID'   => $this->ID(),
2792
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2793
-            ),
2794
-        );
2795
-        if ($previous_value !== null) {
2796
-            $query_params[0]['EXM_value'] = $meta_value;
2797
-        }
2798
-        $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2799
-        if (! $existing_rows_like_that) {
2800
-            return $this->add_extra_meta($meta_key, $meta_value);
2801
-        }
2802
-        foreach ($existing_rows_like_that as $existing_row) {
2803
-            $existing_row->save(array('EXM_value' => $meta_value));
2804
-        }
2805
-        return count($existing_rows_like_that);
2806
-    }
2807
-
2808
-
2809
-    /**
2810
-     * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2811
-     * no other extra meta for this model object have the same key. Returns TRUE if the
2812
-     * extra meta row was entered, false if not
2813
-     *
2814
-     * @param string  $meta_key
2815
-     * @param mixed   $meta_value
2816
-     * @param boolean $unique
2817
-     * @return boolean
2818
-     * @throws InvalidArgumentException
2819
-     * @throws InvalidInterfaceException
2820
-     * @throws InvalidDataTypeException
2821
-     * @throws EE_Error
2822
-     * @throws ReflectionException
2823
-     * @throws ReflectionException
2824
-     */
2825
-    public function add_extra_meta($meta_key, $meta_value, $unique = false)
2826
-    {
2827
-        if ($unique) {
2828
-            $existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2829
-                array(
2830
-                    array(
2831
-                        'EXM_key'  => $meta_key,
2832
-                        'OBJ_ID'   => $this->ID(),
2833
-                        'EXM_type' => $this->get_model()->get_this_model_name(),
2834
-                    ),
2835
-                )
2836
-            );
2837
-            if ($existing_extra_meta) {
2838
-                return false;
2839
-            }
2840
-        }
2841
-        $new_extra_meta = EE_Extra_Meta::new_instance(
2842
-            array(
2843
-                'EXM_key'   => $meta_key,
2844
-                'EXM_value' => $meta_value,
2845
-                'OBJ_ID'    => $this->ID(),
2846
-                'EXM_type'  => $this->get_model()->get_this_model_name(),
2847
-            )
2848
-        );
2849
-        $new_extra_meta->save();
2850
-        return true;
2851
-    }
2852
-
2853
-
2854
-    /**
2855
-     * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2856
-     * is specified, only deletes extra meta records with that value.
2857
-     *
2858
-     * @param string $meta_key
2859
-     * @param mixed  $meta_value
2860
-     * @return int number of extra meta rows deleted
2861
-     * @throws InvalidArgumentException
2862
-     * @throws InvalidInterfaceException
2863
-     * @throws InvalidDataTypeException
2864
-     * @throws EE_Error
2865
-     * @throws ReflectionException
2866
-     */
2867
-    public function delete_extra_meta($meta_key, $meta_value = null)
2868
-    {
2869
-        $query_params = array(
2870
-            array(
2871
-                'EXM_key'  => $meta_key,
2872
-                'OBJ_ID'   => $this->ID(),
2873
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2874
-            ),
2875
-        );
2876
-        if ($meta_value !== null) {
2877
-            $query_params[0]['EXM_value'] = $meta_value;
2878
-        }
2879
-        return EEM_Extra_Meta::instance()->delete($query_params);
2880
-    }
2881
-
2882
-
2883
-    /**
2884
-     * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2885
-     * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2886
-     * You can specify $default is case you haven't found the extra meta
2887
-     *
2888
-     * @param string  $meta_key
2889
-     * @param boolean $single
2890
-     * @param mixed   $default if we don't find anything, what should we return?
2891
-     * @return mixed single value if $single; array if ! $single
2892
-     * @throws ReflectionException
2893
-     * @throws InvalidArgumentException
2894
-     * @throws InvalidInterfaceException
2895
-     * @throws InvalidDataTypeException
2896
-     * @throws EE_Error
2897
-     */
2898
-    public function get_extra_meta($meta_key, $single = false, $default = null)
2899
-    {
2900
-        if ($single) {
2901
-            $result = $this->get_first_related(
2902
-                'Extra_Meta',
2903
-                array(array('EXM_key' => $meta_key))
2904
-            );
2905
-            if ($result instanceof EE_Extra_Meta) {
2906
-                return $result->value();
2907
-            }
2908
-        } else {
2909
-            $results = $this->get_many_related(
2910
-                'Extra_Meta',
2911
-                array(array('EXM_key' => $meta_key))
2912
-            );
2913
-            if ($results) {
2914
-                $values = array();
2915
-                foreach ($results as $result) {
2916
-                    if ($result instanceof EE_Extra_Meta) {
2917
-                        $values[ $result->ID() ] = $result->value();
2918
-                    }
2919
-                }
2920
-                return $values;
2921
-            }
2922
-        }
2923
-        //if nothing discovered yet return default.
2924
-        return apply_filters(
2925
-            'FHEE__EE_Base_Class__get_extra_meta__default_value',
2926
-            $default,
2927
-            $meta_key,
2928
-            $single,
2929
-            $this
2930
-        );
2931
-    }
2932
-
2933
-
2934
-    /**
2935
-     * Returns a simple array of all the extra meta associated with this model object.
2936
-     * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2937
-     * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2938
-     * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2939
-     * If $one_of_each_key is false, it will return an array with the top-level keys being
2940
-     * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2941
-     * finally the extra meta's value as each sub-value. (eg
2942
-     * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2943
-     *
2944
-     * @param boolean $one_of_each_key
2945
-     * @return array
2946
-     * @throws ReflectionException
2947
-     * @throws InvalidArgumentException
2948
-     * @throws InvalidInterfaceException
2949
-     * @throws InvalidDataTypeException
2950
-     * @throws EE_Error
2951
-     */
2952
-    public function all_extra_meta_array($one_of_each_key = true)
2953
-    {
2954
-        $return_array = array();
2955
-        if ($one_of_each_key) {
2956
-            $extra_meta_objs = $this->get_many_related(
2957
-                'Extra_Meta',
2958
-                array('group_by' => 'EXM_key')
2959
-            );
2960
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2961
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2962
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2963
-                }
2964
-            }
2965
-        } else {
2966
-            $extra_meta_objs = $this->get_many_related('Extra_Meta');
2967
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2968
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2969
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2970
-                        $return_array[ $extra_meta_obj->key() ] = array();
2971
-                    }
2972
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2973
-                }
2974
-            }
2975
-        }
2976
-        return $return_array;
2977
-    }
2978
-
2979
-
2980
-    /**
2981
-     * Gets a pretty nice displayable nice for this model object. Often overridden
2982
-     *
2983
-     * @return string
2984
-     * @throws ReflectionException
2985
-     * @throws InvalidArgumentException
2986
-     * @throws InvalidInterfaceException
2987
-     * @throws InvalidDataTypeException
2988
-     * @throws EE_Error
2989
-     */
2990
-    public function name()
2991
-    {
2992
-        //find a field that's not a text field
2993
-        $field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
2994
-        if ($field_we_can_use) {
2995
-            return $this->get($field_we_can_use->get_name());
2996
-        }
2997
-        $first_few_properties = $this->model_field_array();
2998
-        $first_few_properties = array_slice($first_few_properties, 0, 3);
2999
-        $name_parts           = array();
3000
-        foreach ($first_few_properties as $name => $value) {
3001
-            $name_parts[] = "$name:$value";
3002
-        }
3003
-        return implode(',', $name_parts);
3004
-    }
3005
-
3006
-
3007
-    /**
3008
-     * in_entity_map
3009
-     * Checks if this model object has been proven to already be in the entity map
3010
-     *
3011
-     * @return boolean
3012
-     * @throws ReflectionException
3013
-     * @throws InvalidArgumentException
3014
-     * @throws InvalidInterfaceException
3015
-     * @throws InvalidDataTypeException
3016
-     * @throws EE_Error
3017
-     */
3018
-    public function in_entity_map()
3019
-    {
3020
-        // well, if we looked, did we find it in the entity map?
3021
-        return $this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this;
3022
-    }
3023
-
3024
-
3025
-    /**
3026
-     * refresh_from_db
3027
-     * Makes sure the fields and values on this model object are in-sync with what's in the database.
3028
-     *
3029
-     * @throws ReflectionException
3030
-     * @throws InvalidArgumentException
3031
-     * @throws InvalidInterfaceException
3032
-     * @throws InvalidDataTypeException
3033
-     * @throws EE_Error if this model object isn't in the entity mapper (because then you should
3034
-     * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
3035
-     */
3036
-    public function refresh_from_db()
3037
-    {
3038
-        if ($this->ID() && $this->in_entity_map()) {
3039
-            $this->get_model()->refresh_entity_map_from_db($this->ID());
3040
-        } else {
3041
-            //if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
3042
-            //if it has an ID but it's not in the map, and you're asking me to refresh it
3043
-            //that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
3044
-            //absolutely nothing in it for this ID
3045
-            if (WP_DEBUG) {
3046
-                throw new EE_Error(
3047
-                    sprintf(
3048
-                        esc_html__('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3049
-                            'event_espresso'),
3050
-                        $this->ID(),
3051
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3052
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3053
-                    )
3054
-                );
3055
-            }
3056
-        }
3057
-    }
3058
-
3059
-
3060
-    /**
3061
-     * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3062
-     * (probably a bad assumption they have made, oh well)
3063
-     *
3064
-     * @return string
3065
-     */
3066
-    public function __toString()
3067
-    {
3068
-        try {
3069
-            return sprintf('%s (%s)', $this->name(), $this->ID());
3070
-        } catch (Exception $e) {
3071
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3072
-            return '';
3073
-        }
3074
-    }
3075
-
3076
-
3077
-    /**
3078
-     * Clear related model objects if they're already in the DB, because otherwise when we
3079
-     * UN-serialize this model object we'll need to be careful to add them to the entity map.
3080
-     * This means if we have made changes to those related model objects, and want to unserialize
3081
-     * the this model object on a subsequent request, changes to those related model objects will be lost.
3082
-     * Instead, those related model objects should be directly serialized and stored.
3083
-     * Eg, the following won't work:
3084
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3085
-     * $att = $reg->attendee();
3086
-     * $att->set( 'ATT_fname', 'Dirk' );
3087
-     * update_option( 'my_option', serialize( $reg ) );
3088
-     * //END REQUEST
3089
-     * //START NEXT REQUEST
3090
-     * $reg = get_option( 'my_option' );
3091
-     * $reg->attendee()->save();
3092
-     * And would need to be replace with:
3093
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3094
-     * $att = $reg->attendee();
3095
-     * $att->set( 'ATT_fname', 'Dirk' );
3096
-     * update_option( 'my_option', serialize( $reg ) );
3097
-     * //END REQUEST
3098
-     * //START NEXT REQUEST
3099
-     * $att = get_option( 'my_option' );
3100
-     * $att->save();
3101
-     *
3102
-     * @return array
3103
-     * @throws ReflectionException
3104
-     * @throws InvalidArgumentException
3105
-     * @throws InvalidInterfaceException
3106
-     * @throws InvalidDataTypeException
3107
-     * @throws EE_Error
3108
-     */
3109
-    public function __sleep()
3110
-    {
3111
-        $model = $this->get_model();
3112
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3113
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
3114
-                $classname = 'EE_' . $model->get_this_model_name();
3115
-                if (
3116
-                    $this->get_one_from_cache($relation_name) instanceof $classname
3117
-                    && $this->get_one_from_cache($relation_name)->ID()
3118
-                ) {
3119
-                    $this->clear_cache(
3120
-                        $relation_name,
3121
-                        $this->get_one_from_cache($relation_name)->ID()
3122
-                    );
3123
-                }
3124
-            }
3125
-        }
3126
-        $this->_props_n_values_provided_in_constructor = array();
3127
-        $properties_to_serialize                       = get_object_vars($this);
3128
-        //don't serialize the model. It's big and that risks recursion
3129
-        unset($properties_to_serialize['_model']);
3130
-        return array_keys($properties_to_serialize);
3131
-    }
3132
-
3133
-
3134
-    /**
3135
-     * restore _props_n_values_provided_in_constructor
3136
-     * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3137
-     * and therefore should NOT be used to determine if state change has occurred since initial construction.
3138
-     * At best, you would only be able to detect if state change has occurred during THIS request.
3139
-     */
3140
-    public function __wakeup()
3141
-    {
3142
-        $this->_props_n_values_provided_in_constructor = $this->_fields;
3143
-    }
3144
-
3145
-
3146
-    /**
3147
-     * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3148
-     * distinct with the clone host instance are also cloned.
3149
-     */
3150
-    public function __clone()
3151
-    {
3152
-        //handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3153
-        foreach ($this->_fields as $field => $value) {
3154
-            if ($value instanceof DateTime) {
3155
-                $this->_fields[$field] = clone $value;
3156
-            }
3157
-        }
3158
-    }
18
+	/**
19
+	 * This is an array of the original properties and values provided during construction
20
+	 * of this model object. (keys are model field names, values are their values).
21
+	 * This list is important to remember so that when we are merging data from the db, we know
22
+	 * which values to override and which to not override.
23
+	 *
24
+	 * @var array
25
+	 */
26
+	protected $_props_n_values_provided_in_constructor;
27
+
28
+	/**
29
+	 * Timezone
30
+	 * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
31
+	 * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
32
+	 * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
33
+	 * access to it.
34
+	 *
35
+	 * @var string
36
+	 */
37
+	protected $_timezone;
38
+
39
+	/**
40
+	 * date format
41
+	 * pattern or format for displaying dates
42
+	 *
43
+	 * @var string $_dt_frmt
44
+	 */
45
+	protected $_dt_frmt;
46
+
47
+	/**
48
+	 * time format
49
+	 * pattern or format for displaying time
50
+	 *
51
+	 * @var string $_tm_frmt
52
+	 */
53
+	protected $_tm_frmt;
54
+
55
+	/**
56
+	 * This property is for holding a cached array of object properties indexed by property name as the key.
57
+	 * The purpose of this is for setting a cache on properties that may have calculated values after a
58
+	 * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
59
+	 * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
60
+	 *
61
+	 * @var array
62
+	 */
63
+	protected $_cached_properties = array();
64
+
65
+	/**
66
+	 * An array containing keys of the related model, and values are either an array of related mode objects or a
67
+	 * single
68
+	 * related model object. see the model's _model_relations. The keys should match those specified. And if the
69
+	 * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
70
+	 * all others have an array)
71
+	 *
72
+	 * @var array
73
+	 */
74
+	protected $_model_relations = array();
75
+
76
+	/**
77
+	 * Array where keys are field names (see the model's _fields property) and values are their values. To see what
78
+	 * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
79
+	 *
80
+	 * @var array
81
+	 */
82
+	protected $_fields = array();
83
+
84
+	/**
85
+	 * @var boolean indicating whether or not this model object is intended to ever be saved
86
+	 * For example, we might create model objects intended to only be used for the duration
87
+	 * of this request and to be thrown away, and if they were accidentally saved
88
+	 * it would be a bug.
89
+	 */
90
+	protected $_allow_persist = true;
91
+
92
+	/**
93
+	 * @var boolean indicating whether or not this model object's properties have changed since construction
94
+	 */
95
+	protected $_has_changes = false;
96
+
97
+	/**
98
+	 * @var EEM_Base
99
+	 */
100
+	protected $_model;
101
+
102
+	/**
103
+	 * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
104
+	 * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
105
+	 * the db.  They also do not automatically update if there are any changes to the data that produced their results.
106
+	 * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
107
+	 * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
108
+	 * array as:
109
+	 * array(
110
+	 *  'Registration_Count' => 24
111
+	 * );
112
+	 * Note: if the custom select configuration for the query included a data type, the value will be in the data type
113
+	 * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
114
+	 * info)
115
+	 *
116
+	 * @var array
117
+	 */
118
+	protected $custom_selection_results = array();
119
+
120
+
121
+	/**
122
+	 * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
123
+	 * play nice
124
+	 *
125
+	 * @param array   $fieldValues                             where each key is a field (ie, array key in the 2nd
126
+	 *                                                         layer of the model's _fields array, (eg, EVT_ID,
127
+	 *                                                         TXN_amount, QST_name, etc) and values are their values
128
+	 * @param boolean $bydb                                    a flag for setting if the class is instantiated by the
129
+	 *                                                         corresponding db model or not.
130
+	 * @param string  $timezone                                indicate what timezone you want any datetime fields to
131
+	 *                                                         be in when instantiating a EE_Base_Class object.
132
+	 * @param array   $date_formats                            An array of date formats to set on construct where first
133
+	 *                                                         value is the date_format and second value is the time
134
+	 *                                                         format.
135
+	 * @throws InvalidArgumentException
136
+	 * @throws InvalidInterfaceException
137
+	 * @throws InvalidDataTypeException
138
+	 * @throws EE_Error
139
+	 * @throws ReflectionException
140
+	 */
141
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '', $date_formats = array())
142
+	{
143
+		$className = get_class($this);
144
+		do_action("AHEE__{$className}__construct", $this, $fieldValues);
145
+		$model        = $this->get_model();
146
+		$model_fields = $model->field_settings(false);
147
+		// ensure $fieldValues is an array
148
+		$fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
149
+		// verify client code has not passed any invalid field names
150
+		foreach ($fieldValues as $field_name => $field_value) {
151
+			if (! isset($model_fields[ $field_name ])) {
152
+				throw new EE_Error(
153
+					sprintf(
154
+						esc_html__(
155
+							'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
156
+							'event_espresso'
157
+						),
158
+						$field_name,
159
+						get_class($this),
160
+						implode(', ', array_keys($model_fields))
161
+					)
162
+				);
163
+			}
164
+		}
165
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
166
+		if (! empty($date_formats) && is_array($date_formats)) {
167
+			list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
168
+		} else {
169
+			//set default formats for date and time
170
+			$this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
171
+			$this->_tm_frmt = (string) get_option('time_format', 'g:i a');
172
+		}
173
+		//if db model is instantiating
174
+		if ($bydb) {
175
+			//client code has indicated these field values are from the database
176
+			foreach ($model_fields as $fieldName => $field) {
177
+				$this->set_from_db(
178
+					$fieldName,
179
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
180
+				);
181
+			}
182
+		} else {
183
+			//we're constructing a brand
184
+			//new instance of the model object. Generally, this means we'll need to do more field validation
185
+			foreach ($model_fields as $fieldName => $field) {
186
+				$this->set(
187
+					$fieldName,
188
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null, true
189
+				);
190
+			}
191
+		}
192
+		//remember what values were passed to this constructor
193
+		$this->_props_n_values_provided_in_constructor = $fieldValues;
194
+		//remember in entity mapper
195
+		if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
196
+			$model->add_to_entity_map($this);
197
+		}
198
+		//setup all the relations
199
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
200
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
201
+				$this->_model_relations[ $relation_name ] = null;
202
+			} else {
203
+				$this->_model_relations[ $relation_name ] = array();
204
+			}
205
+		}
206
+		/**
207
+		 * Action done at the end of each model object construction
208
+		 *
209
+		 * @param EE_Base_Class $this the model object just created
210
+		 */
211
+		do_action('AHEE__EE_Base_Class__construct__finished', $this);
212
+	}
213
+
214
+
215
+	/**
216
+	 * Gets whether or not this model object is allowed to persist/be saved to the database.
217
+	 *
218
+	 * @return boolean
219
+	 */
220
+	public function allow_persist()
221
+	{
222
+		return $this->_allow_persist;
223
+	}
224
+
225
+
226
+	/**
227
+	 * Sets whether or not this model object should be allowed to be saved to the DB.
228
+	 * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
229
+	 * you got new information that somehow made you change your mind.
230
+	 *
231
+	 * @param boolean $allow_persist
232
+	 * @return boolean
233
+	 */
234
+	public function set_allow_persist($allow_persist)
235
+	{
236
+		return $this->_allow_persist = $allow_persist;
237
+	}
238
+
239
+
240
+	/**
241
+	 * Gets the field's original value when this object was constructed during this request.
242
+	 * This can be helpful when determining if a model object has changed or not
243
+	 *
244
+	 * @param string $field_name
245
+	 * @return mixed|null
246
+	 * @throws ReflectionException
247
+	 * @throws InvalidArgumentException
248
+	 * @throws InvalidInterfaceException
249
+	 * @throws InvalidDataTypeException
250
+	 * @throws EE_Error
251
+	 */
252
+	public function get_original($field_name)
253
+	{
254
+		if (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
255
+			&& $field_settings = $this->get_model()->field_settings_for($field_name)
256
+		) {
257
+			return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
258
+		}
259
+		return null;
260
+	}
261
+
262
+
263
+	/**
264
+	 * @param EE_Base_Class $obj
265
+	 * @return string
266
+	 */
267
+	public function get_class($obj)
268
+	{
269
+		return get_class($obj);
270
+	}
271
+
272
+
273
+	/**
274
+	 * Overrides parent because parent expects old models.
275
+	 * This also doesn't do any validation, and won't work for serialized arrays
276
+	 *
277
+	 * @param    string $field_name
278
+	 * @param    mixed  $field_value
279
+	 * @param bool      $use_default
280
+	 * @throws InvalidArgumentException
281
+	 * @throws InvalidInterfaceException
282
+	 * @throws InvalidDataTypeException
283
+	 * @throws EE_Error
284
+	 * @throws ReflectionException
285
+	 * @throws ReflectionException
286
+	 * @throws ReflectionException
287
+	 */
288
+	public function set($field_name, $field_value, $use_default = false)
289
+	{
290
+		// if not using default and nothing has changed, and object has already been setup (has ID),
291
+		// then don't do anything
292
+		if (
293
+			! $use_default
294
+			&& $this->_fields[ $field_name ] === $field_value
295
+			&& $this->ID()
296
+		) {
297
+			return;
298
+		}
299
+		$model              = $this->get_model();
300
+		$this->_has_changes = true;
301
+		$field_obj          = $model->field_settings_for($field_name);
302
+		if ($field_obj instanceof EE_Model_Field_Base) {
303
+			//			if ( method_exists( $field_obj, 'set_timezone' )) {
304
+			if ($field_obj instanceof EE_Datetime_Field) {
305
+				$field_obj->set_timezone($this->_timezone);
306
+				$field_obj->set_date_format($this->_dt_frmt);
307
+				$field_obj->set_time_format($this->_tm_frmt);
308
+			}
309
+			$holder_of_value = $field_obj->prepare_for_set($field_value);
310
+			//should the value be null?
311
+			if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
+				$this->_fields[ $field_name ] = $field_obj->get_default_value();
313
+				/**
314
+				 * To save having to refactor all the models, if a default value is used for a
315
+				 * EE_Datetime_Field, and that value is not null nor is it a DateTime
316
+				 * object.  Then let's do a set again to ensure that it becomes a DateTime
317
+				 * object.
318
+				 *
319
+				 * @since 4.6.10+
320
+				 */
321
+				if (
322
+					$field_obj instanceof EE_Datetime_Field
323
+					&& $this->_fields[ $field_name ] !== null
324
+					&& ! $this->_fields[ $field_name ] instanceof DateTime
325
+				) {
326
+					empty($this->_fields[ $field_name ])
327
+						? $this->set($field_name, time())
328
+						: $this->set($field_name, $this->_fields[ $field_name ]);
329
+				}
330
+			} else {
331
+				$this->_fields[ $field_name ] = $holder_of_value;
332
+			}
333
+			//if we're not in the constructor...
334
+			//now check if what we set was a primary key
335
+			if (
336
+				//note: props_n_values_provided_in_constructor is only set at the END of the constructor
337
+				$this->_props_n_values_provided_in_constructor
338
+				&& $field_value
339
+				&& $field_name === $model->primary_key_name()
340
+			) {
341
+				//if so, we want all this object's fields to be filled either with
342
+				//what we've explicitly set on this model
343
+				//or what we have in the db
344
+				// echo "setting primary key!";
345
+				$fields_on_model = self::_get_model(get_class($this))->field_settings();
346
+				$obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347
+				foreach ($fields_on_model as $field_obj) {
348
+					if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
349
+						&& $field_obj->get_name() !== $field_name
350
+					) {
351
+						$this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
352
+					}
353
+				}
354
+				//oh this model object has an ID? well make sure its in the entity mapper
355
+				$model->add_to_entity_map($this);
356
+			}
357
+			//let's unset any cache for this field_name from the $_cached_properties property.
358
+			$this->_clear_cached_property($field_name);
359
+		} else {
360
+			throw new EE_Error(
361
+				sprintf(
362
+					esc_html__(
363
+						'A valid EE_Model_Field_Base could not be found for the given field name: %s',
364
+						'event_espresso'
365
+					),
366
+					$field_name
367
+				)
368
+			);
369
+		}
370
+	}
371
+
372
+
373
+	/**
374
+	 * Set custom select values for model.
375
+	 *
376
+	 * @param array $custom_select_values
377
+	 */
378
+	public function setCustomSelectsValues(array $custom_select_values)
379
+	{
380
+		$this->custom_selection_results = $custom_select_values;
381
+	}
382
+
383
+
384
+	/**
385
+	 * Returns the custom select value for the provided alias if its set.
386
+	 * If not set, returns null.
387
+	 *
388
+	 * @param string $alias
389
+	 * @return string|int|float|null
390
+	 */
391
+	public function getCustomSelect($alias)
392
+	{
393
+		return isset($this->custom_selection_results[ $alias ])
394
+			? $this->custom_selection_results[ $alias ]
395
+			: null;
396
+	}
397
+
398
+
399
+	/**
400
+	 * This sets the field value on the db column if it exists for the given $column_name or
401
+	 * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
402
+	 *
403
+	 * @see EE_message::get_column_value for related documentation on the necessity of this method.
404
+	 * @param string $field_name  Must be the exact column name.
405
+	 * @param mixed  $field_value The value to set.
406
+	 * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
407
+	 * @throws InvalidArgumentException
408
+	 * @throws InvalidInterfaceException
409
+	 * @throws InvalidDataTypeException
410
+	 * @throws EE_Error
411
+	 * @throws ReflectionException
412
+	 */
413
+	public function set_field_or_extra_meta($field_name, $field_value)
414
+	{
415
+		if ($this->get_model()->has_field($field_name)) {
416
+			$this->set($field_name, $field_value);
417
+			return true;
418
+		}
419
+		//ensure this object is saved first so that extra meta can be properly related.
420
+		$this->save();
421
+		return $this->update_extra_meta($field_name, $field_value);
422
+	}
423
+
424
+
425
+	/**
426
+	 * This retrieves the value of the db column set on this class or if that's not present
427
+	 * it will attempt to retrieve from extra_meta if found.
428
+	 * Example Usage:
429
+	 * Via EE_Message child class:
430
+	 * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
431
+	 * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
432
+	 * also have additional main fields specific to the messenger.  The system accommodates those extra
433
+	 * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
434
+	 * value for those extra fields dynamically via the EE_message object.
435
+	 *
436
+	 * @param  string $field_name expecting the fully qualified field name.
437
+	 * @return mixed|null  value for the field if found.  null if not found.
438
+	 * @throws ReflectionException
439
+	 * @throws InvalidArgumentException
440
+	 * @throws InvalidInterfaceException
441
+	 * @throws InvalidDataTypeException
442
+	 * @throws EE_Error
443
+	 */
444
+	public function get_field_or_extra_meta($field_name)
445
+	{
446
+		if ($this->get_model()->has_field($field_name)) {
447
+			$column_value = $this->get($field_name);
448
+		} else {
449
+			//This isn't a column in the main table, let's see if it is in the extra meta.
450
+			$column_value = $this->get_extra_meta($field_name, true, null);
451
+		}
452
+		return $column_value;
453
+	}
454
+
455
+
456
+	/**
457
+	 * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
458
+	 * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
459
+	 * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
460
+	 * available to all child classes that may be using the EE_Datetime_Field for a field data type.
461
+	 *
462
+	 * @access public
463
+	 * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
464
+	 * @return void
465
+	 * @throws InvalidArgumentException
466
+	 * @throws InvalidInterfaceException
467
+	 * @throws InvalidDataTypeException
468
+	 * @throws EE_Error
469
+	 * @throws ReflectionException
470
+	 */
471
+	public function set_timezone($timezone = '')
472
+	{
473
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
474
+		//make sure we clear all cached properties because they won't be relevant now
475
+		$this->_clear_cached_properties();
476
+		//make sure we update field settings and the date for all EE_Datetime_Fields
477
+		$model_fields = $this->get_model()->field_settings(false);
478
+		foreach ($model_fields as $field_name => $field_obj) {
479
+			if ($field_obj instanceof EE_Datetime_Field) {
480
+				$field_obj->set_timezone($this->_timezone);
481
+				if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
482
+					EEH_DTT_Helper::setTimezone($this->_fields[$field_name], new DateTimeZone($this->_timezone));
483
+				}
484
+			}
485
+		}
486
+	}
487
+
488
+
489
+	/**
490
+	 * This just returns whatever is set for the current timezone.
491
+	 *
492
+	 * @access public
493
+	 * @return string timezone string
494
+	 */
495
+	public function get_timezone()
496
+	{
497
+		return $this->_timezone;
498
+	}
499
+
500
+
501
+	/**
502
+	 * This sets the internal date format to what is sent in to be used as the new default for the class
503
+	 * internally instead of wp set date format options
504
+	 *
505
+	 * @since 4.6
506
+	 * @param string $format should be a format recognizable by PHP date() functions.
507
+	 */
508
+	public function set_date_format($format)
509
+	{
510
+		$this->_dt_frmt = $format;
511
+		//clear cached_properties because they won't be relevant now.
512
+		$this->_clear_cached_properties();
513
+	}
514
+
515
+
516
+	/**
517
+	 * This sets the internal time format string to what is sent in to be used as the new default for the
518
+	 * class internally instead of wp set time format options.
519
+	 *
520
+	 * @since 4.6
521
+	 * @param string $format should be a format recognizable by PHP date() functions.
522
+	 */
523
+	public function set_time_format($format)
524
+	{
525
+		$this->_tm_frmt = $format;
526
+		//clear cached_properties because they won't be relevant now.
527
+		$this->_clear_cached_properties();
528
+	}
529
+
530
+
531
+	/**
532
+	 * This returns the current internal set format for the date and time formats.
533
+	 *
534
+	 * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
535
+	 *                             where the first value is the date format and the second value is the time format.
536
+	 * @return mixed string|array
537
+	 */
538
+	public function get_format($full = true)
539
+	{
540
+		return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
541
+	}
542
+
543
+
544
+	/**
545
+	 * cache
546
+	 * stores the passed model object on the current model object.
547
+	 * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
548
+	 *
549
+	 * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
550
+	 *                                       'Registration' associated with this model object
551
+	 * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
552
+	 *                                       that could be a payment or a registration)
553
+	 * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
554
+	 *                                       items which will be stored in an array on this object
555
+	 * @throws ReflectionException
556
+	 * @throws InvalidArgumentException
557
+	 * @throws InvalidInterfaceException
558
+	 * @throws InvalidDataTypeException
559
+	 * @throws EE_Error
560
+	 * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
561
+	 *                                       related thing, no array)
562
+	 */
563
+	public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
564
+	{
565
+		// its entirely possible that there IS no related object yet in which case there is nothing to cache.
566
+		if (! $object_to_cache instanceof EE_Base_Class) {
567
+			return false;
568
+		}
569
+		// also get "how" the object is related, or throw an error
570
+		if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
571
+			throw new EE_Error(
572
+				sprintf(
573
+					esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
574
+					$relationName,
575
+					get_class($this)
576
+				)
577
+			);
578
+		}
579
+		// how many things are related ?
580
+		if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
581
+			// if it's a "belongs to" relationship, then there's only one related model object
582
+			// eg, if this is a registration, there's only 1 attendee for it
583
+			// so for these model objects just set it to be cached
584
+			$this->_model_relations[ $relationName ] = $object_to_cache;
585
+			$return                                  = true;
586
+		} else {
587
+			// otherwise, this is the "many" side of a one to many relationship,
588
+			// so we'll add the object to the array of related objects for that type.
589
+			// eg: if this is an event, there are many registrations for that event,
590
+			// so we cache the registrations in an array
591
+			if (! is_array($this->_model_relations[ $relationName ])) {
592
+				// if for some reason, the cached item is a model object,
593
+				// then stick that in the array, otherwise start with an empty array
594
+				$this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
595
+														   instanceof
596
+														   EE_Base_Class
597
+					? array($this->_model_relations[ $relationName ]) : array();
598
+			}
599
+			// first check for a cache_id which is normally empty
600
+			if (! empty($cache_id)) {
601
+				// if the cache_id exists, then it means we are purposely trying to cache this
602
+				// with a known key that can then be used to retrieve the object later on
603
+				$this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
604
+				$return                                               = $cache_id;
605
+			} elseif ($object_to_cache->ID()) {
606
+				// OR the cached object originally came from the db, so let's just use it's PK for an ID
607
+				$this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
608
+				$return                                                            = $object_to_cache->ID();
609
+			} else {
610
+				// OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
611
+				$this->_model_relations[ $relationName ][] = $object_to_cache;
612
+				// move the internal pointer to the end of the array
613
+				end($this->_model_relations[ $relationName ]);
614
+				// and grab the key so that we can return it
615
+				$return = key($this->_model_relations[ $relationName ]);
616
+			}
617
+		}
618
+		return $return;
619
+	}
620
+
621
+
622
+	/**
623
+	 * For adding an item to the cached_properties property.
624
+	 *
625
+	 * @access protected
626
+	 * @param string      $fieldname the property item the corresponding value is for.
627
+	 * @param mixed       $value     The value we are caching.
628
+	 * @param string|null $cache_type
629
+	 * @return void
630
+	 * @throws ReflectionException
631
+	 * @throws InvalidArgumentException
632
+	 * @throws InvalidInterfaceException
633
+	 * @throws InvalidDataTypeException
634
+	 * @throws EE_Error
635
+	 */
636
+	protected function _set_cached_property($fieldname, $value, $cache_type = null)
637
+	{
638
+		//first make sure this property exists
639
+		$this->get_model()->field_settings_for($fieldname);
640
+		$cache_type                                            = empty($cache_type) ? 'standard' : $cache_type;
641
+		$this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
642
+	}
643
+
644
+
645
+	/**
646
+	 * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
647
+	 * This also SETS the cache if we return the actual property!
648
+	 *
649
+	 * @param string $fieldname        the name of the property we're trying to retrieve
650
+	 * @param bool   $pretty
651
+	 * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
652
+	 *                                 (in cases where the same property may be used for different outputs
653
+	 *                                 - i.e. datetime, money etc.)
654
+	 *                                 It can also accept certain pre-defined "schema" strings
655
+	 *                                 to define how to output the property.
656
+	 *                                 see the field's prepare_for_pretty_echoing for what strings can be used
657
+	 * @return mixed                   whatever the value for the property is we're retrieving
658
+	 * @throws ReflectionException
659
+	 * @throws InvalidArgumentException
660
+	 * @throws InvalidInterfaceException
661
+	 * @throws InvalidDataTypeException
662
+	 * @throws EE_Error
663
+	 */
664
+	protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
665
+	{
666
+		//verify the field exists
667
+		$model = $this->get_model();
668
+		$model->field_settings_for($fieldname);
669
+		$cache_type = $pretty ? 'pretty' : 'standard';
670
+		$cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
671
+		if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
672
+			return $this->_cached_properties[ $fieldname ][ $cache_type ];
673
+		}
674
+		$value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
675
+		$this->_set_cached_property($fieldname, $value, $cache_type);
676
+		return $value;
677
+	}
678
+
679
+
680
+	/**
681
+	 * If the cache didn't fetch the needed item, this fetches it.
682
+	 *
683
+	 * @param string $fieldname
684
+	 * @param bool   $pretty
685
+	 * @param string $extra_cache_ref
686
+	 * @return mixed
687
+	 * @throws InvalidArgumentException
688
+	 * @throws InvalidInterfaceException
689
+	 * @throws InvalidDataTypeException
690
+	 * @throws EE_Error
691
+	 * @throws ReflectionException
692
+	 */
693
+	protected function _get_fresh_property($fieldname, $pretty = false, $extra_cache_ref = null)
694
+	{
695
+		$field_obj = $this->get_model()->field_settings_for($fieldname);
696
+		// If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
697
+		if ($field_obj instanceof EE_Datetime_Field) {
698
+			$this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
699
+		}
700
+		if (! isset($this->_fields[ $fieldname ])) {
701
+			$this->_fields[ $fieldname ] = null;
702
+		}
703
+		$value = $pretty
704
+			? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
705
+			: $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
706
+		return $value;
707
+	}
708
+
709
+
710
+	/**
711
+	 * set timezone, formats, and output for EE_Datetime_Field objects
712
+	 *
713
+	 * @param \EE_Datetime_Field $datetime_field
714
+	 * @param bool               $pretty
715
+	 * @param null               $date_or_time
716
+	 * @return void
717
+	 * @throws InvalidArgumentException
718
+	 * @throws InvalidInterfaceException
719
+	 * @throws InvalidDataTypeException
720
+	 * @throws EE_Error
721
+	 */
722
+	protected function _prepare_datetime_field(
723
+		EE_Datetime_Field $datetime_field,
724
+		$pretty = false,
725
+		$date_or_time = null
726
+	) {
727
+		$datetime_field->set_timezone($this->_timezone);
728
+		$datetime_field->set_date_format($this->_dt_frmt, $pretty);
729
+		$datetime_field->set_time_format($this->_tm_frmt, $pretty);
730
+		//set the output returned
731
+		switch ($date_or_time) {
732
+			case 'D' :
733
+				$datetime_field->set_date_time_output('date');
734
+				break;
735
+			case 'T' :
736
+				$datetime_field->set_date_time_output('time');
737
+				break;
738
+			default :
739
+				$datetime_field->set_date_time_output();
740
+		}
741
+	}
742
+
743
+
744
+	/**
745
+	 * This just takes care of clearing out the cached_properties
746
+	 *
747
+	 * @return void
748
+	 */
749
+	protected function _clear_cached_properties()
750
+	{
751
+		$this->_cached_properties = array();
752
+	}
753
+
754
+
755
+	/**
756
+	 * This just clears out ONE property if it exists in the cache
757
+	 *
758
+	 * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
759
+	 * @return void
760
+	 */
761
+	protected function _clear_cached_property($property_name)
762
+	{
763
+		if (isset($this->_cached_properties[ $property_name ])) {
764
+			unset($this->_cached_properties[ $property_name ]);
765
+		}
766
+	}
767
+
768
+
769
+	/**
770
+	 * Ensures that this related thing is a model object.
771
+	 *
772
+	 * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
773
+	 * @param string $model_name   name of the related thing, eg 'Attendee',
774
+	 * @return EE_Base_Class
775
+	 * @throws ReflectionException
776
+	 * @throws InvalidArgumentException
777
+	 * @throws InvalidInterfaceException
778
+	 * @throws InvalidDataTypeException
779
+	 * @throws EE_Error
780
+	 */
781
+	protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
782
+	{
783
+		$other_model_instance = self::_get_model_instance_with_name(
784
+			self::_get_model_classname($model_name),
785
+			$this->_timezone
786
+		);
787
+		return $other_model_instance->ensure_is_obj($object_or_id);
788
+	}
789
+
790
+
791
+	/**
792
+	 * Forgets the cached model of the given relation Name. So the next time we request it,
793
+	 * we will fetch it again from the database. (Handy if you know it's changed somehow).
794
+	 * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
795
+	 * then only remove that one object from our cached array. Otherwise, clear the entire list
796
+	 *
797
+	 * @param string $relationName                         one of the keys in the _model_relations array on the model.
798
+	 *                                                     Eg 'Registration'
799
+	 * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
800
+	 *                                                     if you intend to use $clear_all = TRUE, or the relation only
801
+	 *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
802
+	 * @param bool   $clear_all                            This flags clearing the entire cache relation property if
803
+	 *                                                     this is HasMany or HABTM.
804
+	 * @throws ReflectionException
805
+	 * @throws InvalidArgumentException
806
+	 * @throws InvalidInterfaceException
807
+	 * @throws InvalidDataTypeException
808
+	 * @throws EE_Error
809
+	 * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
810
+	 *                                                     relation from all
811
+	 */
812
+	public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
813
+	{
814
+		$relationship_to_model = $this->get_model()->related_settings_for($relationName);
815
+		$index_in_cache        = '';
816
+		if (! $relationship_to_model) {
817
+			throw new EE_Error(
818
+				sprintf(
819
+					esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
820
+					$relationName,
821
+					get_class($this)
822
+				)
823
+			);
824
+		}
825
+		if ($clear_all) {
826
+			$obj_removed                             = true;
827
+			$this->_model_relations[ $relationName ] = null;
828
+		} elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
829
+			$obj_removed                             = $this->_model_relations[ $relationName ];
830
+			$this->_model_relations[ $relationName ] = null;
831
+		} else {
832
+			if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
833
+				&& $object_to_remove_or_index_into_array->ID()
834
+			) {
835
+				$index_in_cache = $object_to_remove_or_index_into_array->ID();
836
+				if (is_array($this->_model_relations[ $relationName ])
837
+					&& ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
838
+				) {
839
+					$index_found_at = null;
840
+					//find this object in the array even though it has a different key
841
+					foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
842
+						/** @noinspection TypeUnsafeComparisonInspection */
843
+						if (
844
+							$obj instanceof EE_Base_Class
845
+							&& (
846
+								$obj == $object_to_remove_or_index_into_array
847
+								|| $obj->ID() === $object_to_remove_or_index_into_array->ID()
848
+							)
849
+						) {
850
+							$index_found_at = $index;
851
+							break;
852
+						}
853
+					}
854
+					if ($index_found_at) {
855
+						$index_in_cache = $index_found_at;
856
+					} else {
857
+						//it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
858
+						//if it wasn't in it to begin with. So we're done
859
+						return $object_to_remove_or_index_into_array;
860
+					}
861
+				}
862
+			} elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
863
+				//so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
864
+				foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
865
+					/** @noinspection TypeUnsafeComparisonInspection */
866
+					if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
867
+						$index_in_cache = $index;
868
+					}
869
+				}
870
+			} else {
871
+				$index_in_cache = $object_to_remove_or_index_into_array;
872
+			}
873
+			//supposedly we've found it. But it could just be that the client code
874
+			//provided a bad index/object
875
+			if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
876
+				$obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
877
+				unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
878
+			} else {
879
+				//that thing was never cached anyways.
880
+				$obj_removed = null;
881
+			}
882
+		}
883
+		return $obj_removed;
884
+	}
885
+
886
+
887
+	/**
888
+	 * update_cache_after_object_save
889
+	 * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
890
+	 * obtained after being saved to the db
891
+	 *
892
+	 * @param string        $relationName       - the type of object that is cached
893
+	 * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
894
+	 * @param string        $current_cache_id   - the ID that was used when originally caching the object
895
+	 * @return boolean TRUE on success, FALSE on fail
896
+	 * @throws ReflectionException
897
+	 * @throws InvalidArgumentException
898
+	 * @throws InvalidInterfaceException
899
+	 * @throws InvalidDataTypeException
900
+	 * @throws EE_Error
901
+	 */
902
+	public function update_cache_after_object_save(
903
+		$relationName,
904
+		EE_Base_Class $newly_saved_object,
905
+		$current_cache_id = ''
906
+	) {
907
+		// verify that incoming object is of the correct type
908
+		$obj_class = 'EE_' . $relationName;
909
+		if ($newly_saved_object instanceof $obj_class) {
910
+			/* @type EE_Base_Class $newly_saved_object */
911
+			// now get the type of relation
912
+			$relationship_to_model = $this->get_model()->related_settings_for($relationName);
913
+			// if this is a 1:1 relationship
914
+			if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
915
+				// then just replace the cached object with the newly saved object
916
+				$this->_model_relations[ $relationName ] = $newly_saved_object;
917
+				return true;
918
+				// or if it's some kind of sordid feral polyamorous relationship...
919
+			}
920
+			if (is_array($this->_model_relations[ $relationName ])
921
+					  && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
922
+			) {
923
+				// then remove the current cached item
924
+				unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
925
+				// and cache the newly saved object using it's new ID
926
+				$this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
927
+				return true;
928
+			}
929
+		}
930
+		return false;
931
+	}
932
+
933
+
934
+	/**
935
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
936
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
937
+	 *
938
+	 * @param string $relationName
939
+	 * @return EE_Base_Class
940
+	 */
941
+	public function get_one_from_cache($relationName)
942
+	{
943
+		$cached_array_or_object = isset($this->_model_relations[ $relationName ])
944
+			? $this->_model_relations[ $relationName ]
945
+			: null;
946
+		if (is_array($cached_array_or_object)) {
947
+			return array_shift($cached_array_or_object);
948
+		}
949
+		return $cached_array_or_object;
950
+	}
951
+
952
+
953
+	/**
954
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
955
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
956
+	 *
957
+	 * @param string $relationName
958
+	 * @throws ReflectionException
959
+	 * @throws InvalidArgumentException
960
+	 * @throws InvalidInterfaceException
961
+	 * @throws InvalidDataTypeException
962
+	 * @throws EE_Error
963
+	 * @return EE_Base_Class[] NOT necessarily indexed by primary keys
964
+	 */
965
+	public function get_all_from_cache($relationName)
966
+	{
967
+		$objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
968
+		// if the result is not an array, but exists, make it an array
969
+		$objects = is_array($objects) ? $objects : array($objects);
970
+		//bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
971
+		//basically, if this model object was stored in the session, and these cached model objects
972
+		//already have IDs, let's make sure they're in their model's entity mapper
973
+		//otherwise we will have duplicates next time we call
974
+		// EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
975
+		$model = EE_Registry::instance()->load_model($relationName);
976
+		foreach ($objects as $model_object) {
977
+			if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
978
+				//ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
979
+				if ($model_object->ID()) {
980
+					$model->add_to_entity_map($model_object);
981
+				}
982
+			} else {
983
+				throw new EE_Error(
984
+					sprintf(
985
+						esc_html__(
986
+							'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
987
+							'event_espresso'
988
+						),
989
+						$relationName,
990
+						gettype($model_object)
991
+					)
992
+				);
993
+			}
994
+		}
995
+		return $objects;
996
+	}
997
+
998
+
999
+	/**
1000
+	 * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1001
+	 * matching the given query conditions.
1002
+	 *
1003
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1004
+	 * @param int   $limit              How many objects to return.
1005
+	 * @param array $query_params       Any additional conditions on the query.
1006
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1007
+	 *                                  you can indicate just the columns you want returned
1008
+	 * @return array|EE_Base_Class[]
1009
+	 * @throws ReflectionException
1010
+	 * @throws InvalidArgumentException
1011
+	 * @throws InvalidInterfaceException
1012
+	 * @throws InvalidDataTypeException
1013
+	 * @throws EE_Error
1014
+	 */
1015
+	public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
1016
+	{
1017
+		$model         = $this->get_model();
1018
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1019
+			? $model->get_primary_key_field()->get_name()
1020
+			: $field_to_order_by;
1021
+		$current_value = ! empty($field) ? $this->get($field) : null;
1022
+		if (empty($field) || empty($current_value)) {
1023
+			return array();
1024
+		}
1025
+		return $model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1026
+	}
1027
+
1028
+
1029
+	/**
1030
+	 * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1031
+	 * matching the given query conditions.
1032
+	 *
1033
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1034
+	 * @param int   $limit              How many objects to return.
1035
+	 * @param array $query_params       Any additional conditions on the query.
1036
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1037
+	 *                                  you can indicate just the columns you want returned
1038
+	 * @return array|EE_Base_Class[]
1039
+	 * @throws ReflectionException
1040
+	 * @throws InvalidArgumentException
1041
+	 * @throws InvalidInterfaceException
1042
+	 * @throws InvalidDataTypeException
1043
+	 * @throws EE_Error
1044
+	 */
1045
+	public function previous_x(
1046
+		$field_to_order_by = null,
1047
+		$limit = 1,
1048
+		$query_params = array(),
1049
+		$columns_to_select = null
1050
+	) {
1051
+		$model         = $this->get_model();
1052
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1053
+			? $model->get_primary_key_field()->get_name()
1054
+			: $field_to_order_by;
1055
+		$current_value = ! empty($field) ? $this->get($field) : null;
1056
+		if (empty($field) || empty($current_value)) {
1057
+			return array();
1058
+		}
1059
+		return $model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1060
+	}
1061
+
1062
+
1063
+	/**
1064
+	 * Returns the next EE_Base_Class object in sequence from this object as found in the database
1065
+	 * matching the given query conditions.
1066
+	 *
1067
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1068
+	 * @param array $query_params       Any additional conditions on the query.
1069
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1070
+	 *                                  you can indicate just the columns you want returned
1071
+	 * @return array|EE_Base_Class
1072
+	 * @throws ReflectionException
1073
+	 * @throws InvalidArgumentException
1074
+	 * @throws InvalidInterfaceException
1075
+	 * @throws InvalidDataTypeException
1076
+	 * @throws EE_Error
1077
+	 */
1078
+	public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1079
+	{
1080
+		$model         = $this->get_model();
1081
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1082
+			? $model->get_primary_key_field()->get_name()
1083
+			: $field_to_order_by;
1084
+		$current_value = ! empty($field) ? $this->get($field) : null;
1085
+		if (empty($field) || empty($current_value)) {
1086
+			return array();
1087
+		}
1088
+		return $model->next($current_value, $field, $query_params, $columns_to_select);
1089
+	}
1090
+
1091
+
1092
+	/**
1093
+	 * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1094
+	 * matching the given query conditions.
1095
+	 *
1096
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1097
+	 * @param array $query_params       Any additional conditions on the query.
1098
+	 * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1099
+	 *                                  you can indicate just the column you want returned
1100
+	 * @return array|EE_Base_Class
1101
+	 * @throws ReflectionException
1102
+	 * @throws InvalidArgumentException
1103
+	 * @throws InvalidInterfaceException
1104
+	 * @throws InvalidDataTypeException
1105
+	 * @throws EE_Error
1106
+	 */
1107
+	public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1108
+	{
1109
+		$model         = $this->get_model();
1110
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1111
+			? $model->get_primary_key_field()->get_name()
1112
+			: $field_to_order_by;
1113
+		$current_value = ! empty($field) ? $this->get($field) : null;
1114
+		if (empty($field) || empty($current_value)) {
1115
+			return array();
1116
+		}
1117
+		return $model->previous($current_value, $field, $query_params, $columns_to_select);
1118
+	}
1119
+
1120
+
1121
+	/**
1122
+	 * Overrides parent because parent expects old models.
1123
+	 * This also doesn't do any validation, and won't work for serialized arrays
1124
+	 *
1125
+	 * @param string $field_name
1126
+	 * @param mixed  $field_value_from_db
1127
+	 * @throws ReflectionException
1128
+	 * @throws InvalidArgumentException
1129
+	 * @throws InvalidInterfaceException
1130
+	 * @throws InvalidDataTypeException
1131
+	 * @throws EE_Error
1132
+	 */
1133
+	public function set_from_db($field_name, $field_value_from_db)
1134
+	{
1135
+		$field_obj = $this->get_model()->field_settings_for($field_name);
1136
+		if ($field_obj instanceof EE_Model_Field_Base) {
1137
+			//you would think the DB has no NULLs for non-null label fields right? wrong!
1138
+			//eg, a CPT model object could have an entry in the posts table, but no
1139
+			//entry in the meta table. Meaning that all its columns in the meta table
1140
+			//are null! yikes! so when we find one like that, use defaults for its meta columns
1141
+			if ($field_value_from_db === null) {
1142
+				if ($field_obj->is_nullable()) {
1143
+					//if the field allows nulls, then let it be null
1144
+					$field_value = null;
1145
+				} else {
1146
+					$field_value = $field_obj->get_default_value();
1147
+				}
1148
+			} else {
1149
+				$field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1150
+			}
1151
+			$this->_fields[ $field_name ] = $field_value;
1152
+			$this->_clear_cached_property($field_name);
1153
+		}
1154
+	}
1155
+
1156
+
1157
+	/**
1158
+	 * verifies that the specified field is of the correct type
1159
+	 *
1160
+	 * @param string $field_name
1161
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1162
+	 *                                (in cases where the same property may be used for different outputs
1163
+	 *                                - i.e. datetime, money etc.)
1164
+	 * @return mixed
1165
+	 * @throws ReflectionException
1166
+	 * @throws InvalidArgumentException
1167
+	 * @throws InvalidInterfaceException
1168
+	 * @throws InvalidDataTypeException
1169
+	 * @throws EE_Error
1170
+	 */
1171
+	public function get($field_name, $extra_cache_ref = null)
1172
+	{
1173
+		return $this->_get_cached_property($field_name, false, $extra_cache_ref);
1174
+	}
1175
+
1176
+
1177
+	/**
1178
+	 * This method simply returns the RAW unprocessed value for the given property in this class
1179
+	 *
1180
+	 * @param  string $field_name A valid fieldname
1181
+	 * @return mixed              Whatever the raw value stored on the property is.
1182
+	 * @throws ReflectionException
1183
+	 * @throws InvalidArgumentException
1184
+	 * @throws InvalidInterfaceException
1185
+	 * @throws InvalidDataTypeException
1186
+	 * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1187
+	 */
1188
+	public function get_raw($field_name)
1189
+	{
1190
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1191
+		return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1192
+			? $this->_fields[ $field_name ]->format('U')
1193
+			: $this->_fields[ $field_name ];
1194
+	}
1195
+
1196
+
1197
+	/**
1198
+	 * This is used to return the internal DateTime object used for a field that is a
1199
+	 * EE_Datetime_Field.
1200
+	 *
1201
+	 * @param string $field_name               The field name retrieving the DateTime object.
1202
+	 * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1203
+	 * @throws EE_Error an error is set and false returned.  If the field IS an
1204
+	 *                                         EE_Datetime_Field and but the field value is null, then
1205
+	 *                                         just null is returned (because that indicates that likely
1206
+	 *                                         this field is nullable).
1207
+	 * @throws InvalidArgumentException
1208
+	 * @throws InvalidDataTypeException
1209
+	 * @throws InvalidInterfaceException
1210
+	 * @throws ReflectionException
1211
+	 */
1212
+	public function get_DateTime_object($field_name)
1213
+	{
1214
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1215
+		if (! $field_settings instanceof EE_Datetime_Field) {
1216
+			EE_Error::add_error(
1217
+				sprintf(
1218
+					esc_html__(
1219
+						'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1220
+						'event_espresso'
1221
+					),
1222
+					$field_name
1223
+				),
1224
+				__FILE__,
1225
+				__FUNCTION__,
1226
+				__LINE__
1227
+			);
1228
+			return false;
1229
+		}
1230
+		return isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime
1231
+			? clone $this->_fields[$field_name]
1232
+			: null;
1233
+	}
1234
+
1235
+
1236
+	/**
1237
+	 * To be used in template to immediately echo out the value, and format it for output.
1238
+	 * Eg, should call stripslashes and whatnot before echoing
1239
+	 *
1240
+	 * @param string $field_name      the name of the field as it appears in the DB
1241
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1242
+	 *                                (in cases where the same property may be used for different outputs
1243
+	 *                                - i.e. datetime, money etc.)
1244
+	 * @return void
1245
+	 * @throws ReflectionException
1246
+	 * @throws InvalidArgumentException
1247
+	 * @throws InvalidInterfaceException
1248
+	 * @throws InvalidDataTypeException
1249
+	 * @throws EE_Error
1250
+	 */
1251
+	public function e($field_name, $extra_cache_ref = null)
1252
+	{
1253
+		echo $this->get_pretty($field_name, $extra_cache_ref);
1254
+	}
1255
+
1256
+
1257
+	/**
1258
+	 * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1259
+	 * can be easily used as the value of form input.
1260
+	 *
1261
+	 * @param string $field_name
1262
+	 * @return void
1263
+	 * @throws ReflectionException
1264
+	 * @throws InvalidArgumentException
1265
+	 * @throws InvalidInterfaceException
1266
+	 * @throws InvalidDataTypeException
1267
+	 * @throws EE_Error
1268
+	 */
1269
+	public function f($field_name)
1270
+	{
1271
+		$this->e($field_name, 'form_input');
1272
+	}
1273
+
1274
+
1275
+	/**
1276
+	 * Same as `f()` but just returns the value instead of echoing it
1277
+	 *
1278
+	 * @param string $field_name
1279
+	 * @return string
1280
+	 * @throws ReflectionException
1281
+	 * @throws InvalidArgumentException
1282
+	 * @throws InvalidInterfaceException
1283
+	 * @throws InvalidDataTypeException
1284
+	 * @throws EE_Error
1285
+	 */
1286
+	public function get_f($field_name)
1287
+	{
1288
+		return (string) $this->get_pretty($field_name, 'form_input');
1289
+	}
1290
+
1291
+
1292
+	/**
1293
+	 * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1294
+	 * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1295
+	 * to see what options are available.
1296
+	 *
1297
+	 * @param string $field_name
1298
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1299
+	 *                                (in cases where the same property may be used for different outputs
1300
+	 *                                - i.e. datetime, money etc.)
1301
+	 * @return mixed
1302
+	 * @throws ReflectionException
1303
+	 * @throws InvalidArgumentException
1304
+	 * @throws InvalidInterfaceException
1305
+	 * @throws InvalidDataTypeException
1306
+	 * @throws EE_Error
1307
+	 */
1308
+	public function get_pretty($field_name, $extra_cache_ref = null)
1309
+	{
1310
+		return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1311
+	}
1312
+
1313
+
1314
+	/**
1315
+	 * This simply returns the datetime for the given field name
1316
+	 * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1317
+	 * (and the equivalent e_date, e_time, e_datetime).
1318
+	 *
1319
+	 * @access   protected
1320
+	 * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1321
+	 * @param string   $dt_frmt      valid datetime format used for date
1322
+	 *                               (if '' then we just use the default on the field,
1323
+	 *                               if NULL we use the last-used format)
1324
+	 * @param string   $tm_frmt      Same as above except this is for time format
1325
+	 * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1326
+	 * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1327
+	 * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1328
+	 *                               if field is not a valid dtt field, or void if echoing
1329
+	 * @throws ReflectionException
1330
+	 * @throws InvalidArgumentException
1331
+	 * @throws InvalidInterfaceException
1332
+	 * @throws InvalidDataTypeException
1333
+	 * @throws EE_Error
1334
+	 */
1335
+	protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1336
+	{
1337
+		// clear cached property
1338
+		$this->_clear_cached_property($field_name);
1339
+		//reset format properties because they are used in get()
1340
+		$this->_dt_frmt = $dt_frmt !== '' ? $dt_frmt : $this->_dt_frmt;
1341
+		$this->_tm_frmt = $tm_frmt !== '' ? $tm_frmt : $this->_tm_frmt;
1342
+		if ($echo) {
1343
+			$this->e($field_name, $date_or_time);
1344
+			return '';
1345
+		}
1346
+		return $this->get($field_name, $date_or_time);
1347
+	}
1348
+
1349
+
1350
+	/**
1351
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1352
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1353
+	 * other echoes the pretty value for dtt)
1354
+	 *
1355
+	 * @param  string $field_name name of model object datetime field holding the value
1356
+	 * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1357
+	 * @return string            datetime value formatted
1358
+	 * @throws ReflectionException
1359
+	 * @throws InvalidArgumentException
1360
+	 * @throws InvalidInterfaceException
1361
+	 * @throws InvalidDataTypeException
1362
+	 * @throws EE_Error
1363
+	 */
1364
+	public function get_date($field_name, $format = '')
1365
+	{
1366
+		return $this->_get_datetime($field_name, $format, null, 'D');
1367
+	}
1368
+
1369
+
1370
+	/**
1371
+	 * @param        $field_name
1372
+	 * @param string $format
1373
+	 * @throws ReflectionException
1374
+	 * @throws InvalidArgumentException
1375
+	 * @throws InvalidInterfaceException
1376
+	 * @throws InvalidDataTypeException
1377
+	 * @throws EE_Error
1378
+	 */
1379
+	public function e_date($field_name, $format = '')
1380
+	{
1381
+		$this->_get_datetime($field_name, $format, null, 'D', true);
1382
+	}
1383
+
1384
+
1385
+	/**
1386
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1387
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1388
+	 * other echoes the pretty value for dtt)
1389
+	 *
1390
+	 * @param  string $field_name name of model object datetime field holding the value
1391
+	 * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1392
+	 * @return string             datetime value formatted
1393
+	 * @throws ReflectionException
1394
+	 * @throws InvalidArgumentException
1395
+	 * @throws InvalidInterfaceException
1396
+	 * @throws InvalidDataTypeException
1397
+	 * @throws EE_Error
1398
+	 */
1399
+	public function get_time($field_name, $format = '')
1400
+	{
1401
+		return $this->_get_datetime($field_name, null, $format, 'T');
1402
+	}
1403
+
1404
+
1405
+	/**
1406
+	 * @param        $field_name
1407
+	 * @param string $format
1408
+	 * @throws ReflectionException
1409
+	 * @throws InvalidArgumentException
1410
+	 * @throws InvalidInterfaceException
1411
+	 * @throws InvalidDataTypeException
1412
+	 * @throws EE_Error
1413
+	 */
1414
+	public function e_time($field_name, $format = '')
1415
+	{
1416
+		$this->_get_datetime($field_name, null, $format, 'T', true);
1417
+	}
1418
+
1419
+
1420
+	/**
1421
+	 * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1422
+	 * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1423
+	 * other echoes the pretty value for dtt)
1424
+	 *
1425
+	 * @param  string $field_name name of model object datetime field holding the value
1426
+	 * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1427
+	 * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1428
+	 * @return string             datetime value formatted
1429
+	 * @throws ReflectionException
1430
+	 * @throws InvalidArgumentException
1431
+	 * @throws InvalidInterfaceException
1432
+	 * @throws InvalidDataTypeException
1433
+	 * @throws EE_Error
1434
+	 */
1435
+	public function get_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1436
+	{
1437
+		return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1438
+	}
1439
+
1440
+
1441
+	/**
1442
+	 * @param string $field_name
1443
+	 * @param string $dt_frmt
1444
+	 * @param string $tm_frmt
1445
+	 * @throws ReflectionException
1446
+	 * @throws InvalidArgumentException
1447
+	 * @throws InvalidInterfaceException
1448
+	 * @throws InvalidDataTypeException
1449
+	 * @throws EE_Error
1450
+	 */
1451
+	public function e_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1452
+	{
1453
+		$this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1454
+	}
1455
+
1456
+
1457
+	/**
1458
+	 * Get the i8ln value for a date using the WordPress @see date_i18n function.
1459
+	 *
1460
+	 * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1461
+	 * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1462
+	 *                           on the object will be used.
1463
+	 * @return string Date and time string in set locale or false if no field exists for the given
1464
+	 * @throws ReflectionException
1465
+	 * @throws InvalidArgumentException
1466
+	 * @throws InvalidInterfaceException
1467
+	 * @throws InvalidDataTypeException
1468
+	 * @throws EE_Error
1469
+	 *                           field name.
1470
+	 */
1471
+	public function get_i18n_datetime($field_name, $format = '')
1472
+	{
1473
+		$format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1474
+		return date_i18n(
1475
+			$format,
1476
+			EEH_DTT_Helper::get_timestamp_with_offset(
1477
+				$this->get_raw($field_name),
1478
+				$this->_timezone
1479
+			)
1480
+		);
1481
+	}
1482
+
1483
+
1484
+	/**
1485
+	 * This method validates whether the given field name is a valid field on the model object as well as it is of a
1486
+	 * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1487
+	 * thrown.
1488
+	 *
1489
+	 * @param  string $field_name The field name being checked
1490
+	 * @throws ReflectionException
1491
+	 * @throws InvalidArgumentException
1492
+	 * @throws InvalidInterfaceException
1493
+	 * @throws InvalidDataTypeException
1494
+	 * @throws EE_Error
1495
+	 * @return EE_Datetime_Field
1496
+	 */
1497
+	protected function _get_dtt_field_settings($field_name)
1498
+	{
1499
+		$field = $this->get_model()->field_settings_for($field_name);
1500
+		//check if field is dtt
1501
+		if ($field instanceof EE_Datetime_Field) {
1502
+			return $field;
1503
+		}
1504
+		throw new EE_Error(
1505
+			sprintf(
1506
+				esc_html__(
1507
+					'The field name "%s" has been requested for the EE_Base_Class datetime functions and it is not a valid EE_Datetime_Field.  Please check the spelling of the field and make sure it has been setup as a EE_Datetime_Field in the %s model constructor',
1508
+					'event_espresso'
1509
+				),
1510
+				$field_name,
1511
+				self::_get_model_classname(get_class($this))
1512
+			)
1513
+		);
1514
+	}
1515
+
1516
+
1517
+
1518
+
1519
+	/**
1520
+	 * NOTE ABOUT BELOW:
1521
+	 * These convenience date and time setters are for setting date and time independently.  In other words you might
1522
+	 * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1523
+	 * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1524
+	 * method and make sure you send the entire datetime value for setting.
1525
+	 */
1526
+	/**
1527
+	 * sets the time on a datetime property
1528
+	 *
1529
+	 * @access protected
1530
+	 * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1531
+	 * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1532
+	 * @throws ReflectionException
1533
+	 * @throws InvalidArgumentException
1534
+	 * @throws InvalidInterfaceException
1535
+	 * @throws InvalidDataTypeException
1536
+	 * @throws EE_Error
1537
+	 */
1538
+	protected function _set_time_for($time, $fieldname)
1539
+	{
1540
+		$this->_set_date_time('T', $time, $fieldname);
1541
+	}
1542
+
1543
+
1544
+	/**
1545
+	 * sets the date on a datetime property
1546
+	 *
1547
+	 * @access protected
1548
+	 * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1549
+	 * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1550
+	 * @throws ReflectionException
1551
+	 * @throws InvalidArgumentException
1552
+	 * @throws InvalidInterfaceException
1553
+	 * @throws InvalidDataTypeException
1554
+	 * @throws EE_Error
1555
+	 */
1556
+	protected function _set_date_for($date, $fieldname)
1557
+	{
1558
+		$this->_set_date_time('D', $date, $fieldname);
1559
+	}
1560
+
1561
+
1562
+	/**
1563
+	 * This takes care of setting a date or time independently on a given model object property. This method also
1564
+	 * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1565
+	 *
1566
+	 * @access protected
1567
+	 * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1568
+	 * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1569
+	 * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1570
+	 *                                        EE_Datetime_Field property)
1571
+	 * @throws ReflectionException
1572
+	 * @throws InvalidArgumentException
1573
+	 * @throws InvalidInterfaceException
1574
+	 * @throws InvalidDataTypeException
1575
+	 * @throws EE_Error
1576
+	 */
1577
+	protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1578
+	{
1579
+		$field = $this->_get_dtt_field_settings($fieldname);
1580
+		$field->set_timezone($this->_timezone);
1581
+		$field->set_date_format($this->_dt_frmt);
1582
+		$field->set_time_format($this->_tm_frmt);
1583
+		switch ($what) {
1584
+			case 'T' :
1585
+				$this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1586
+					$datetime_value,
1587
+					$this->_fields[ $fieldname ]
1588
+				);
1589
+				break;
1590
+			case 'D' :
1591
+				$this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1592
+					$datetime_value,
1593
+					$this->_fields[ $fieldname ]
1594
+				);
1595
+				break;
1596
+			case 'B' :
1597
+				$this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1598
+				break;
1599
+		}
1600
+		$this->_clear_cached_property($fieldname);
1601
+	}
1602
+
1603
+
1604
+	/**
1605
+	 * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1606
+	 * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1607
+	 * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1608
+	 * that could lead to some unexpected results!
1609
+	 *
1610
+	 * @access public
1611
+	 * @param string $field_name               This is the name of the field on the object that contains the date/time
1612
+	 *                                         value being returned.
1613
+	 * @param string $callback                 must match a valid method in this class (defaults to get_datetime)
1614
+	 * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1615
+	 * @param string $prepend                  You can include something to prepend on the timestamp
1616
+	 * @param string $append                   You can include something to append on the timestamp
1617
+	 * @throws ReflectionException
1618
+	 * @throws InvalidArgumentException
1619
+	 * @throws InvalidInterfaceException
1620
+	 * @throws InvalidDataTypeException
1621
+	 * @throws EE_Error
1622
+	 * @return string timestamp
1623
+	 */
1624
+	public function display_in_my_timezone(
1625
+		$field_name,
1626
+		$callback = 'get_datetime',
1627
+		$args = null,
1628
+		$prepend = '',
1629
+		$append = ''
1630
+	) {
1631
+		$timezone = EEH_DTT_Helper::get_timezone();
1632
+		if ($timezone === $this->_timezone) {
1633
+			return '';
1634
+		}
1635
+		$original_timezone = $this->_timezone;
1636
+		$this->set_timezone($timezone);
1637
+		$fn   = (array) $field_name;
1638
+		$args = array_merge($fn, (array) $args);
1639
+		if (! method_exists($this, $callback)) {
1640
+			throw new EE_Error(
1641
+				sprintf(
1642
+					esc_html__(
1643
+						'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1644
+						'event_espresso'
1645
+					),
1646
+					$callback
1647
+				)
1648
+			);
1649
+		}
1650
+		$args   = (array) $args;
1651
+		$return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1652
+		$this->set_timezone($original_timezone);
1653
+		return $return;
1654
+	}
1655
+
1656
+
1657
+	/**
1658
+	 * Deletes this model object.
1659
+	 * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1660
+	 * override
1661
+	 * `EE_Base_Class::_delete` NOT this class.
1662
+	 *
1663
+	 * @return boolean | int
1664
+	 * @throws ReflectionException
1665
+	 * @throws InvalidArgumentException
1666
+	 * @throws InvalidInterfaceException
1667
+	 * @throws InvalidDataTypeException
1668
+	 * @throws EE_Error
1669
+	 */
1670
+	public function delete()
1671
+	{
1672
+		/**
1673
+		 * Called just before the `EE_Base_Class::_delete` method call.
1674
+		 * Note:
1675
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1676
+		 * should be aware that `_delete` may not always result in a permanent delete.
1677
+		 * For example, `EE_Soft_Delete_Base_Class::_delete`
1678
+		 * soft deletes (trash) the object and does not permanently delete it.
1679
+		 *
1680
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1681
+		 */
1682
+		do_action('AHEE__EE_Base_Class__delete__before', $this);
1683
+		$result = $this->_delete();
1684
+		/**
1685
+		 * Called just after the `EE_Base_Class::_delete` method call.
1686
+		 * Note:
1687
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1688
+		 * should be aware that `_delete` may not always result in a permanent delete.
1689
+		 * For example `EE_Soft_Base_Class::_delete`
1690
+		 * soft deletes (trash) the object and does not permanently delete it.
1691
+		 *
1692
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1693
+		 * @param boolean       $result
1694
+		 */
1695
+		do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1696
+		return $result;
1697
+	}
1698
+
1699
+
1700
+	/**
1701
+	 * Calls the specific delete method for the instantiated class.
1702
+	 * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1703
+	 * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1704
+	 * `EE_Base_Class::delete`
1705
+	 *
1706
+	 * @return bool|int
1707
+	 * @throws ReflectionException
1708
+	 * @throws InvalidArgumentException
1709
+	 * @throws InvalidInterfaceException
1710
+	 * @throws InvalidDataTypeException
1711
+	 * @throws EE_Error
1712
+	 */
1713
+	protected function _delete()
1714
+	{
1715
+		return $this->delete_permanently();
1716
+	}
1717
+
1718
+
1719
+	/**
1720
+	 * Deletes this model object permanently from db
1721
+	 * (but keep in mind related models may block the delete and return an error)
1722
+	 *
1723
+	 * @return bool | int
1724
+	 * @throws ReflectionException
1725
+	 * @throws InvalidArgumentException
1726
+	 * @throws InvalidInterfaceException
1727
+	 * @throws InvalidDataTypeException
1728
+	 * @throws EE_Error
1729
+	 */
1730
+	public function delete_permanently()
1731
+	{
1732
+		/**
1733
+		 * Called just before HARD deleting a model object
1734
+		 *
1735
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1736
+		 */
1737
+		do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1738
+		$model  = $this->get_model();
1739
+		$result = $model->delete_permanently_by_ID($this->ID());
1740
+		$this->refresh_cache_of_related_objects();
1741
+		/**
1742
+		 * Called just after HARD deleting a model object
1743
+		 *
1744
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1745
+		 * @param boolean       $result
1746
+		 */
1747
+		do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1748
+		return $result;
1749
+	}
1750
+
1751
+
1752
+	/**
1753
+	 * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1754
+	 * related model objects
1755
+	 *
1756
+	 * @throws ReflectionException
1757
+	 * @throws InvalidArgumentException
1758
+	 * @throws InvalidInterfaceException
1759
+	 * @throws InvalidDataTypeException
1760
+	 * @throws EE_Error
1761
+	 */
1762
+	public function refresh_cache_of_related_objects()
1763
+	{
1764
+		$model = $this->get_model();
1765
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1766
+			if (! empty($this->_model_relations[ $relation_name ])) {
1767
+				$related_objects = $this->_model_relations[ $relation_name ];
1768
+				if ($relation_obj instanceof EE_Belongs_To_Relation) {
1769
+					//this relation only stores a single model object, not an array
1770
+					//but let's make it consistent
1771
+					$related_objects = array($related_objects);
1772
+				}
1773
+				foreach ($related_objects as $related_object) {
1774
+					//only refresh their cache if they're in memory
1775
+					if ($related_object instanceof EE_Base_Class) {
1776
+						$related_object->clear_cache(
1777
+							$model->get_this_model_name(),
1778
+							$this
1779
+						);
1780
+					}
1781
+				}
1782
+			}
1783
+		}
1784
+	}
1785
+
1786
+
1787
+	/**
1788
+	 *        Saves this object to the database. An array may be supplied to set some values on this
1789
+	 * object just before saving.
1790
+	 *
1791
+	 * @access public
1792
+	 * @param array $set_cols_n_values keys are field names, values are their new values,
1793
+	 *                                 if provided during the save() method (often client code will change the fields'
1794
+	 *                                 values before calling save)
1795
+	 * @throws InvalidArgumentException
1796
+	 * @throws InvalidInterfaceException
1797
+	 * @throws InvalidDataTypeException
1798
+	 * @throws EE_Error
1799
+	 * @return int , 1 on a successful update, the ID of the new entry on insert; 0 on failure or if the model object
1800
+	 *                                 isn't allowed to persist (as determined by EE_Base_Class::allow_persist())
1801
+	 * @throws ReflectionException
1802
+	 * @throws ReflectionException
1803
+	 * @throws ReflectionException
1804
+	 */
1805
+	public function save($set_cols_n_values = array())
1806
+	{
1807
+		$model = $this->get_model();
1808
+		/**
1809
+		 * Filters the fields we're about to save on the model object
1810
+		 *
1811
+		 * @param array         $set_cols_n_values
1812
+		 * @param EE_Base_Class $model_object
1813
+		 */
1814
+		$set_cols_n_values = (array) apply_filters(
1815
+			'FHEE__EE_Base_Class__save__set_cols_n_values',
1816
+			$set_cols_n_values,
1817
+			$this
1818
+		);
1819
+		//set attributes as provided in $set_cols_n_values
1820
+		foreach ($set_cols_n_values as $column => $value) {
1821
+			$this->set($column, $value);
1822
+		}
1823
+		// no changes ? then don't do anything
1824
+		if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1825
+			return 0;
1826
+		}
1827
+		/**
1828
+		 * Saving a model object.
1829
+		 * Before we perform a save, this action is fired.
1830
+		 *
1831
+		 * @param EE_Base_Class $model_object the model object about to be saved.
1832
+		 */
1833
+		do_action('AHEE__EE_Base_Class__save__begin', $this);
1834
+		if (! $this->allow_persist()) {
1835
+			return 0;
1836
+		}
1837
+		// now get current attribute values
1838
+		$save_cols_n_values = $this->_fields;
1839
+		// if the object already has an ID, update it. Otherwise, insert it
1840
+		// also: change the assumption about values passed to the model NOT being prepare dby the model object.
1841
+		// They have been
1842
+		$old_assumption_concerning_value_preparation = $model
1843
+			->get_assumption_concerning_values_already_prepared_by_model_object();
1844
+		$model->assume_values_already_prepared_by_model_object(true);
1845
+		//does this model have an autoincrement PK?
1846
+		if ($model->has_primary_key_field()) {
1847
+			if ($model->get_primary_key_field()->is_auto_increment()) {
1848
+				//ok check if it's set, if so: update; if not, insert
1849
+				if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1850
+					$results = $model->update_by_ID($save_cols_n_values, $this->ID());
1851
+				} else {
1852
+					unset($save_cols_n_values[ $model->primary_key_name() ]);
1853
+					$results = $model->insert($save_cols_n_values);
1854
+					if ($results) {
1855
+						//if successful, set the primary key
1856
+						//but don't use the normal SET method, because it will check if
1857
+						//an item with the same ID exists in the mapper & db, then
1858
+						//will find it in the db (because we just added it) and THAT object
1859
+						//will get added to the mapper before we can add this one!
1860
+						//but if we just avoid using the SET method, all that headache can be avoided
1861
+						$pk_field_name                   = $model->primary_key_name();
1862
+						$this->_fields[ $pk_field_name ] = $results;
1863
+						$this->_clear_cached_property($pk_field_name);
1864
+						$model->add_to_entity_map($this);
1865
+						$this->_update_cached_related_model_objs_fks();
1866
+					}
1867
+				}
1868
+			} else {//PK is NOT auto-increment
1869
+				//so check if one like it already exists in the db
1870
+				if ($model->exists_by_ID($this->ID())) {
1871
+					if (WP_DEBUG && ! $this->in_entity_map()) {
1872
+						throw new EE_Error(
1873
+							sprintf(
1874
+								esc_html__(
1875
+									'Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1876
+									'event_espresso'
1877
+								),
1878
+								get_class($this),
1879
+								get_class($model) . '::instance()->add_to_entity_map()',
1880
+								get_class($model) . '::instance()->get_one_by_ID()',
1881
+								'<br />'
1882
+							)
1883
+						);
1884
+					}
1885
+					$results = $model->update_by_ID($save_cols_n_values, $this->ID());
1886
+				} else {
1887
+					$results = $model->insert($save_cols_n_values);
1888
+					$this->_update_cached_related_model_objs_fks();
1889
+				}
1890
+			}
1891
+		} else {//there is NO primary key
1892
+			$already_in_db = false;
1893
+			foreach ($model->unique_indexes() as $index) {
1894
+				$uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1895
+				if ($model->exists(array($uniqueness_where_params))) {
1896
+					$already_in_db = true;
1897
+				}
1898
+			}
1899
+			if ($already_in_db) {
1900
+				$combined_pk_fields_n_values = array_intersect_key($save_cols_n_values,
1901
+					$model->get_combined_primary_key_fields());
1902
+				$results                     = $model->update(
1903
+					$save_cols_n_values,
1904
+					$combined_pk_fields_n_values
1905
+				);
1906
+			} else {
1907
+				$results = $model->insert($save_cols_n_values);
1908
+			}
1909
+		}
1910
+		//restore the old assumption about values being prepared by the model object
1911
+		$model->assume_values_already_prepared_by_model_object(
1912
+				$old_assumption_concerning_value_preparation
1913
+			);
1914
+		/**
1915
+		 * After saving the model object this action is called
1916
+		 *
1917
+		 * @param EE_Base_Class $model_object which was just saved
1918
+		 * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1919
+		 *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1920
+		 */
1921
+		do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1922
+		$this->_has_changes = false;
1923
+		return $results;
1924
+	}
1925
+
1926
+
1927
+	/**
1928
+	 * Updates the foreign key on related models objects pointing to this to have this model object's ID
1929
+	 * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1930
+	 * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1931
+	 * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1932
+	 * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1933
+	 * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1934
+	 * or not they exist in the DB (if they do, their DB records will be automatically updated)
1935
+	 *
1936
+	 * @return void
1937
+	 * @throws ReflectionException
1938
+	 * @throws InvalidArgumentException
1939
+	 * @throws InvalidInterfaceException
1940
+	 * @throws InvalidDataTypeException
1941
+	 * @throws EE_Error
1942
+	 */
1943
+	protected function _update_cached_related_model_objs_fks()
1944
+	{
1945
+		$model = $this->get_model();
1946
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1947
+			if ($relation_obj instanceof EE_Has_Many_Relation) {
1948
+				foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1949
+					$fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1950
+						$model->get_this_model_name()
1951
+					);
1952
+					$related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1953
+					if ($related_model_obj_in_cache->ID()) {
1954
+						$related_model_obj_in_cache->save();
1955
+					}
1956
+				}
1957
+			}
1958
+		}
1959
+	}
1960
+
1961
+
1962
+	/**
1963
+	 * Saves this model object and its NEW cached relations to the database.
1964
+	 * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1965
+	 * In order for that to work, we would need to mark model objects as dirty/clean...
1966
+	 * because otherwise, there's a potential for infinite looping of saving
1967
+	 * Saves the cached related model objects, and ensures the relation between them
1968
+	 * and this object and properly setup
1969
+	 *
1970
+	 * @return int ID of new model object on save; 0 on failure+
1971
+	 * @throws ReflectionException
1972
+	 * @throws InvalidArgumentException
1973
+	 * @throws InvalidInterfaceException
1974
+	 * @throws InvalidDataTypeException
1975
+	 * @throws EE_Error
1976
+	 */
1977
+	public function save_new_cached_related_model_objs()
1978
+	{
1979
+		//make sure this has been saved
1980
+		if (! $this->ID()) {
1981
+			$id = $this->save();
1982
+		} else {
1983
+			$id = $this->ID();
1984
+		}
1985
+		//now save all the NEW cached model objects  (ie they don't exist in the DB)
1986
+		foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1987
+			if ($this->_model_relations[ $relationName ]) {
1988
+				//is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1989
+				//or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1990
+				/* @var $related_model_obj EE_Base_Class */
1991
+				if ($relationObj instanceof EE_Belongs_To_Relation) {
1992
+					//add a relation to that relation type (which saves the appropriate thing in the process)
1993
+					//but ONLY if it DOES NOT exist in the DB
1994
+					$related_model_obj = $this->_model_relations[ $relationName ];
1995
+					//					if( ! $related_model_obj->ID()){
1996
+					$this->_add_relation_to($related_model_obj, $relationName);
1997
+					$related_model_obj->save_new_cached_related_model_objs();
1998
+					//					}
1999
+				} else {
2000
+					foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2001
+						//add a relation to that relation type (which saves the appropriate thing in the process)
2002
+						//but ONLY if it DOES NOT exist in the DB
2003
+						//						if( ! $related_model_obj->ID()){
2004
+						$this->_add_relation_to($related_model_obj, $relationName);
2005
+						$related_model_obj->save_new_cached_related_model_objs();
2006
+						//						}
2007
+					}
2008
+				}
2009
+			}
2010
+		}
2011
+		return $id;
2012
+	}
2013
+
2014
+
2015
+	/**
2016
+	 * for getting a model while instantiated.
2017
+	 *
2018
+	 * @return EEM_Base | EEM_CPT_Base
2019
+	 * @throws ReflectionException
2020
+	 * @throws InvalidArgumentException
2021
+	 * @throws InvalidInterfaceException
2022
+	 * @throws InvalidDataTypeException
2023
+	 * @throws EE_Error
2024
+	 */
2025
+	public function get_model()
2026
+	{
2027
+		if (! $this->_model) {
2028
+			$modelName    = self::_get_model_classname(get_class($this));
2029
+			$this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2030
+		} else {
2031
+			$this->_model->set_timezone($this->_timezone);
2032
+		}
2033
+		return $this->_model;
2034
+	}
2035
+
2036
+
2037
+	/**
2038
+	 * @param $props_n_values
2039
+	 * @param $classname
2040
+	 * @return mixed bool|EE_Base_Class|EEM_CPT_Base
2041
+	 * @throws ReflectionException
2042
+	 * @throws InvalidArgumentException
2043
+	 * @throws InvalidInterfaceException
2044
+	 * @throws InvalidDataTypeException
2045
+	 * @throws EE_Error
2046
+	 */
2047
+	protected static function _get_object_from_entity_mapper($props_n_values, $classname)
2048
+	{
2049
+		//TODO: will not work for Term_Relationships because they have no PK!
2050
+		$primary_id_ref = self::_get_primary_key_name($classname);
2051
+		if (
2052
+			array_key_exists($primary_id_ref, $props_n_values)
2053
+			&& ! empty($props_n_values[ $primary_id_ref ])
2054
+		) {
2055
+			$id = $props_n_values[ $primary_id_ref ];
2056
+			return self::_get_model($classname)->get_from_entity_map($id);
2057
+		}
2058
+		return false;
2059
+	}
2060
+
2061
+
2062
+	/**
2063
+	 * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
2064
+	 * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
2065
+	 * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
2066
+	 * we return false.
2067
+	 *
2068
+	 * @param  array  $props_n_values   incoming array of properties and their values
2069
+	 * @param  string $classname        the classname of the child class
2070
+	 * @param null    $timezone
2071
+	 * @param array   $date_formats     incoming date_formats in an array where the first value is the
2072
+	 *                                  date_format and the second value is the time format
2073
+	 * @return mixed (EE_Base_Class|bool)
2074
+	 * @throws InvalidArgumentException
2075
+	 * @throws InvalidInterfaceException
2076
+	 * @throws InvalidDataTypeException
2077
+	 * @throws EE_Error
2078
+	 * @throws ReflectionException
2079
+	 * @throws ReflectionException
2080
+	 * @throws ReflectionException
2081
+	 */
2082
+	protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
2083
+	{
2084
+		$existing = null;
2085
+		$model    = self::_get_model($classname, $timezone);
2086
+		if ($model->has_primary_key_field()) {
2087
+			$primary_id_ref = self::_get_primary_key_name($classname);
2088
+			if (array_key_exists($primary_id_ref, $props_n_values)
2089
+				&& ! empty($props_n_values[ $primary_id_ref ])
2090
+			) {
2091
+				$existing = $model->get_one_by_ID(
2092
+					$props_n_values[ $primary_id_ref ]
2093
+				);
2094
+			}
2095
+		} elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
2096
+			//no primary key on this model, but there's still a matching item in the DB
2097
+			$existing = self::_get_model($classname, $timezone)->get_one_by_ID(
2098
+				self::_get_model($classname, $timezone)
2099
+					->get_index_primary_key_string($props_n_values)
2100
+			);
2101
+		}
2102
+		if ($existing) {
2103
+			//set date formats if present before setting values
2104
+			if (! empty($date_formats) && is_array($date_formats)) {
2105
+				$existing->set_date_format($date_formats[0]);
2106
+				$existing->set_time_format($date_formats[1]);
2107
+			} else {
2108
+				//set default formats for date and time
2109
+				$existing->set_date_format(get_option('date_format'));
2110
+				$existing->set_time_format(get_option('time_format'));
2111
+			}
2112
+			foreach ($props_n_values as $property => $field_value) {
2113
+				$existing->set($property, $field_value);
2114
+			}
2115
+			return $existing;
2116
+		}
2117
+		return false;
2118
+	}
2119
+
2120
+
2121
+	/**
2122
+	 * Gets the EEM_*_Model for this class
2123
+	 *
2124
+	 * @access public now, as this is more convenient
2125
+	 * @param      $classname
2126
+	 * @param null $timezone
2127
+	 * @throws ReflectionException
2128
+	 * @throws InvalidArgumentException
2129
+	 * @throws InvalidInterfaceException
2130
+	 * @throws InvalidDataTypeException
2131
+	 * @throws EE_Error
2132
+	 * @return EEM_Base
2133
+	 */
2134
+	protected static function _get_model($classname, $timezone = null)
2135
+	{
2136
+		//find model for this class
2137
+		if (! $classname) {
2138
+			throw new EE_Error(
2139
+				sprintf(
2140
+					esc_html__(
2141
+						'What were you thinking calling _get_model(%s)?? You need to specify the class name',
2142
+						'event_espresso'
2143
+					),
2144
+					$classname
2145
+				)
2146
+			);
2147
+		}
2148
+		$modelName = self::_get_model_classname($classname);
2149
+		return self::_get_model_instance_with_name($modelName, $timezone);
2150
+	}
2151
+
2152
+
2153
+	/**
2154
+	 * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2155
+	 *
2156
+	 * @param string $model_classname
2157
+	 * @param null   $timezone
2158
+	 * @return EEM_Base
2159
+	 * @throws ReflectionException
2160
+	 * @throws InvalidArgumentException
2161
+	 * @throws InvalidInterfaceException
2162
+	 * @throws InvalidDataTypeException
2163
+	 * @throws EE_Error
2164
+	 */
2165
+	protected static function _get_model_instance_with_name($model_classname, $timezone = null)
2166
+	{
2167
+		$model_classname = str_replace('EEM_', '', $model_classname);
2168
+		$model           = EE_Registry::instance()->load_model($model_classname);
2169
+		$model->set_timezone($timezone);
2170
+		return $model;
2171
+	}
2172
+
2173
+
2174
+	/**
2175
+	 * If a model name is provided (eg Registration), gets the model classname for that model.
2176
+	 * Also works if a model class's classname is provided (eg EE_Registration).
2177
+	 *
2178
+	 * @param null $model_name
2179
+	 * @return string like EEM_Attendee
2180
+	 */
2181
+	private static function _get_model_classname($model_name = null)
2182
+	{
2183
+		if (strpos($model_name, 'EE_') === 0) {
2184
+			$model_classname = str_replace('EE_', 'EEM_', $model_name);
2185
+		} else {
2186
+			$model_classname = 'EEM_' . $model_name;
2187
+		}
2188
+		return $model_classname;
2189
+	}
2190
+
2191
+
2192
+	/**
2193
+	 * returns the name of the primary key attribute
2194
+	 *
2195
+	 * @param null $classname
2196
+	 * @throws ReflectionException
2197
+	 * @throws InvalidArgumentException
2198
+	 * @throws InvalidInterfaceException
2199
+	 * @throws InvalidDataTypeException
2200
+	 * @throws EE_Error
2201
+	 * @return string
2202
+	 */
2203
+	protected static function _get_primary_key_name($classname = null)
2204
+	{
2205
+		if (! $classname) {
2206
+			throw new EE_Error(
2207
+				sprintf(
2208
+					esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
2209
+					$classname
2210
+				)
2211
+			);
2212
+		}
2213
+		return self::_get_model($classname)->get_primary_key_field()->get_name();
2214
+	}
2215
+
2216
+
2217
+	/**
2218
+	 * Gets the value of the primary key.
2219
+	 * If the object hasn't yet been saved, it should be whatever the model field's default was
2220
+	 * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
2221
+	 * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
2222
+	 *
2223
+	 * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
2224
+	 * @throws ReflectionException
2225
+	 * @throws InvalidArgumentException
2226
+	 * @throws InvalidInterfaceException
2227
+	 * @throws InvalidDataTypeException
2228
+	 * @throws EE_Error
2229
+	 */
2230
+	public function ID()
2231
+	{
2232
+		$model = $this->get_model();
2233
+		//now that we know the name of the variable, use a variable variable to get its value and return its
2234
+		if ($model->has_primary_key_field()) {
2235
+			return $this->_fields[ $model->primary_key_name() ];
2236
+		}
2237
+		return $model->get_index_primary_key_string($this->_fields);
2238
+	}
2239
+
2240
+
2241
+	/**
2242
+	 * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2243
+	 * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2244
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2245
+	 *
2246
+	 * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2247
+	 * @param string $relationName                     eg 'Events','Question',etc.
2248
+	 *                                                 an attendee to a group, you also want to specify which role they
2249
+	 *                                                 will have in that group. So you would use this parameter to
2250
+	 *                                                 specify array('role-column-name'=>'role-id')
2251
+	 * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2252
+	 *                                                 allow you to further constrict the relation to being added.
2253
+	 *                                                 However, keep in mind that the columns (keys) given must match a
2254
+	 *                                                 column on the JOIN table and currently only the HABTM models
2255
+	 *                                                 accept these additional conditions.  Also remember that if an
2256
+	 *                                                 exact match isn't found for these extra cols/val pairs, then a
2257
+	 *                                                 NEW row is created in the join table.
2258
+	 * @param null   $cache_id
2259
+	 * @throws ReflectionException
2260
+	 * @throws InvalidArgumentException
2261
+	 * @throws InvalidInterfaceException
2262
+	 * @throws InvalidDataTypeException
2263
+	 * @throws EE_Error
2264
+	 * @return EE_Base_Class the object the relation was added to
2265
+	 */
2266
+	public function _add_relation_to(
2267
+		$otherObjectModelObjectOrID,
2268
+		$relationName,
2269
+		$extra_join_model_fields_n_values = array(),
2270
+		$cache_id = null
2271
+	) {
2272
+		$model = $this->get_model();
2273
+		//if this thing exists in the DB, save the relation to the DB
2274
+		if ($this->ID()) {
2275
+			$otherObject = $model->add_relationship_to(
2276
+				$this,
2277
+				$otherObjectModelObjectOrID,
2278
+				$relationName,
2279
+				$extra_join_model_fields_n_values
2280
+			);
2281
+			//clear cache so future get_many_related and get_first_related() return new results.
2282
+			$this->clear_cache($relationName, $otherObject, true);
2283
+			if ($otherObject instanceof EE_Base_Class) {
2284
+				$otherObject->clear_cache($model->get_this_model_name(), $this);
2285
+			}
2286
+		} else {
2287
+			//this thing doesn't exist in the DB,  so just cache it
2288
+			if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2289
+				throw new EE_Error(
2290
+					sprintf(
2291
+						esc_html__(
2292
+							'Before a model object is saved to the database, calls to _add_relation_to must be passed an actual object, not just an ID. You provided %s as the model object to a %s',
2293
+							'event_espresso'
2294
+						),
2295
+						$otherObjectModelObjectOrID,
2296
+						get_class($this)
2297
+					)
2298
+				);
2299
+			}
2300
+			$otherObject = $otherObjectModelObjectOrID;
2301
+			$this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2302
+		}
2303
+		if ($otherObject instanceof EE_Base_Class) {
2304
+			//fix the reciprocal relation too
2305
+			if ($otherObject->ID()) {
2306
+				//its saved so assumed relations exist in the DB, so we can just
2307
+				//clear the cache so future queries use the updated info in the DB
2308
+				$otherObject->clear_cache(
2309
+					$model->get_this_model_name(),
2310
+					null,
2311
+					true
2312
+				);
2313
+			} else {
2314
+				//it's not saved, so it caches relations like this
2315
+				$otherObject->cache($model->get_this_model_name(), $this);
2316
+			}
2317
+		}
2318
+		return $otherObject;
2319
+	}
2320
+
2321
+
2322
+	/**
2323
+	 * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2324
+	 * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2325
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2326
+	 * from the cache
2327
+	 *
2328
+	 * @param mixed  $otherObjectModelObjectOrID
2329
+	 *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2330
+	 *                to the DB yet
2331
+	 * @param string $relationName
2332
+	 * @param array  $where_query
2333
+	 *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2334
+	 *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2335
+	 *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2336
+	 *                remember that if an exact match isn't found for these extra cols/val pairs, then a NEW row is
2337
+	 *                created in the join table.
2338
+	 * @return EE_Base_Class the relation was removed from
2339
+	 * @throws ReflectionException
2340
+	 * @throws InvalidArgumentException
2341
+	 * @throws InvalidInterfaceException
2342
+	 * @throws InvalidDataTypeException
2343
+	 * @throws EE_Error
2344
+	 */
2345
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2346
+	{
2347
+		if ($this->ID()) {
2348
+			//if this exists in the DB, save the relation change to the DB too
2349
+			$otherObject = $this->get_model()->remove_relationship_to(
2350
+				$this,
2351
+				$otherObjectModelObjectOrID,
2352
+				$relationName,
2353
+				$where_query
2354
+			);
2355
+			$this->clear_cache(
2356
+				$relationName,
2357
+				$otherObject
2358
+			);
2359
+		} else {
2360
+			//this doesn't exist in the DB, just remove it from the cache
2361
+			$otherObject = $this->clear_cache(
2362
+				$relationName,
2363
+				$otherObjectModelObjectOrID
2364
+			);
2365
+		}
2366
+		if ($otherObject instanceof EE_Base_Class) {
2367
+			$otherObject->clear_cache(
2368
+				$this->get_model()->get_this_model_name(),
2369
+				$this
2370
+			);
2371
+		}
2372
+		return $otherObject;
2373
+	}
2374
+
2375
+
2376
+	/**
2377
+	 * Removes ALL the related things for the $relationName.
2378
+	 *
2379
+	 * @param string $relationName
2380
+	 * @param array  $where_query_params like EEM_Base::get_all's $query_params[0] (where conditions)
2381
+	 * @return EE_Base_Class
2382
+	 * @throws ReflectionException
2383
+	 * @throws InvalidArgumentException
2384
+	 * @throws InvalidInterfaceException
2385
+	 * @throws InvalidDataTypeException
2386
+	 * @throws EE_Error
2387
+	 */
2388
+	public function _remove_relations($relationName, $where_query_params = array())
2389
+	{
2390
+		if ($this->ID()) {
2391
+			//if this exists in the DB, save the relation change to the DB too
2392
+			$otherObjects = $this->get_model()->remove_relations(
2393
+				$this,
2394
+				$relationName,
2395
+				$where_query_params
2396
+			);
2397
+			$this->clear_cache(
2398
+				$relationName,
2399
+				null,
2400
+				true
2401
+			);
2402
+		} else {
2403
+			//this doesn't exist in the DB, just remove it from the cache
2404
+			$otherObjects = $this->clear_cache(
2405
+				$relationName,
2406
+				null,
2407
+				true
2408
+			);
2409
+		}
2410
+		if (is_array($otherObjects)) {
2411
+			foreach ($otherObjects as $otherObject) {
2412
+				$otherObject->clear_cache(
2413
+					$this->get_model()->get_this_model_name(),
2414
+					$this
2415
+				);
2416
+			}
2417
+		}
2418
+		return $otherObjects;
2419
+	}
2420
+
2421
+
2422
+	/**
2423
+	 * Gets all the related model objects of the specified type. Eg, if the current class if
2424
+	 * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2425
+	 * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2426
+	 * because we want to get even deleted items etc.
2427
+	 *
2428
+	 * @param string $relationName key in the model's _model_relations array
2429
+	 * @param array  $query_params like EEM_Base::get_all
2430
+	 * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
2431
+	 *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
2432
+	 *                             results if you want IDs
2433
+	 * @throws ReflectionException
2434
+	 * @throws InvalidArgumentException
2435
+	 * @throws InvalidInterfaceException
2436
+	 * @throws InvalidDataTypeException
2437
+	 * @throws EE_Error
2438
+	 */
2439
+	public function get_many_related($relationName, $query_params = array())
2440
+	{
2441
+		if ($this->ID()) {
2442
+			//this exists in the DB, so get the related things from either the cache or the DB
2443
+			//if there are query parameters, forget about caching the related model objects.
2444
+			if ($query_params) {
2445
+				$related_model_objects = $this->get_model()->get_all_related(
2446
+					$this,
2447
+					$relationName,
2448
+					$query_params
2449
+				);
2450
+			} else {
2451
+				//did we already cache the result of this query?
2452
+				$cached_results = $this->get_all_from_cache($relationName);
2453
+				if (! $cached_results) {
2454
+					$related_model_objects = $this->get_model()->get_all_related(
2455
+						$this,
2456
+						$relationName,
2457
+						$query_params
2458
+					);
2459
+					//if no query parameters were passed, then we got all the related model objects
2460
+					//for that relation. We can cache them then.
2461
+					foreach ($related_model_objects as $related_model_object) {
2462
+						$this->cache($relationName, $related_model_object);
2463
+					}
2464
+				} else {
2465
+					$related_model_objects = $cached_results;
2466
+				}
2467
+			}
2468
+		} else {
2469
+			//this doesn't exist in the DB, so just get the related things from the cache
2470
+			$related_model_objects = $this->get_all_from_cache($relationName);
2471
+		}
2472
+		return $related_model_objects;
2473
+	}
2474
+
2475
+
2476
+	/**
2477
+	 * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2478
+	 * unless otherwise specified in the $query_params
2479
+	 *
2480
+	 * @param string $relation_name  model_name like 'Event', or 'Registration'
2481
+	 * @param array  $query_params   like EEM_Base::get_all's
2482
+	 * @param string $field_to_count name of field to count by. By default, uses primary key
2483
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2484
+	 *                               that by the setting $distinct to TRUE;
2485
+	 * @return int
2486
+	 * @throws ReflectionException
2487
+	 * @throws InvalidArgumentException
2488
+	 * @throws InvalidInterfaceException
2489
+	 * @throws InvalidDataTypeException
2490
+	 * @throws EE_Error
2491
+	 */
2492
+	public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2493
+	{
2494
+		return $this->get_model()->count_related(
2495
+			$this,
2496
+			$relation_name,
2497
+			$query_params,
2498
+			$field_to_count,
2499
+			$distinct
2500
+		);
2501
+	}
2502
+
2503
+
2504
+	/**
2505
+	 * Instead of getting the related model objects, simply sums up the values of the specified field.
2506
+	 * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2507
+	 *
2508
+	 * @param string $relation_name model_name like 'Event', or 'Registration'
2509
+	 * @param array  $query_params  like EEM_Base::get_all's
2510
+	 * @param string $field_to_sum  name of field to count by.
2511
+	 *                              By default, uses primary key
2512
+	 *                              (which doesn't make much sense, so you should probably change it)
2513
+	 * @return int
2514
+	 * @throws ReflectionException
2515
+	 * @throws InvalidArgumentException
2516
+	 * @throws InvalidInterfaceException
2517
+	 * @throws InvalidDataTypeException
2518
+	 * @throws EE_Error
2519
+	 */
2520
+	public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2521
+	{
2522
+		return $this->get_model()->sum_related(
2523
+			$this,
2524
+			$relation_name,
2525
+			$query_params,
2526
+			$field_to_sum
2527
+		);
2528
+	}
2529
+
2530
+
2531
+	/**
2532
+	 * Gets the first (ie, one) related model object of the specified type.
2533
+	 *
2534
+	 * @param string $relationName key in the model's _model_relations array
2535
+	 * @param array  $query_params like EEM_Base::get_all
2536
+	 * @return EE_Base_Class (not an array, a single object)
2537
+	 * @throws ReflectionException
2538
+	 * @throws InvalidArgumentException
2539
+	 * @throws InvalidInterfaceException
2540
+	 * @throws InvalidDataTypeException
2541
+	 * @throws EE_Error
2542
+	 */
2543
+	public function get_first_related($relationName, $query_params = array())
2544
+	{
2545
+		$model = $this->get_model();
2546
+		if ($this->ID()) {//this exists in the DB, get from the cache OR the DB
2547
+			//if they've provided some query parameters, don't bother trying to cache the result
2548
+			//also make sure we're not caching the result of get_first_related
2549
+			//on a relation which should have an array of objects (because the cache might have an array of objects)
2550
+			if ($query_params
2551
+				|| ! $model->related_settings_for($relationName)
2552
+					 instanceof
2553
+					 EE_Belongs_To_Relation
2554
+			) {
2555
+				$related_model_object = $model->get_first_related(
2556
+					$this,
2557
+					$relationName,
2558
+					$query_params
2559
+				);
2560
+			} else {
2561
+				//first, check if we've already cached the result of this query
2562
+				$cached_result = $this->get_one_from_cache($relationName);
2563
+				if (! $cached_result) {
2564
+					$related_model_object = $model->get_first_related(
2565
+						$this,
2566
+						$relationName,
2567
+						$query_params
2568
+					);
2569
+					$this->cache($relationName, $related_model_object);
2570
+				} else {
2571
+					$related_model_object = $cached_result;
2572
+				}
2573
+			}
2574
+		} else {
2575
+			$related_model_object = null;
2576
+			// this doesn't exist in the Db,
2577
+			// but maybe the relation is of type belongs to, and so the related thing might
2578
+			if ($model->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2579
+				$related_model_object = $model->get_first_related(
2580
+					$this,
2581
+					$relationName,
2582
+					$query_params
2583
+				);
2584
+			}
2585
+			// this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2586
+			// just get what's cached on this object
2587
+			if (! $related_model_object) {
2588
+				$related_model_object = $this->get_one_from_cache($relationName);
2589
+			}
2590
+		}
2591
+		return $related_model_object;
2592
+	}
2593
+
2594
+
2595
+	/**
2596
+	 * Does a delete on all related objects of type $relationName and removes
2597
+	 * the current model object's relation to them. If they can't be deleted (because
2598
+	 * of blocking related model objects) does nothing. If the related model objects are
2599
+	 * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2600
+	 * If this model object doesn't exist yet in the DB, just removes its related things
2601
+	 *
2602
+	 * @param string $relationName
2603
+	 * @param array  $query_params like EEM_Base::get_all's
2604
+	 * @return int how many deleted
2605
+	 * @throws ReflectionException
2606
+	 * @throws InvalidArgumentException
2607
+	 * @throws InvalidInterfaceException
2608
+	 * @throws InvalidDataTypeException
2609
+	 * @throws EE_Error
2610
+	 */
2611
+	public function delete_related($relationName, $query_params = array())
2612
+	{
2613
+		if ($this->ID()) {
2614
+			$count = $this->get_model()->delete_related(
2615
+				$this,
2616
+				$relationName,
2617
+				$query_params
2618
+			);
2619
+		} else {
2620
+			$count = count($this->get_all_from_cache($relationName));
2621
+			$this->clear_cache($relationName, null, true);
2622
+		}
2623
+		return $count;
2624
+	}
2625
+
2626
+
2627
+	/**
2628
+	 * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2629
+	 * the current model object's relation to them. If they can't be deleted (because
2630
+	 * of blocking related model objects) just does a soft delete on it instead, if possible.
2631
+	 * If the related thing isn't a soft-deletable model object, this function is identical
2632
+	 * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2633
+	 *
2634
+	 * @param string $relationName
2635
+	 * @param array  $query_params like EEM_Base::get_all's
2636
+	 * @return int how many deleted (including those soft deleted)
2637
+	 * @throws ReflectionException
2638
+	 * @throws InvalidArgumentException
2639
+	 * @throws InvalidInterfaceException
2640
+	 * @throws InvalidDataTypeException
2641
+	 * @throws EE_Error
2642
+	 */
2643
+	public function delete_related_permanently($relationName, $query_params = array())
2644
+	{
2645
+		if ($this->ID()) {
2646
+			$count = $this->get_model()->delete_related_permanently(
2647
+				$this,
2648
+				$relationName,
2649
+				$query_params
2650
+			);
2651
+		} else {
2652
+			$count = count($this->get_all_from_cache($relationName));
2653
+		}
2654
+		$this->clear_cache($relationName, null, true);
2655
+		return $count;
2656
+	}
2657
+
2658
+
2659
+	/**
2660
+	 * is_set
2661
+	 * Just a simple utility function children can use for checking if property exists
2662
+	 *
2663
+	 * @access  public
2664
+	 * @param  string $field_name property to check
2665
+	 * @return bool                              TRUE if existing,FALSE if not.
2666
+	 */
2667
+	public function is_set($field_name)
2668
+	{
2669
+		return isset($this->_fields[ $field_name ]);
2670
+	}
2671
+
2672
+
2673
+	/**
2674
+	 * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2675
+	 * EE_Error exception if they don't
2676
+	 *
2677
+	 * @param  mixed (string|array) $properties properties to check
2678
+	 * @throws EE_Error
2679
+	 * @return bool                              TRUE if existing, throw EE_Error if not.
2680
+	 */
2681
+	protected function _property_exists($properties)
2682
+	{
2683
+		foreach ((array) $properties as $property_name) {
2684
+			//first make sure this property exists
2685
+			if (! $this->_fields[ $property_name ]) {
2686
+				throw new EE_Error(
2687
+					sprintf(
2688
+						esc_html__(
2689
+							'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2690
+							'event_espresso'
2691
+						),
2692
+						$property_name
2693
+					)
2694
+				);
2695
+			}
2696
+		}
2697
+		return true;
2698
+	}
2699
+
2700
+
2701
+	/**
2702
+	 * This simply returns an array of model fields for this object
2703
+	 *
2704
+	 * @return array
2705
+	 * @throws ReflectionException
2706
+	 * @throws InvalidArgumentException
2707
+	 * @throws InvalidInterfaceException
2708
+	 * @throws InvalidDataTypeException
2709
+	 * @throws EE_Error
2710
+	 */
2711
+	public function model_field_array()
2712
+	{
2713
+		$fields     = $this->get_model()->field_settings(false);
2714
+		$properties = array();
2715
+		//remove prepended underscore
2716
+		foreach ($fields as $field_name => $settings) {
2717
+			$properties[ $field_name ] = $this->get($field_name);
2718
+		}
2719
+		return $properties;
2720
+	}
2721
+
2722
+
2723
+	/**
2724
+	 * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2725
+	 * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2726
+	 * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2727
+	 * Instead of requiring a plugin to extend the EE_Base_Class
2728
+	 * (which works fine is there's only 1 plugin, but when will that happen?)
2729
+	 * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2730
+	 * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2731
+	 * and accepts 2 arguments: the object on which the function was called,
2732
+	 * and an array of the original arguments passed to the function.
2733
+	 * Whatever their callback function returns will be returned by this function.
2734
+	 * Example: in functions.php (or in a plugin):
2735
+	 *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2736
+	 *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2737
+	 *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2738
+	 *          return $previousReturnValue.$returnString;
2739
+	 *      }
2740
+	 * require('EE_Answer.class.php');
2741
+	 * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2742
+	 * echo $answer->my_callback('monkeys',100);
2743
+	 * //will output "you called my_callback! and passed args:monkeys,100"
2744
+	 *
2745
+	 * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2746
+	 * @param array  $args       array of original arguments passed to the function
2747
+	 * @throws EE_Error
2748
+	 * @return mixed whatever the plugin which calls add_filter decides
2749
+	 */
2750
+	public function __call($methodName, $args)
2751
+	{
2752
+		$className = get_class($this);
2753
+		$tagName   = "FHEE__{$className}__{$methodName}";
2754
+		if (! has_filter($tagName)) {
2755
+			throw new EE_Error(
2756
+				sprintf(
2757
+					esc_html__(
2758
+						"Method %s on class %s does not exist! You can create one with the following code in functions.php or in a plugin: add_filter('%s','my_callback',10,3);function my_callback(\$previousReturnValue,EE_Base_Class \$object, \$argsArray){/*function body*/return \$whatever;}",
2759
+						'event_espresso'
2760
+					),
2761
+					$methodName,
2762
+					$className,
2763
+					$tagName
2764
+				)
2765
+			);
2766
+		}
2767
+		return apply_filters($tagName, null, $this, $args);
2768
+	}
2769
+
2770
+
2771
+	/**
2772
+	 * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2773
+	 * A $previous_value can be specified in case there are many meta rows with the same key
2774
+	 *
2775
+	 * @param string $meta_key
2776
+	 * @param mixed  $meta_value
2777
+	 * @param mixed  $previous_value
2778
+	 * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2779
+	 *                  NOTE: if the values haven't changed, returns 0
2780
+	 * @throws InvalidArgumentException
2781
+	 * @throws InvalidInterfaceException
2782
+	 * @throws InvalidDataTypeException
2783
+	 * @throws EE_Error
2784
+	 * @throws ReflectionException
2785
+	 */
2786
+	public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2787
+	{
2788
+		$query_params = array(
2789
+			array(
2790
+				'EXM_key'  => $meta_key,
2791
+				'OBJ_ID'   => $this->ID(),
2792
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2793
+			),
2794
+		);
2795
+		if ($previous_value !== null) {
2796
+			$query_params[0]['EXM_value'] = $meta_value;
2797
+		}
2798
+		$existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2799
+		if (! $existing_rows_like_that) {
2800
+			return $this->add_extra_meta($meta_key, $meta_value);
2801
+		}
2802
+		foreach ($existing_rows_like_that as $existing_row) {
2803
+			$existing_row->save(array('EXM_value' => $meta_value));
2804
+		}
2805
+		return count($existing_rows_like_that);
2806
+	}
2807
+
2808
+
2809
+	/**
2810
+	 * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2811
+	 * no other extra meta for this model object have the same key. Returns TRUE if the
2812
+	 * extra meta row was entered, false if not
2813
+	 *
2814
+	 * @param string  $meta_key
2815
+	 * @param mixed   $meta_value
2816
+	 * @param boolean $unique
2817
+	 * @return boolean
2818
+	 * @throws InvalidArgumentException
2819
+	 * @throws InvalidInterfaceException
2820
+	 * @throws InvalidDataTypeException
2821
+	 * @throws EE_Error
2822
+	 * @throws ReflectionException
2823
+	 * @throws ReflectionException
2824
+	 */
2825
+	public function add_extra_meta($meta_key, $meta_value, $unique = false)
2826
+	{
2827
+		if ($unique) {
2828
+			$existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2829
+				array(
2830
+					array(
2831
+						'EXM_key'  => $meta_key,
2832
+						'OBJ_ID'   => $this->ID(),
2833
+						'EXM_type' => $this->get_model()->get_this_model_name(),
2834
+					),
2835
+				)
2836
+			);
2837
+			if ($existing_extra_meta) {
2838
+				return false;
2839
+			}
2840
+		}
2841
+		$new_extra_meta = EE_Extra_Meta::new_instance(
2842
+			array(
2843
+				'EXM_key'   => $meta_key,
2844
+				'EXM_value' => $meta_value,
2845
+				'OBJ_ID'    => $this->ID(),
2846
+				'EXM_type'  => $this->get_model()->get_this_model_name(),
2847
+			)
2848
+		);
2849
+		$new_extra_meta->save();
2850
+		return true;
2851
+	}
2852
+
2853
+
2854
+	/**
2855
+	 * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2856
+	 * is specified, only deletes extra meta records with that value.
2857
+	 *
2858
+	 * @param string $meta_key
2859
+	 * @param mixed  $meta_value
2860
+	 * @return int number of extra meta rows deleted
2861
+	 * @throws InvalidArgumentException
2862
+	 * @throws InvalidInterfaceException
2863
+	 * @throws InvalidDataTypeException
2864
+	 * @throws EE_Error
2865
+	 * @throws ReflectionException
2866
+	 */
2867
+	public function delete_extra_meta($meta_key, $meta_value = null)
2868
+	{
2869
+		$query_params = array(
2870
+			array(
2871
+				'EXM_key'  => $meta_key,
2872
+				'OBJ_ID'   => $this->ID(),
2873
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2874
+			),
2875
+		);
2876
+		if ($meta_value !== null) {
2877
+			$query_params[0]['EXM_value'] = $meta_value;
2878
+		}
2879
+		return EEM_Extra_Meta::instance()->delete($query_params);
2880
+	}
2881
+
2882
+
2883
+	/**
2884
+	 * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2885
+	 * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2886
+	 * You can specify $default is case you haven't found the extra meta
2887
+	 *
2888
+	 * @param string  $meta_key
2889
+	 * @param boolean $single
2890
+	 * @param mixed   $default if we don't find anything, what should we return?
2891
+	 * @return mixed single value if $single; array if ! $single
2892
+	 * @throws ReflectionException
2893
+	 * @throws InvalidArgumentException
2894
+	 * @throws InvalidInterfaceException
2895
+	 * @throws InvalidDataTypeException
2896
+	 * @throws EE_Error
2897
+	 */
2898
+	public function get_extra_meta($meta_key, $single = false, $default = null)
2899
+	{
2900
+		if ($single) {
2901
+			$result = $this->get_first_related(
2902
+				'Extra_Meta',
2903
+				array(array('EXM_key' => $meta_key))
2904
+			);
2905
+			if ($result instanceof EE_Extra_Meta) {
2906
+				return $result->value();
2907
+			}
2908
+		} else {
2909
+			$results = $this->get_many_related(
2910
+				'Extra_Meta',
2911
+				array(array('EXM_key' => $meta_key))
2912
+			);
2913
+			if ($results) {
2914
+				$values = array();
2915
+				foreach ($results as $result) {
2916
+					if ($result instanceof EE_Extra_Meta) {
2917
+						$values[ $result->ID() ] = $result->value();
2918
+					}
2919
+				}
2920
+				return $values;
2921
+			}
2922
+		}
2923
+		//if nothing discovered yet return default.
2924
+		return apply_filters(
2925
+			'FHEE__EE_Base_Class__get_extra_meta__default_value',
2926
+			$default,
2927
+			$meta_key,
2928
+			$single,
2929
+			$this
2930
+		);
2931
+	}
2932
+
2933
+
2934
+	/**
2935
+	 * Returns a simple array of all the extra meta associated with this model object.
2936
+	 * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2937
+	 * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2938
+	 * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2939
+	 * If $one_of_each_key is false, it will return an array with the top-level keys being
2940
+	 * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2941
+	 * finally the extra meta's value as each sub-value. (eg
2942
+	 * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2943
+	 *
2944
+	 * @param boolean $one_of_each_key
2945
+	 * @return array
2946
+	 * @throws ReflectionException
2947
+	 * @throws InvalidArgumentException
2948
+	 * @throws InvalidInterfaceException
2949
+	 * @throws InvalidDataTypeException
2950
+	 * @throws EE_Error
2951
+	 */
2952
+	public function all_extra_meta_array($one_of_each_key = true)
2953
+	{
2954
+		$return_array = array();
2955
+		if ($one_of_each_key) {
2956
+			$extra_meta_objs = $this->get_many_related(
2957
+				'Extra_Meta',
2958
+				array('group_by' => 'EXM_key')
2959
+			);
2960
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2961
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2962
+					$return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2963
+				}
2964
+			}
2965
+		} else {
2966
+			$extra_meta_objs = $this->get_many_related('Extra_Meta');
2967
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2968
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2969
+					if (! isset($return_array[ $extra_meta_obj->key() ])) {
2970
+						$return_array[ $extra_meta_obj->key() ] = array();
2971
+					}
2972
+					$return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2973
+				}
2974
+			}
2975
+		}
2976
+		return $return_array;
2977
+	}
2978
+
2979
+
2980
+	/**
2981
+	 * Gets a pretty nice displayable nice for this model object. Often overridden
2982
+	 *
2983
+	 * @return string
2984
+	 * @throws ReflectionException
2985
+	 * @throws InvalidArgumentException
2986
+	 * @throws InvalidInterfaceException
2987
+	 * @throws InvalidDataTypeException
2988
+	 * @throws EE_Error
2989
+	 */
2990
+	public function name()
2991
+	{
2992
+		//find a field that's not a text field
2993
+		$field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
2994
+		if ($field_we_can_use) {
2995
+			return $this->get($field_we_can_use->get_name());
2996
+		}
2997
+		$first_few_properties = $this->model_field_array();
2998
+		$first_few_properties = array_slice($first_few_properties, 0, 3);
2999
+		$name_parts           = array();
3000
+		foreach ($first_few_properties as $name => $value) {
3001
+			$name_parts[] = "$name:$value";
3002
+		}
3003
+		return implode(',', $name_parts);
3004
+	}
3005
+
3006
+
3007
+	/**
3008
+	 * in_entity_map
3009
+	 * Checks if this model object has been proven to already be in the entity map
3010
+	 *
3011
+	 * @return boolean
3012
+	 * @throws ReflectionException
3013
+	 * @throws InvalidArgumentException
3014
+	 * @throws InvalidInterfaceException
3015
+	 * @throws InvalidDataTypeException
3016
+	 * @throws EE_Error
3017
+	 */
3018
+	public function in_entity_map()
3019
+	{
3020
+		// well, if we looked, did we find it in the entity map?
3021
+		return $this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this;
3022
+	}
3023
+
3024
+
3025
+	/**
3026
+	 * refresh_from_db
3027
+	 * Makes sure the fields and values on this model object are in-sync with what's in the database.
3028
+	 *
3029
+	 * @throws ReflectionException
3030
+	 * @throws InvalidArgumentException
3031
+	 * @throws InvalidInterfaceException
3032
+	 * @throws InvalidDataTypeException
3033
+	 * @throws EE_Error if this model object isn't in the entity mapper (because then you should
3034
+	 * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
3035
+	 */
3036
+	public function refresh_from_db()
3037
+	{
3038
+		if ($this->ID() && $this->in_entity_map()) {
3039
+			$this->get_model()->refresh_entity_map_from_db($this->ID());
3040
+		} else {
3041
+			//if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
3042
+			//if it has an ID but it's not in the map, and you're asking me to refresh it
3043
+			//that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
3044
+			//absolutely nothing in it for this ID
3045
+			if (WP_DEBUG) {
3046
+				throw new EE_Error(
3047
+					sprintf(
3048
+						esc_html__('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3049
+							'event_espresso'),
3050
+						$this->ID(),
3051
+						get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3052
+						get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3053
+					)
3054
+				);
3055
+			}
3056
+		}
3057
+	}
3058
+
3059
+
3060
+	/**
3061
+	 * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3062
+	 * (probably a bad assumption they have made, oh well)
3063
+	 *
3064
+	 * @return string
3065
+	 */
3066
+	public function __toString()
3067
+	{
3068
+		try {
3069
+			return sprintf('%s (%s)', $this->name(), $this->ID());
3070
+		} catch (Exception $e) {
3071
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3072
+			return '';
3073
+		}
3074
+	}
3075
+
3076
+
3077
+	/**
3078
+	 * Clear related model objects if they're already in the DB, because otherwise when we
3079
+	 * UN-serialize this model object we'll need to be careful to add them to the entity map.
3080
+	 * This means if we have made changes to those related model objects, and want to unserialize
3081
+	 * the this model object on a subsequent request, changes to those related model objects will be lost.
3082
+	 * Instead, those related model objects should be directly serialized and stored.
3083
+	 * Eg, the following won't work:
3084
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3085
+	 * $att = $reg->attendee();
3086
+	 * $att->set( 'ATT_fname', 'Dirk' );
3087
+	 * update_option( 'my_option', serialize( $reg ) );
3088
+	 * //END REQUEST
3089
+	 * //START NEXT REQUEST
3090
+	 * $reg = get_option( 'my_option' );
3091
+	 * $reg->attendee()->save();
3092
+	 * And would need to be replace with:
3093
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3094
+	 * $att = $reg->attendee();
3095
+	 * $att->set( 'ATT_fname', 'Dirk' );
3096
+	 * update_option( 'my_option', serialize( $reg ) );
3097
+	 * //END REQUEST
3098
+	 * //START NEXT REQUEST
3099
+	 * $att = get_option( 'my_option' );
3100
+	 * $att->save();
3101
+	 *
3102
+	 * @return array
3103
+	 * @throws ReflectionException
3104
+	 * @throws InvalidArgumentException
3105
+	 * @throws InvalidInterfaceException
3106
+	 * @throws InvalidDataTypeException
3107
+	 * @throws EE_Error
3108
+	 */
3109
+	public function __sleep()
3110
+	{
3111
+		$model = $this->get_model();
3112
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3113
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
3114
+				$classname = 'EE_' . $model->get_this_model_name();
3115
+				if (
3116
+					$this->get_one_from_cache($relation_name) instanceof $classname
3117
+					&& $this->get_one_from_cache($relation_name)->ID()
3118
+				) {
3119
+					$this->clear_cache(
3120
+						$relation_name,
3121
+						$this->get_one_from_cache($relation_name)->ID()
3122
+					);
3123
+				}
3124
+			}
3125
+		}
3126
+		$this->_props_n_values_provided_in_constructor = array();
3127
+		$properties_to_serialize                       = get_object_vars($this);
3128
+		//don't serialize the model. It's big and that risks recursion
3129
+		unset($properties_to_serialize['_model']);
3130
+		return array_keys($properties_to_serialize);
3131
+	}
3132
+
3133
+
3134
+	/**
3135
+	 * restore _props_n_values_provided_in_constructor
3136
+	 * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3137
+	 * and therefore should NOT be used to determine if state change has occurred since initial construction.
3138
+	 * At best, you would only be able to detect if state change has occurred during THIS request.
3139
+	 */
3140
+	public function __wakeup()
3141
+	{
3142
+		$this->_props_n_values_provided_in_constructor = $this->_fields;
3143
+	}
3144
+
3145
+
3146
+	/**
3147
+	 * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3148
+	 * distinct with the clone host instance are also cloned.
3149
+	 */
3150
+	public function __clone()
3151
+	{
3152
+		//handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3153
+		foreach ($this->_fields as $field => $value) {
3154
+			if ($value instanceof DateTime) {
3155
+				$this->_fields[$field] = clone $value;
3156
+			}
3157
+		}
3158
+	}
3159 3159
 }
3160 3160
 
3161 3161
 
Please login to merge, or discard this patch.
Spacing   +115 added lines, -115 removed lines patch added patch discarded remove patch
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
         $fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
149 149
         // verify client code has not passed any invalid field names
150 150
         foreach ($fieldValues as $field_name => $field_value) {
151
-            if (! isset($model_fields[ $field_name ])) {
151
+            if ( ! isset($model_fields[$field_name])) {
152 152
                 throw new EE_Error(
153 153
                     sprintf(
154 154
                         esc_html__(
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
             }
164 164
         }
165 165
         $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
166
-        if (! empty($date_formats) && is_array($date_formats)) {
166
+        if ( ! empty($date_formats) && is_array($date_formats)) {
167 167
             list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
168 168
         } else {
169 169
             //set default formats for date and time
@@ -176,7 +176,7 @@  discard block
 block discarded – undo
176 176
             foreach ($model_fields as $fieldName => $field) {
177 177
                 $this->set_from_db(
178 178
                     $fieldName,
179
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
179
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null
180 180
                 );
181 181
             }
182 182
         } else {
@@ -185,22 +185,22 @@  discard block
 block discarded – undo
185 185
             foreach ($model_fields as $fieldName => $field) {
186 186
                 $this->set(
187 187
                     $fieldName,
188
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null, true
188
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null, true
189 189
                 );
190 190
             }
191 191
         }
192 192
         //remember what values were passed to this constructor
193 193
         $this->_props_n_values_provided_in_constructor = $fieldValues;
194 194
         //remember in entity mapper
195
-        if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
195
+        if ( ! $bydb && $model->has_primary_key_field() && $this->ID()) {
196 196
             $model->add_to_entity_map($this);
197 197
         }
198 198
         //setup all the relations
199 199
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
200 200
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
201
-                $this->_model_relations[ $relation_name ] = null;
201
+                $this->_model_relations[$relation_name] = null;
202 202
             } else {
203
-                $this->_model_relations[ $relation_name ] = array();
203
+                $this->_model_relations[$relation_name] = array();
204 204
             }
205 205
         }
206 206
         /**
@@ -251,10 +251,10 @@  discard block
 block discarded – undo
251 251
      */
252 252
     public function get_original($field_name)
253 253
     {
254
-        if (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
254
+        if (isset($this->_props_n_values_provided_in_constructor[$field_name])
255 255
             && $field_settings = $this->get_model()->field_settings_for($field_name)
256 256
         ) {
257
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
257
+            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[$field_name]);
258 258
         }
259 259
         return null;
260 260
     }
@@ -291,7 +291,7 @@  discard block
 block discarded – undo
291 291
         // then don't do anything
292 292
         if (
293 293
             ! $use_default
294
-            && $this->_fields[ $field_name ] === $field_value
294
+            && $this->_fields[$field_name] === $field_value
295 295
             && $this->ID()
296 296
         ) {
297 297
             return;
@@ -309,7 +309,7 @@  discard block
 block discarded – undo
309 309
             $holder_of_value = $field_obj->prepare_for_set($field_value);
310 310
             //should the value be null?
311 311
             if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
312
+                $this->_fields[$field_name] = $field_obj->get_default_value();
313 313
                 /**
314 314
                  * To save having to refactor all the models, if a default value is used for a
315 315
                  * EE_Datetime_Field, and that value is not null nor is it a DateTime
@@ -320,15 +320,15 @@  discard block
 block discarded – undo
320 320
                  */
321 321
                 if (
322 322
                     $field_obj instanceof EE_Datetime_Field
323
-                    && $this->_fields[ $field_name ] !== null
324
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
323
+                    && $this->_fields[$field_name] !== null
324
+                    && ! $this->_fields[$field_name] instanceof DateTime
325 325
                 ) {
326
-                    empty($this->_fields[ $field_name ])
326
+                    empty($this->_fields[$field_name])
327 327
                         ? $this->set($field_name, time())
328
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
328
+                        : $this->set($field_name, $this->_fields[$field_name]);
329 329
                 }
330 330
             } else {
331
-                $this->_fields[ $field_name ] = $holder_of_value;
331
+                $this->_fields[$field_name] = $holder_of_value;
332 332
             }
333 333
             //if we're not in the constructor...
334 334
             //now check if what we set was a primary key
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
                 $fields_on_model = self::_get_model(get_class($this))->field_settings();
346 346
                 $obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347 347
                 foreach ($fields_on_model as $field_obj) {
348
-                    if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
348
+                    if ( ! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
349 349
                         && $field_obj->get_name() !== $field_name
350 350
                     ) {
351 351
                         $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
@@ -390,8 +390,8 @@  discard block
 block discarded – undo
390 390
      */
391 391
     public function getCustomSelect($alias)
392 392
     {
393
-        return isset($this->custom_selection_results[ $alias ])
394
-            ? $this->custom_selection_results[ $alias ]
393
+        return isset($this->custom_selection_results[$alias])
394
+            ? $this->custom_selection_results[$alias]
395 395
             : null;
396 396
     }
397 397
 
@@ -478,7 +478,7 @@  discard block
 block discarded – undo
478 478
         foreach ($model_fields as $field_name => $field_obj) {
479 479
             if ($field_obj instanceof EE_Datetime_Field) {
480 480
                 $field_obj->set_timezone($this->_timezone);
481
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
481
+                if (isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime) {
482 482
                     EEH_DTT_Helper::setTimezone($this->_fields[$field_name], new DateTimeZone($this->_timezone));
483 483
                 }
484 484
             }
@@ -537,7 +537,7 @@  discard block
 block discarded – undo
537 537
      */
538 538
     public function get_format($full = true)
539 539
     {
540
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
540
+        return $full ? $this->_dt_frmt.' '.$this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
541 541
     }
542 542
 
543 543
 
@@ -563,11 +563,11 @@  discard block
 block discarded – undo
563 563
     public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
564 564
     {
565 565
         // its entirely possible that there IS no related object yet in which case there is nothing to cache.
566
-        if (! $object_to_cache instanceof EE_Base_Class) {
566
+        if ( ! $object_to_cache instanceof EE_Base_Class) {
567 567
             return false;
568 568
         }
569 569
         // also get "how" the object is related, or throw an error
570
-        if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
570
+        if ( ! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
571 571
             throw new EE_Error(
572 572
                 sprintf(
573 573
                     esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
@@ -581,38 +581,38 @@  discard block
 block discarded – undo
581 581
             // if it's a "belongs to" relationship, then there's only one related model object
582 582
             // eg, if this is a registration, there's only 1 attendee for it
583 583
             // so for these model objects just set it to be cached
584
-            $this->_model_relations[ $relationName ] = $object_to_cache;
584
+            $this->_model_relations[$relationName] = $object_to_cache;
585 585
             $return                                  = true;
586 586
         } else {
587 587
             // otherwise, this is the "many" side of a one to many relationship,
588 588
             // so we'll add the object to the array of related objects for that type.
589 589
             // eg: if this is an event, there are many registrations for that event,
590 590
             // so we cache the registrations in an array
591
-            if (! is_array($this->_model_relations[ $relationName ])) {
591
+            if ( ! is_array($this->_model_relations[$relationName])) {
592 592
                 // if for some reason, the cached item is a model object,
593 593
                 // then stick that in the array, otherwise start with an empty array
594
-                $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
594
+                $this->_model_relations[$relationName] = $this->_model_relations[$relationName]
595 595
                                                            instanceof
596 596
                                                            EE_Base_Class
597
-                    ? array($this->_model_relations[ $relationName ]) : array();
597
+                    ? array($this->_model_relations[$relationName]) : array();
598 598
             }
599 599
             // first check for a cache_id which is normally empty
600
-            if (! empty($cache_id)) {
600
+            if ( ! empty($cache_id)) {
601 601
                 // if the cache_id exists, then it means we are purposely trying to cache this
602 602
                 // with a known key that can then be used to retrieve the object later on
603
-                $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
603
+                $this->_model_relations[$relationName][$cache_id] = $object_to_cache;
604 604
                 $return                                               = $cache_id;
605 605
             } elseif ($object_to_cache->ID()) {
606 606
                 // OR the cached object originally came from the db, so let's just use it's PK for an ID
607
-                $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
607
+                $this->_model_relations[$relationName][$object_to_cache->ID()] = $object_to_cache;
608 608
                 $return                                                            = $object_to_cache->ID();
609 609
             } else {
610 610
                 // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
611
-                $this->_model_relations[ $relationName ][] = $object_to_cache;
611
+                $this->_model_relations[$relationName][] = $object_to_cache;
612 612
                 // move the internal pointer to the end of the array
613
-                end($this->_model_relations[ $relationName ]);
613
+                end($this->_model_relations[$relationName]);
614 614
                 // and grab the key so that we can return it
615
-                $return = key($this->_model_relations[ $relationName ]);
615
+                $return = key($this->_model_relations[$relationName]);
616 616
             }
617 617
         }
618 618
         return $return;
@@ -638,7 +638,7 @@  discard block
 block discarded – undo
638 638
         //first make sure this property exists
639 639
         $this->get_model()->field_settings_for($fieldname);
640 640
         $cache_type                                            = empty($cache_type) ? 'standard' : $cache_type;
641
-        $this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
641
+        $this->_cached_properties[$fieldname][$cache_type] = $value;
642 642
     }
643 643
 
644 644
 
@@ -667,9 +667,9 @@  discard block
 block discarded – undo
667 667
         $model = $this->get_model();
668 668
         $model->field_settings_for($fieldname);
669 669
         $cache_type = $pretty ? 'pretty' : 'standard';
670
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
671
-        if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
672
-            return $this->_cached_properties[ $fieldname ][ $cache_type ];
670
+        $cache_type .= ! empty($extra_cache_ref) ? '_'.$extra_cache_ref : '';
671
+        if (isset($this->_cached_properties[$fieldname][$cache_type])) {
672
+            return $this->_cached_properties[$fieldname][$cache_type];
673 673
         }
674 674
         $value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
675 675
         $this->_set_cached_property($fieldname, $value, $cache_type);
@@ -697,12 +697,12 @@  discard block
 block discarded – undo
697 697
         if ($field_obj instanceof EE_Datetime_Field) {
698 698
             $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
699 699
         }
700
-        if (! isset($this->_fields[ $fieldname ])) {
701
-            $this->_fields[ $fieldname ] = null;
700
+        if ( ! isset($this->_fields[$fieldname])) {
701
+            $this->_fields[$fieldname] = null;
702 702
         }
703 703
         $value = $pretty
704
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
705
-            : $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
704
+            ? $field_obj->prepare_for_pretty_echoing($this->_fields[$fieldname], $extra_cache_ref)
705
+            : $field_obj->prepare_for_get($this->_fields[$fieldname]);
706 706
         return $value;
707 707
     }
708 708
 
@@ -760,8 +760,8 @@  discard block
 block discarded – undo
760 760
      */
761 761
     protected function _clear_cached_property($property_name)
762 762
     {
763
-        if (isset($this->_cached_properties[ $property_name ])) {
764
-            unset($this->_cached_properties[ $property_name ]);
763
+        if (isset($this->_cached_properties[$property_name])) {
764
+            unset($this->_cached_properties[$property_name]);
765 765
         }
766 766
     }
767 767
 
@@ -813,7 +813,7 @@  discard block
 block discarded – undo
813 813
     {
814 814
         $relationship_to_model = $this->get_model()->related_settings_for($relationName);
815 815
         $index_in_cache        = '';
816
-        if (! $relationship_to_model) {
816
+        if ( ! $relationship_to_model) {
817 817
             throw new EE_Error(
818 818
                 sprintf(
819 819
                     esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
@@ -824,21 +824,21 @@  discard block
 block discarded – undo
824 824
         }
825 825
         if ($clear_all) {
826 826
             $obj_removed                             = true;
827
-            $this->_model_relations[ $relationName ] = null;
827
+            $this->_model_relations[$relationName] = null;
828 828
         } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
829
-            $obj_removed                             = $this->_model_relations[ $relationName ];
830
-            $this->_model_relations[ $relationName ] = null;
829
+            $obj_removed                             = $this->_model_relations[$relationName];
830
+            $this->_model_relations[$relationName] = null;
831 831
         } else {
832 832
             if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
833 833
                 && $object_to_remove_or_index_into_array->ID()
834 834
             ) {
835 835
                 $index_in_cache = $object_to_remove_or_index_into_array->ID();
836
-                if (is_array($this->_model_relations[ $relationName ])
837
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
836
+                if (is_array($this->_model_relations[$relationName])
837
+                    && ! isset($this->_model_relations[$relationName][$index_in_cache])
838 838
                 ) {
839 839
                     $index_found_at = null;
840 840
                     //find this object in the array even though it has a different key
841
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
841
+                    foreach ($this->_model_relations[$relationName] as $index => $obj) {
842 842
                         /** @noinspection TypeUnsafeComparisonInspection */
843 843
                         if (
844 844
                             $obj instanceof EE_Base_Class
@@ -872,9 +872,9 @@  discard block
 block discarded – undo
872 872
             }
873 873
             //supposedly we've found it. But it could just be that the client code
874 874
             //provided a bad index/object
875
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
876
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
877
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
875
+            if (isset($this->_model_relations[$relationName][$index_in_cache])) {
876
+                $obj_removed = $this->_model_relations[$relationName][$index_in_cache];
877
+                unset($this->_model_relations[$relationName][$index_in_cache]);
878 878
             } else {
879 879
                 //that thing was never cached anyways.
880 880
                 $obj_removed = null;
@@ -905,7 +905,7 @@  discard block
 block discarded – undo
905 905
         $current_cache_id = ''
906 906
     ) {
907 907
         // verify that incoming object is of the correct type
908
-        $obj_class = 'EE_' . $relationName;
908
+        $obj_class = 'EE_'.$relationName;
909 909
         if ($newly_saved_object instanceof $obj_class) {
910 910
             /* @type EE_Base_Class $newly_saved_object */
911 911
             // now get the type of relation
@@ -913,17 +913,17 @@  discard block
 block discarded – undo
913 913
             // if this is a 1:1 relationship
914 914
             if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
915 915
                 // then just replace the cached object with the newly saved object
916
-                $this->_model_relations[ $relationName ] = $newly_saved_object;
916
+                $this->_model_relations[$relationName] = $newly_saved_object;
917 917
                 return true;
918 918
                 // or if it's some kind of sordid feral polyamorous relationship...
919 919
             }
920
-            if (is_array($this->_model_relations[ $relationName ])
921
-                      && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
920
+            if (is_array($this->_model_relations[$relationName])
921
+                      && isset($this->_model_relations[$relationName][$current_cache_id])
922 922
             ) {
923 923
                 // then remove the current cached item
924
-                unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
924
+                unset($this->_model_relations[$relationName][$current_cache_id]);
925 925
                 // and cache the newly saved object using it's new ID
926
-                $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
926
+                $this->_model_relations[$relationName][$newly_saved_object->ID()] = $newly_saved_object;
927 927
                 return true;
928 928
             }
929 929
         }
@@ -940,8 +940,8 @@  discard block
 block discarded – undo
940 940
      */
941 941
     public function get_one_from_cache($relationName)
942 942
     {
943
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
944
-            ? $this->_model_relations[ $relationName ]
943
+        $cached_array_or_object = isset($this->_model_relations[$relationName])
944
+            ? $this->_model_relations[$relationName]
945 945
             : null;
946 946
         if (is_array($cached_array_or_object)) {
947 947
             return array_shift($cached_array_or_object);
@@ -964,7 +964,7 @@  discard block
 block discarded – undo
964 964
      */
965 965
     public function get_all_from_cache($relationName)
966 966
     {
967
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
967
+        $objects = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName] : array();
968 968
         // if the result is not an array, but exists, make it an array
969 969
         $objects = is_array($objects) ? $objects : array($objects);
970 970
         //bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
@@ -1148,7 +1148,7 @@  discard block
 block discarded – undo
1148 1148
             } else {
1149 1149
                 $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1150 1150
             }
1151
-            $this->_fields[ $field_name ] = $field_value;
1151
+            $this->_fields[$field_name] = $field_value;
1152 1152
             $this->_clear_cached_property($field_name);
1153 1153
         }
1154 1154
     }
@@ -1188,9 +1188,9 @@  discard block
 block discarded – undo
1188 1188
     public function get_raw($field_name)
1189 1189
     {
1190 1190
         $field_settings = $this->get_model()->field_settings_for($field_name);
1191
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1192
-            ? $this->_fields[ $field_name ]->format('U')
1193
-            : $this->_fields[ $field_name ];
1191
+        return $field_settings instanceof EE_Datetime_Field && $this->_fields[$field_name] instanceof DateTime
1192
+            ? $this->_fields[$field_name]->format('U')
1193
+            : $this->_fields[$field_name];
1194 1194
     }
1195 1195
 
1196 1196
 
@@ -1212,7 +1212,7 @@  discard block
 block discarded – undo
1212 1212
     public function get_DateTime_object($field_name)
1213 1213
     {
1214 1214
         $field_settings = $this->get_model()->field_settings_for($field_name);
1215
-        if (! $field_settings instanceof EE_Datetime_Field) {
1215
+        if ( ! $field_settings instanceof EE_Datetime_Field) {
1216 1216
             EE_Error::add_error(
1217 1217
                 sprintf(
1218 1218
                     esc_html__(
@@ -1470,7 +1470,7 @@  discard block
 block discarded – undo
1470 1470
      */
1471 1471
     public function get_i18n_datetime($field_name, $format = '')
1472 1472
     {
1473
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1473
+        $format = empty($format) ? $this->_dt_frmt.' '.$this->_tm_frmt : $format;
1474 1474
         return date_i18n(
1475 1475
             $format,
1476 1476
             EEH_DTT_Helper::get_timestamp_with_offset(
@@ -1582,19 +1582,19 @@  discard block
 block discarded – undo
1582 1582
         $field->set_time_format($this->_tm_frmt);
1583 1583
         switch ($what) {
1584 1584
             case 'T' :
1585
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1585
+                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_time(
1586 1586
                     $datetime_value,
1587
-                    $this->_fields[ $fieldname ]
1587
+                    $this->_fields[$fieldname]
1588 1588
                 );
1589 1589
                 break;
1590 1590
             case 'D' :
1591
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1591
+                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_date(
1592 1592
                     $datetime_value,
1593
-                    $this->_fields[ $fieldname ]
1593
+                    $this->_fields[$fieldname]
1594 1594
                 );
1595 1595
                 break;
1596 1596
             case 'B' :
1597
-                $this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1597
+                $this->_fields[$fieldname] = $field->prepare_for_set($datetime_value);
1598 1598
                 break;
1599 1599
         }
1600 1600
         $this->_clear_cached_property($fieldname);
@@ -1636,7 +1636,7 @@  discard block
 block discarded – undo
1636 1636
         $this->set_timezone($timezone);
1637 1637
         $fn   = (array) $field_name;
1638 1638
         $args = array_merge($fn, (array) $args);
1639
-        if (! method_exists($this, $callback)) {
1639
+        if ( ! method_exists($this, $callback)) {
1640 1640
             throw new EE_Error(
1641 1641
                 sprintf(
1642 1642
                     esc_html__(
@@ -1648,7 +1648,7 @@  discard block
 block discarded – undo
1648 1648
             );
1649 1649
         }
1650 1650
         $args   = (array) $args;
1651
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1651
+        $return = $prepend.call_user_func_array(array($this, $callback), $args).$append;
1652 1652
         $this->set_timezone($original_timezone);
1653 1653
         return $return;
1654 1654
     }
@@ -1763,8 +1763,8 @@  discard block
 block discarded – undo
1763 1763
     {
1764 1764
         $model = $this->get_model();
1765 1765
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1766
-            if (! empty($this->_model_relations[ $relation_name ])) {
1767
-                $related_objects = $this->_model_relations[ $relation_name ];
1766
+            if ( ! empty($this->_model_relations[$relation_name])) {
1767
+                $related_objects = $this->_model_relations[$relation_name];
1768 1768
                 if ($relation_obj instanceof EE_Belongs_To_Relation) {
1769 1769
                     //this relation only stores a single model object, not an array
1770 1770
                     //but let's make it consistent
@@ -1821,7 +1821,7 @@  discard block
 block discarded – undo
1821 1821
             $this->set($column, $value);
1822 1822
         }
1823 1823
         // no changes ? then don't do anything
1824
-        if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1824
+        if ( ! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1825 1825
             return 0;
1826 1826
         }
1827 1827
         /**
@@ -1831,7 +1831,7 @@  discard block
 block discarded – undo
1831 1831
          * @param EE_Base_Class $model_object the model object about to be saved.
1832 1832
          */
1833 1833
         do_action('AHEE__EE_Base_Class__save__begin', $this);
1834
-        if (! $this->allow_persist()) {
1834
+        if ( ! $this->allow_persist()) {
1835 1835
             return 0;
1836 1836
         }
1837 1837
         // now get current attribute values
@@ -1846,10 +1846,10 @@  discard block
 block discarded – undo
1846 1846
         if ($model->has_primary_key_field()) {
1847 1847
             if ($model->get_primary_key_field()->is_auto_increment()) {
1848 1848
                 //ok check if it's set, if so: update; if not, insert
1849
-                if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1849
+                if ( ! empty($save_cols_n_values[$model->primary_key_name()])) {
1850 1850
                     $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1851 1851
                 } else {
1852
-                    unset($save_cols_n_values[ $model->primary_key_name() ]);
1852
+                    unset($save_cols_n_values[$model->primary_key_name()]);
1853 1853
                     $results = $model->insert($save_cols_n_values);
1854 1854
                     if ($results) {
1855 1855
                         //if successful, set the primary key
@@ -1859,7 +1859,7 @@  discard block
 block discarded – undo
1859 1859
                         //will get added to the mapper before we can add this one!
1860 1860
                         //but if we just avoid using the SET method, all that headache can be avoided
1861 1861
                         $pk_field_name                   = $model->primary_key_name();
1862
-                        $this->_fields[ $pk_field_name ] = $results;
1862
+                        $this->_fields[$pk_field_name] = $results;
1863 1863
                         $this->_clear_cached_property($pk_field_name);
1864 1864
                         $model->add_to_entity_map($this);
1865 1865
                         $this->_update_cached_related_model_objs_fks();
@@ -1876,8 +1876,8 @@  discard block
 block discarded – undo
1876 1876
                                     'event_espresso'
1877 1877
                                 ),
1878 1878
                                 get_class($this),
1879
-                                get_class($model) . '::instance()->add_to_entity_map()',
1880
-                                get_class($model) . '::instance()->get_one_by_ID()',
1879
+                                get_class($model).'::instance()->add_to_entity_map()',
1880
+                                get_class($model).'::instance()->get_one_by_ID()',
1881 1881
                                 '<br />'
1882 1882
                             )
1883 1883
                         );
@@ -1977,27 +1977,27 @@  discard block
 block discarded – undo
1977 1977
     public function save_new_cached_related_model_objs()
1978 1978
     {
1979 1979
         //make sure this has been saved
1980
-        if (! $this->ID()) {
1980
+        if ( ! $this->ID()) {
1981 1981
             $id = $this->save();
1982 1982
         } else {
1983 1983
             $id = $this->ID();
1984 1984
         }
1985 1985
         //now save all the NEW cached model objects  (ie they don't exist in the DB)
1986 1986
         foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1987
-            if ($this->_model_relations[ $relationName ]) {
1987
+            if ($this->_model_relations[$relationName]) {
1988 1988
                 //is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1989 1989
                 //or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1990 1990
                 /* @var $related_model_obj EE_Base_Class */
1991 1991
                 if ($relationObj instanceof EE_Belongs_To_Relation) {
1992 1992
                     //add a relation to that relation type (which saves the appropriate thing in the process)
1993 1993
                     //but ONLY if it DOES NOT exist in the DB
1994
-                    $related_model_obj = $this->_model_relations[ $relationName ];
1994
+                    $related_model_obj = $this->_model_relations[$relationName];
1995 1995
                     //					if( ! $related_model_obj->ID()){
1996 1996
                     $this->_add_relation_to($related_model_obj, $relationName);
1997 1997
                     $related_model_obj->save_new_cached_related_model_objs();
1998 1998
                     //					}
1999 1999
                 } else {
2000
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2000
+                    foreach ($this->_model_relations[$relationName] as $related_model_obj) {
2001 2001
                         //add a relation to that relation type (which saves the appropriate thing in the process)
2002 2002
                         //but ONLY if it DOES NOT exist in the DB
2003 2003
                         //						if( ! $related_model_obj->ID()){
@@ -2024,7 +2024,7 @@  discard block
 block discarded – undo
2024 2024
      */
2025 2025
     public function get_model()
2026 2026
     {
2027
-        if (! $this->_model) {
2027
+        if ( ! $this->_model) {
2028 2028
             $modelName    = self::_get_model_classname(get_class($this));
2029 2029
             $this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2030 2030
         } else {
@@ -2050,9 +2050,9 @@  discard block
 block discarded – undo
2050 2050
         $primary_id_ref = self::_get_primary_key_name($classname);
2051 2051
         if (
2052 2052
             array_key_exists($primary_id_ref, $props_n_values)
2053
-            && ! empty($props_n_values[ $primary_id_ref ])
2053
+            && ! empty($props_n_values[$primary_id_ref])
2054 2054
         ) {
2055
-            $id = $props_n_values[ $primary_id_ref ];
2055
+            $id = $props_n_values[$primary_id_ref];
2056 2056
             return self::_get_model($classname)->get_from_entity_map($id);
2057 2057
         }
2058 2058
         return false;
@@ -2086,10 +2086,10 @@  discard block
 block discarded – undo
2086 2086
         if ($model->has_primary_key_field()) {
2087 2087
             $primary_id_ref = self::_get_primary_key_name($classname);
2088 2088
             if (array_key_exists($primary_id_ref, $props_n_values)
2089
-                && ! empty($props_n_values[ $primary_id_ref ])
2089
+                && ! empty($props_n_values[$primary_id_ref])
2090 2090
             ) {
2091 2091
                 $existing = $model->get_one_by_ID(
2092
-                    $props_n_values[ $primary_id_ref ]
2092
+                    $props_n_values[$primary_id_ref]
2093 2093
                 );
2094 2094
             }
2095 2095
         } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
@@ -2101,7 +2101,7 @@  discard block
 block discarded – undo
2101 2101
         }
2102 2102
         if ($existing) {
2103 2103
             //set date formats if present before setting values
2104
-            if (! empty($date_formats) && is_array($date_formats)) {
2104
+            if ( ! empty($date_formats) && is_array($date_formats)) {
2105 2105
                 $existing->set_date_format($date_formats[0]);
2106 2106
                 $existing->set_time_format($date_formats[1]);
2107 2107
             } else {
@@ -2134,7 +2134,7 @@  discard block
 block discarded – undo
2134 2134
     protected static function _get_model($classname, $timezone = null)
2135 2135
     {
2136 2136
         //find model for this class
2137
-        if (! $classname) {
2137
+        if ( ! $classname) {
2138 2138
             throw new EE_Error(
2139 2139
                 sprintf(
2140 2140
                     esc_html__(
@@ -2183,7 +2183,7 @@  discard block
 block discarded – undo
2183 2183
         if (strpos($model_name, 'EE_') === 0) {
2184 2184
             $model_classname = str_replace('EE_', 'EEM_', $model_name);
2185 2185
         } else {
2186
-            $model_classname = 'EEM_' . $model_name;
2186
+            $model_classname = 'EEM_'.$model_name;
2187 2187
         }
2188 2188
         return $model_classname;
2189 2189
     }
@@ -2202,7 +2202,7 @@  discard block
 block discarded – undo
2202 2202
      */
2203 2203
     protected static function _get_primary_key_name($classname = null)
2204 2204
     {
2205
-        if (! $classname) {
2205
+        if ( ! $classname) {
2206 2206
             throw new EE_Error(
2207 2207
                 sprintf(
2208 2208
                     esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
@@ -2232,7 +2232,7 @@  discard block
 block discarded – undo
2232 2232
         $model = $this->get_model();
2233 2233
         //now that we know the name of the variable, use a variable variable to get its value and return its
2234 2234
         if ($model->has_primary_key_field()) {
2235
-            return $this->_fields[ $model->primary_key_name() ];
2235
+            return $this->_fields[$model->primary_key_name()];
2236 2236
         }
2237 2237
         return $model->get_index_primary_key_string($this->_fields);
2238 2238
     }
@@ -2285,7 +2285,7 @@  discard block
 block discarded – undo
2285 2285
             }
2286 2286
         } else {
2287 2287
             //this thing doesn't exist in the DB,  so just cache it
2288
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2288
+            if ( ! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2289 2289
                 throw new EE_Error(
2290 2290
                     sprintf(
2291 2291
                         esc_html__(
@@ -2450,7 +2450,7 @@  discard block
 block discarded – undo
2450 2450
             } else {
2451 2451
                 //did we already cache the result of this query?
2452 2452
                 $cached_results = $this->get_all_from_cache($relationName);
2453
-                if (! $cached_results) {
2453
+                if ( ! $cached_results) {
2454 2454
                     $related_model_objects = $this->get_model()->get_all_related(
2455 2455
                         $this,
2456 2456
                         $relationName,
@@ -2560,7 +2560,7 @@  discard block
 block discarded – undo
2560 2560
             } else {
2561 2561
                 //first, check if we've already cached the result of this query
2562 2562
                 $cached_result = $this->get_one_from_cache($relationName);
2563
-                if (! $cached_result) {
2563
+                if ( ! $cached_result) {
2564 2564
                     $related_model_object = $model->get_first_related(
2565 2565
                         $this,
2566 2566
                         $relationName,
@@ -2584,7 +2584,7 @@  discard block
 block discarded – undo
2584 2584
             }
2585 2585
             // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2586 2586
             // just get what's cached on this object
2587
-            if (! $related_model_object) {
2587
+            if ( ! $related_model_object) {
2588 2588
                 $related_model_object = $this->get_one_from_cache($relationName);
2589 2589
             }
2590 2590
         }
@@ -2666,7 +2666,7 @@  discard block
 block discarded – undo
2666 2666
      */
2667 2667
     public function is_set($field_name)
2668 2668
     {
2669
-        return isset($this->_fields[ $field_name ]);
2669
+        return isset($this->_fields[$field_name]);
2670 2670
     }
2671 2671
 
2672 2672
 
@@ -2682,7 +2682,7 @@  discard block
 block discarded – undo
2682 2682
     {
2683 2683
         foreach ((array) $properties as $property_name) {
2684 2684
             //first make sure this property exists
2685
-            if (! $this->_fields[ $property_name ]) {
2685
+            if ( ! $this->_fields[$property_name]) {
2686 2686
                 throw new EE_Error(
2687 2687
                     sprintf(
2688 2688
                         esc_html__(
@@ -2714,7 +2714,7 @@  discard block
 block discarded – undo
2714 2714
         $properties = array();
2715 2715
         //remove prepended underscore
2716 2716
         foreach ($fields as $field_name => $settings) {
2717
-            $properties[ $field_name ] = $this->get($field_name);
2717
+            $properties[$field_name] = $this->get($field_name);
2718 2718
         }
2719 2719
         return $properties;
2720 2720
     }
@@ -2751,7 +2751,7 @@  discard block
 block discarded – undo
2751 2751
     {
2752 2752
         $className = get_class($this);
2753 2753
         $tagName   = "FHEE__{$className}__{$methodName}";
2754
-        if (! has_filter($tagName)) {
2754
+        if ( ! has_filter($tagName)) {
2755 2755
             throw new EE_Error(
2756 2756
                 sprintf(
2757 2757
                     esc_html__(
@@ -2796,7 +2796,7 @@  discard block
 block discarded – undo
2796 2796
             $query_params[0]['EXM_value'] = $meta_value;
2797 2797
         }
2798 2798
         $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2799
-        if (! $existing_rows_like_that) {
2799
+        if ( ! $existing_rows_like_that) {
2800 2800
             return $this->add_extra_meta($meta_key, $meta_value);
2801 2801
         }
2802 2802
         foreach ($existing_rows_like_that as $existing_row) {
@@ -2914,7 +2914,7 @@  discard block
 block discarded – undo
2914 2914
                 $values = array();
2915 2915
                 foreach ($results as $result) {
2916 2916
                     if ($result instanceof EE_Extra_Meta) {
2917
-                        $values[ $result->ID() ] = $result->value();
2917
+                        $values[$result->ID()] = $result->value();
2918 2918
                     }
2919 2919
                 }
2920 2920
                 return $values;
@@ -2959,17 +2959,17 @@  discard block
 block discarded – undo
2959 2959
             );
2960 2960
             foreach ($extra_meta_objs as $extra_meta_obj) {
2961 2961
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2962
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2962
+                    $return_array[$extra_meta_obj->key()] = $extra_meta_obj->value();
2963 2963
                 }
2964 2964
             }
2965 2965
         } else {
2966 2966
             $extra_meta_objs = $this->get_many_related('Extra_Meta');
2967 2967
             foreach ($extra_meta_objs as $extra_meta_obj) {
2968 2968
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2969
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2970
-                        $return_array[ $extra_meta_obj->key() ] = array();
2969
+                    if ( ! isset($return_array[$extra_meta_obj->key()])) {
2970
+                        $return_array[$extra_meta_obj->key()] = array();
2971 2971
                     }
2972
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2972
+                    $return_array[$extra_meta_obj->key()][$extra_meta_obj->ID()] = $extra_meta_obj->value();
2973 2973
                 }
2974 2974
             }
2975 2975
         }
@@ -3048,8 +3048,8 @@  discard block
 block discarded – undo
3048 3048
                         esc_html__('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3049 3049
                             'event_espresso'),
3050 3050
                         $this->ID(),
3051
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3052
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3051
+                        get_class($this->get_model()).'::instance()->add_to_entity_map()',
3052
+                        get_class($this->get_model()).'::instance()->refresh_entity_map()'
3053 3053
                     )
3054 3054
                 );
3055 3055
             }
@@ -3111,7 +3111,7 @@  discard block
 block discarded – undo
3111 3111
         $model = $this->get_model();
3112 3112
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3113 3113
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
3114
-                $classname = 'EE_' . $model->get_this_model_name();
3114
+                $classname = 'EE_'.$model->get_this_model_name();
3115 3115
                 if (
3116 3116
                     $this->get_one_from_cache($relation_name) instanceof $classname
3117 3117
                     && $this->get_one_from_cache($relation_name)->ID()
Please login to merge, or discard this patch.
core/data_migration_scripts/EE_DMS_Core_4_1_0.dms.php 1 patch
Indentation   +1137 added lines, -1137 removed lines patch added patch discarded remove patch
@@ -12,11 +12,11 @@  discard block
 block discarded – undo
12 12
 $stages = glob(EE_CORE . 'data_migration_scripts/4_1_0_stages/*');
13 13
 $class_to_filepath = array();
14 14
 if ( ! empty($stages)) {
15
-    foreach ($stages as $filepath) {
16
-        $matches = array();
17
-        preg_match('~4_1_0_stages/(.*).dmsstage.php~', $filepath, $matches);
18
-        $class_to_filepath[$matches[1]] = $filepath;
19
-    }
15
+	foreach ($stages as $filepath) {
16
+		$matches = array();
17
+		preg_match('~4_1_0_stages/(.*).dmsstage.php~', $filepath, $matches);
18
+		$class_to_filepath[$matches[1]] = $filepath;
19
+	}
20 20
 }
21 21
 //give addons a chance to autoload their stages too
22 22
 $class_to_filepath = apply_filters('FHEE__EE_DMS_4_1_0__autoloaded_stages', $class_to_filepath);
@@ -44,91 +44,91 @@  discard block
 block discarded – undo
44 44
 
45 45
 
46 46
 
47
-    /**
48
-     * EE_DMS_Core_4_1_0 constructor.
49
-     *
50
-     * @param TableManager  $table_manager
51
-     * @param TableAnalysis $table_analysis
52
-     */
53
-    public function __construct(TableManager $table_manager = null, TableAnalysis $table_analysis = null)
54
-    {
55
-        $this->_pretty_name = esc_html__("Data Migration from Event Espresso 3 to Event Espresso 4.1.0", "event_espresso");
56
-        $this->_priority = 10;
57
-        $this->_migration_stages = array(
58
-                new EE_DMS_4_1_0_org_options(),
59
-                new EE_DMS_4_1_0_shortcodes(),
60
-                new EE_DMS_4_1_0_gateways(),
61
-                new EE_DMS_4_1_0_events(),
62
-                new EE_DMS_4_1_0_prices(),
63
-                new EE_DMS_4_1_0_category_details(),
64
-                new EE_DMS_4_1_0_event_category(),
65
-                new EE_DMS_4_1_0_venues(),
66
-                new EE_DMS_4_1_0_event_venue(),
67
-                new EE_DMS_4_1_0_question_groups(),
68
-                new EE_DMS_4_1_0_questions(),
69
-                new EE_DMS_4_1_0_question_group_question(),
70
-                new EE_DMS_4_1_0_event_question_group(),
71
-                new EE_DMS_4_1_0_attendees(),
72
-                new EE_DMS_4_1_0_line_items(),
73
-                new EE_DMS_4_1_0_answers(),
74
-                new EE_DMS_4_1_0_checkins(),
75
-        );
76
-        parent::__construct($table_manager, $table_analysis);
77
-    }
78
-
79
-
80
-
81
-    /**
82
-     * Checks if this 3.1 Check-in table exists. If it doesn't we can't migrate Check-ins
83
-     *
84
-     * @global wpdb $wpdb
85
-     * @return boolean
86
-     */
87
-    private function _checkin_table_exists()
88
-    {
89
-        global $wpdb;
90
-        $results = $wpdb->get_results("SHOW TABLES LIKE '" . $wpdb->prefix . "events_attendee_checkin" . "'");
91
-        if ($results) {
92
-            return true;
93
-        } else {
94
-            return false;
95
-        }
96
-    }
97
-
98
-
99
-
100
-    public function can_migrate_from_version($version_array)
101
-    {
102
-        $version_string = $version_array['Core'];
103
-        if (version_compare($version_string, '4.0.0', '<=') && version_compare($version_string, '3.1.26', '>=')) {
47
+	/**
48
+	 * EE_DMS_Core_4_1_0 constructor.
49
+	 *
50
+	 * @param TableManager  $table_manager
51
+	 * @param TableAnalysis $table_analysis
52
+	 */
53
+	public function __construct(TableManager $table_manager = null, TableAnalysis $table_analysis = null)
54
+	{
55
+		$this->_pretty_name = esc_html__("Data Migration from Event Espresso 3 to Event Espresso 4.1.0", "event_espresso");
56
+		$this->_priority = 10;
57
+		$this->_migration_stages = array(
58
+				new EE_DMS_4_1_0_org_options(),
59
+				new EE_DMS_4_1_0_shortcodes(),
60
+				new EE_DMS_4_1_0_gateways(),
61
+				new EE_DMS_4_1_0_events(),
62
+				new EE_DMS_4_1_0_prices(),
63
+				new EE_DMS_4_1_0_category_details(),
64
+				new EE_DMS_4_1_0_event_category(),
65
+				new EE_DMS_4_1_0_venues(),
66
+				new EE_DMS_4_1_0_event_venue(),
67
+				new EE_DMS_4_1_0_question_groups(),
68
+				new EE_DMS_4_1_0_questions(),
69
+				new EE_DMS_4_1_0_question_group_question(),
70
+				new EE_DMS_4_1_0_event_question_group(),
71
+				new EE_DMS_4_1_0_attendees(),
72
+				new EE_DMS_4_1_0_line_items(),
73
+				new EE_DMS_4_1_0_answers(),
74
+				new EE_DMS_4_1_0_checkins(),
75
+		);
76
+		parent::__construct($table_manager, $table_analysis);
77
+	}
78
+
79
+
80
+
81
+	/**
82
+	 * Checks if this 3.1 Check-in table exists. If it doesn't we can't migrate Check-ins
83
+	 *
84
+	 * @global wpdb $wpdb
85
+	 * @return boolean
86
+	 */
87
+	private function _checkin_table_exists()
88
+	{
89
+		global $wpdb;
90
+		$results = $wpdb->get_results("SHOW TABLES LIKE '" . $wpdb->prefix . "events_attendee_checkin" . "'");
91
+		if ($results) {
92
+			return true;
93
+		} else {
94
+			return false;
95
+		}
96
+	}
97
+
98
+
99
+
100
+	public function can_migrate_from_version($version_array)
101
+	{
102
+		$version_string = $version_array['Core'];
103
+		if (version_compare($version_string, '4.0.0', '<=') && version_compare($version_string, '3.1.26', '>=')) {
104 104
 //			echo "$version_string can be migrated fro";
105
-            return true;
106
-        } elseif ( ! $version_string) {
105
+			return true;
106
+		} elseif ( ! $version_string) {
107 107
 //			echo "no version string provided: $version_string";
108
-            //no version string provided... this must be pre 4.1
109
-            //because since 4.1 we're
110
-            return false;//changed mind. dont want people thinking they should migrate yet because they cant
111
-        } else {
108
+			//no version string provided... this must be pre 4.1
109
+			//because since 4.1 we're
110
+			return false;//changed mind. dont want people thinking they should migrate yet because they cant
111
+		} else {
112 112
 //			echo "$version_string doesnt apply";
113
-            return false;
114
-        }
115
-    }
113
+			return false;
114
+		}
115
+	}
116 116
 
117 117
 
118 118
 
119
-    public function schema_changes_before_migration()
120
-    {
121
-        //relies on 4.1's EEH_Activation::create_table
122
-        require_once(EE_HELPERS . 'EEH_Activation.helper.php');
123
-        $table_name = 'esp_answer';
124
-        $sql = " ANS_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
119
+	public function schema_changes_before_migration()
120
+	{
121
+		//relies on 4.1's EEH_Activation::create_table
122
+		require_once(EE_HELPERS . 'EEH_Activation.helper.php');
123
+		$table_name = 'esp_answer';
124
+		$sql = " ANS_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
125 125
 					REG_ID INT UNSIGNED NOT NULL,
126 126
 					QST_ID INT UNSIGNED NOT NULL,
127 127
 					ANS_value TEXT NOT NULL,
128 128
 					PRIMARY KEY  (ANS_ID)";
129
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
130
-        $table_name = 'esp_attendee_meta';
131
-        $sql = "ATTM_ID INT(10) UNSIGNED NOT	NULL AUTO_INCREMENT,
129
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
130
+		$table_name = 'esp_attendee_meta';
131
+		$sql = "ATTM_ID INT(10) UNSIGNED NOT	NULL AUTO_INCREMENT,
132 132
 						ATT_ID BIGINT(20) UNSIGNED NOT NULL,
133 133
 						ATT_fname VARCHAR(45) NOT NULL,
134 134
 						ATT_lname VARCHAR(45) NOT	NULL,
@@ -144,9 +144,9 @@  discard block
 block discarded – undo
144 144
 								KEY ATT_fname (ATT_fname),
145 145
 								KEY ATT_lname (ATT_lname),
146 146
 								KEY ATT_email (ATT_email)";
147
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB ');
148
-        $table_name = 'esp_country';
149
-        $sql = "CNT_ISO VARCHAR(2) COLLATE utf8_bin NOT NULL,
147
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB ');
148
+		$table_name = 'esp_country';
149
+		$sql = "CNT_ISO VARCHAR(2) COLLATE utf8_bin NOT NULL,
150 150
 					  CNT_ISO3 VARCHAR(3) COLLATE utf8_bin NOT NULL,
151 151
 					  RGN_ID TINYINT(3) UNSIGNED DEFAULT NULL,
152 152
 					  CNT_name VARCHAR(45) COLLATE utf8_bin NOT NULL,
@@ -162,9 +162,9 @@  discard block
 block discarded – undo
162 162
 					  CNT_is_EU TINYINT(1) DEFAULT '0',
163 163
 					  CNT_active TINYINT(1) DEFAULT '0',
164 164
 					  PRIMARY KEY  (CNT_ISO)";
165
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
166
-        $table_name = 'esp_datetime';
167
-        $sql = "DTT_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
165
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
166
+		$table_name = 'esp_datetime';
167
+		$sql = "DTT_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
168 168
 				  EVT_ID BIGINT(20) UNSIGNED NOT NULL,
169 169
 				  DTT_EVT_start DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
170 170
 				  DTT_EVT_end DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
@@ -177,9 +177,9 @@  discard block
 block discarded – undo
177 177
 						PRIMARY KEY  (DTT_ID),
178 178
 						KEY EVT_ID (EVT_ID),
179 179
 						KEY DTT_is_primary (DTT_is_primary)";
180
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
181
-        $table_name = 'esp_event_meta';
182
-        $sql = "
180
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
181
+		$table_name = 'esp_event_meta';
182
+		$sql = "
183 183
 			EVTM_ID INT NOT NULL AUTO_INCREMENT,
184 184
 			EVT_ID BIGINT(20) UNSIGNED NOT NULL,
185 185
 			EVT_display_desc TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
@@ -194,31 +194,31 @@  discard block
 block discarded – undo
194 194
 			EVT_external_URL VARCHAR(200) NULL,
195 195
 			EVT_donations TINYINT(1) NULL,
196 196
 			PRIMARY KEY  (EVTM_ID)";
197
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
198
-        $table_name = 'esp_event_question_group';
199
-        $sql = "EQG_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
197
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
198
+		$table_name = 'esp_event_question_group';
199
+		$sql = "EQG_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
200 200
 					EVT_ID BIGINT(20) UNSIGNED NOT NULL,
201 201
 					QSG_ID INT UNSIGNED NOT NULL,
202 202
 					EQG_primary TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
203 203
 					PRIMARY KEY  (EQG_ID)";
204
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
205
-        $table_name = 'esp_event_venue';
206
-        $sql = "EVV_ID INT(11) NOT NULL AUTO_INCREMENT,
204
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
205
+		$table_name = 'esp_event_venue';
206
+		$sql = "EVV_ID INT(11) NOT NULL AUTO_INCREMENT,
207 207
 				EVT_ID BIGINT(20) UNSIGNED NOT NULL,
208 208
 				VNU_ID BIGINT(20) UNSIGNED NOT NULL,
209 209
 				EVV_primary TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
210 210
 				PRIMARY KEY  (EVV_ID)";
211
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
212
-        $table_name = 'esp_extra_meta';
213
-        $sql = "EXM_ID INT(11) NOT NULL AUTO_INCREMENT,
211
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
212
+		$table_name = 'esp_extra_meta';
213
+		$sql = "EXM_ID INT(11) NOT NULL AUTO_INCREMENT,
214 214
 				OBJ_ID INT(11) DEFAULT NULL,
215 215
 				EXM_type VARCHAR(45) DEFAULT NULL,
216 216
 				EXM_key VARCHAR(45) DEFAULT NULL,
217 217
 				EXM_value TEXT,
218 218
 				PRIMARY KEY  (EXM_ID)";
219
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
220
-        $table_name = 'esp_line_item';
221
-        $sql = "LIN_ID INT(11) NOT NULL AUTO_INCREMENT,
219
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
220
+		$table_name = 'esp_line_item';
221
+		$sql = "LIN_ID INT(11) NOT NULL AUTO_INCREMENT,
222 222
 				LIN_code VARCHAR(245) NOT NULL DEFAULT '',
223 223
 				TXN_ID INT(11) DEFAULT NULL,
224 224
 				LIN_name VARCHAR(245) NOT NULL DEFAULT '',
@@ -234,18 +234,18 @@  discard block
 block discarded – undo
234 234
 				OBJ_ID INT(11) DEFAULT NULL,
235 235
 				OBJ_type VARCHAR(45)DEFAULT NULL,
236 236
 				PRIMARY KEY  (LIN_ID)";
237
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
238
-        $table_name = 'esp_message_template';
239
-        $sql = "MTP_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
237
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
238
+		$table_name = 'esp_message_template';
239
+		$sql = "MTP_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
240 240
 					GRP_ID INT(10) UNSIGNED NOT NULL,
241 241
 					MTP_context VARCHAR(50) NOT NULL,
242 242
 					MTP_template_field VARCHAR(30) NOT NULL,
243 243
 					MTP_content TEXT NOT NULL,
244 244
 					PRIMARY KEY  (MTP_ID),
245 245
 					KEY GRP_ID (GRP_ID)";
246
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
247
-        $table_name = 'esp_message_template_group';
248
-        $sql = "GRP_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
246
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
247
+		$table_name = 'esp_message_template_group';
248
+		$sql = "GRP_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
249 249
 					EVT_ID BIGINT(20) UNSIGNED DEFAULT NULL,
250 250
 					MTP_user_id INT(10) NOT NULL DEFAULT '1',
251 251
 					MTP_messenger VARCHAR(30) NOT NULL,
@@ -257,9 +257,9 @@  discard block
 block discarded – undo
257 257
 					PRIMARY KEY  (GRP_ID),
258 258
 					KEY EVT_ID (EVT_ID),
259 259
 					KEY MTP_user_id (MTP_user_id)";
260
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
261
-        $table_name = 'esp_payment';
262
-        $sql = "PAY_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
260
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
261
+		$table_name = 'esp_payment';
262
+		$sql = "PAY_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
263 263
 					TXN_ID INT(10) UNSIGNED DEFAULT NULL,
264 264
 					STS_ID VARCHAR(3) COLLATE utf8_bin DEFAULT NULL,
265 265
 					PAY_timestamp DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
@@ -275,9 +275,9 @@  discard block
 block discarded – undo
275 275
 					PRIMARY KEY  (PAY_ID),
276 276
 					KEY TXN_ID (TXN_ID),
277 277
 					KEY PAY_timestamp (PAY_timestamp)";
278
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB ');
279
-        $table_name = "esp_ticket";
280
-        $sql = "TKT_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
278
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB ');
279
+		$table_name = "esp_ticket";
280
+		$sql = "TKT_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
281 281
 					  TTM_ID INT(10) UNSIGNED NOT NULL,
282 282
 					  TKT_name VARCHAR(245) NOT NULL DEFAULT '',
283 283
 					  TKT_description TEXT NOT NULL,
@@ -296,28 +296,28 @@  discard block
 block discarded – undo
296 296
 					  TKT_parent INT(10) UNSIGNED DEFAULT '0',
297 297
 					  TKT_deleted TINYINT(1) NOT NULL DEFAULT '0',
298 298
 					  PRIMARY KEY  (TKT_ID)";
299
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
300
-        $table_name = "esp_ticket_price";
301
-        $sql = "TKP_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
299
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
300
+		$table_name = "esp_ticket_price";
301
+		$sql = "TKP_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
302 302
 					  TKT_ID INT(10) UNSIGNED NOT NULL,
303 303
 					  PRC_ID INT(10) UNSIGNED NOT NULL,
304 304
 					  PRIMARY KEY  (TKP_ID)";
305
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
306
-        $table_name = "esp_datetime_ticket";
307
-        $sql = "DTK_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
305
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
306
+		$table_name = "esp_datetime_ticket";
307
+		$sql = "DTK_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
308 308
 					  DTT_ID INT(10) UNSIGNED NOT NULL,
309 309
 					  TKT_ID INT(10) UNSIGNED NOT NULL,
310 310
 					  PRIMARY KEY  (DTK_ID)";
311
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
312
-        $table_name = "esp_ticket_template";
313
-        $sql = "TTM_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
311
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
312
+		$table_name = "esp_ticket_template";
313
+		$sql = "TTM_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
314 314
 					  TTM_name VARCHAR(45) NOT NULL,
315 315
 					  TTM_description TEXT,
316 316
 					  TTM_file VARCHAR(45),
317 317
 					  PRIMARY KEY  (TTM_ID)";
318
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
319
-        $table_name = "esp_price";
320
-        $sql = "PRC_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
318
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
319
+		$table_name = "esp_price";
320
+		$sql = "PRC_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
321 321
 					  PRT_ID TINYINT(3) UNSIGNED NOT NULL,
322 322
 					  PRC_amount DECIMAL(10,3) NOT NULL DEFAULT '0.00',
323 323
 					  PRC_name VARCHAR(245) NOT NULL,
@@ -328,9 +328,9 @@  discard block
 block discarded – undo
328 328
 					  PRC_order TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
329 329
 					  PRC_parent INT(10) UNSIGNED DEFAULT 0,
330 330
 					  PRIMARY KEY  (PRC_ID)";
331
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
332
-        $table_name = "esp_price_type";
333
-        $sql = "PRT_ID TINYINT(3) UNSIGNED NOT NULL AUTO_INCREMENT,
331
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
332
+		$table_name = "esp_price_type";
333
+		$sql = "PRT_ID TINYINT(3) UNSIGNED NOT NULL AUTO_INCREMENT,
334 334
 				  PRT_name VARCHAR(45) NOT NULL,
335 335
 				  PBT_ID TINYINT(3) UNSIGNED NOT NULL DEFAULT '1',
336 336
 				  PRT_is_percent TINYINT(1) NOT NULL DEFAULT '0',
@@ -338,9 +338,9 @@  discard block
 block discarded – undo
338 338
 				  PRT_deleted TINYINT(1) NOT NULL DEFAULT '0',
339 339
 				  UNIQUE KEY PRT_name_UNIQUE (PRT_name),
340 340
 				  PRIMARY KEY  (PRT_ID)";
341
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
342
-        $table_name = 'esp_question';
343
-        $sql = 'QST_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
341
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
342
+		$table_name = 'esp_question';
343
+		$sql = 'QST_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
344 344
 					QST_display_text TEXT NOT NULL,
345 345
 					QST_admin_label VARCHAR(255) NOT NULL,
346 346
 					QST_system VARCHAR(25) DEFAULT NULL,
@@ -352,10 +352,10 @@  discard block
 block discarded – undo
352 352
 					QST_wp_user BIGINT UNSIGNED NULL,
353 353
 					QST_deleted TINYINT UNSIGNED NOT NULL DEFAULT 0,
354 354
 					PRIMARY KEY  (QST_ID)';
355
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
356
-        $this->_get_table_manager()->dropIndex('esp_question_group', 'QSG_identifier_UNIQUE');
357
-        $table_name = 'esp_question_group';
358
-        $sql = 'QSG_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
355
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
356
+		$this->_get_table_manager()->dropIndex('esp_question_group', 'QSG_identifier_UNIQUE');
357
+		$table_name = 'esp_question_group';
358
+		$sql = 'QSG_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
359 359
 					QSG_name VARCHAR(255) NOT NULL,
360 360
 					QSG_identifier VARCHAR(100) NOT NULL,
361 361
 					QSG_desc TEXT NULL,
@@ -366,23 +366,23 @@  discard block
 block discarded – undo
366 366
 					QSG_deleted TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
367 367
 					PRIMARY KEY  (QSG_ID),
368 368
 					UNIQUE KEY QSG_identifier_UNIQUE (QSG_identifier ASC)';
369
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
370
-        $table_name = 'esp_question_group_question';
371
-        $sql = "QGQ_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
369
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
370
+		$table_name = 'esp_question_group_question';
371
+		$sql = "QGQ_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
372 372
 					QSG_ID INT UNSIGNED NOT NULL,
373 373
 					QST_ID INT UNSIGNED NOT NULL,
374 374
 					PRIMARY KEY  (QGQ_ID) ";
375
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
376
-        $table_name = 'esp_question_option';
377
-        $sql = "QSO_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
375
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
376
+		$table_name = 'esp_question_option';
377
+		$sql = "QSO_ID INT UNSIGNED NOT NULL AUTO_INCREMENT,
378 378
 					QSO_value VARCHAR(255) NOT NULL,
379 379
 					QSO_desc TEXT NOT NULL,
380 380
 					QST_ID INT UNSIGNED NOT NULL,
381 381
 					QSO_deleted TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
382 382
 					PRIMARY KEY  (QSO_ID)";
383
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
384
-        $table_name = 'esp_registration';
385
-        $sql = "REG_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
383
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
384
+		$table_name = 'esp_registration';
385
+		$sql = "REG_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
386 386
 					  EVT_ID BIGINT(20) UNSIGNED NOT NULL,
387 387
 					  ATT_ID BIGINT(20) UNSIGNED NOT NULL,
388 388
 					  TXN_ID INT(10) UNSIGNED NOT NULL,
@@ -405,25 +405,25 @@  discard block
 block discarded – undo
405 405
 					  KEY STS_ID (STS_ID),
406 406
 					  KEY REG_url_link (REG_url_link),
407 407
 					  KEY REG_code (REG_code)";
408
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB ');
409
-        $table_name = 'esp_checkin';
410
-        $sql = "CHK_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
408
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB ');
409
+		$table_name = 'esp_checkin';
410
+		$sql = "CHK_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
411 411
 					REG_ID INT(10) UNSIGNED NOT NULL,
412 412
 					DTT_ID INT(10) UNSIGNED NOT NULL,
413 413
 					CHK_in TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
414 414
 					CHK_timestamp DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
415 415
 					PRIMARY KEY  (CHK_ID)";
416
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
417
-        $table_name = 'esp_state';
418
-        $sql = "STA_ID smallint(5) UNSIGNED NOT NULL AUTO_INCREMENT,
416
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
417
+		$table_name = 'esp_state';
418
+		$sql = "STA_ID smallint(5) UNSIGNED NOT NULL AUTO_INCREMENT,
419 419
 					  CNT_ISO VARCHAR(2) COLLATE utf8_bin NOT NULL,
420 420
 					  STA_abbrev VARCHAR(6) COLLATE utf8_bin NOT NULL,
421 421
 					  STA_name VARCHAR(100) COLLATE utf8_bin NOT NULL,
422 422
 					  STA_active TINYINT(1) DEFAULT '1',
423 423
 					  PRIMARY KEY  (STA_ID)";
424
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
425
-        $table_name = 'esp_status';
426
-        $sql = "STS_ID VARCHAR(3) COLLATE utf8_bin NOT NULL,
424
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
425
+		$table_name = 'esp_status';
426
+		$sql = "STS_ID VARCHAR(3) COLLATE utf8_bin NOT NULL,
427 427
 					  STS_code VARCHAR(45) COLLATE utf8_bin NOT NULL,
428 428
 					  STS_type set('event','registration','transaction','payment','email') COLLATE utf8_bin NOT NULL,
429 429
 					  STS_can_edit TINYINT(1) NOT NULL DEFAULT 0,
@@ -431,9 +431,9 @@  discard block
 block discarded – undo
431 431
 					  STS_open TINYINT(1) NOT NULL DEFAULT 1,
432 432
 					  UNIQUE KEY STS_ID_UNIQUE (STS_ID),
433 433
 					  KEY STS_type (STS_type)";
434
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
435
-        $table_name = 'esp_transaction';
436
-        $sql = "TXN_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
434
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
435
+		$table_name = 'esp_transaction';
436
+		$sql = "TXN_ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
437 437
 					  TXN_timestamp DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
438 438
 					  TXN_total DECIMAL(10,3) DEFAULT '0.00',
439 439
 					  TXN_paid DECIMAL(10,3) NOT NULL DEFAULT '0.00',
@@ -443,9 +443,9 @@  discard block
 block discarded – undo
443 443
 					  PRIMARY KEY  (TXN_ID),
444 444
 					  KEY TXN_timestamp (TXN_timestamp),
445 445
 					  KEY STS_ID (STS_ID)";
446
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
447
-        $table_name = 'esp_venue_meta';
448
-        $sql = "VNUM_ID INT(11) NOT NULL AUTO_INCREMENT,
446
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
447
+		$table_name = 'esp_venue_meta';
448
+		$sql = "VNUM_ID INT(11) NOT NULL AUTO_INCREMENT,
449 449
 			VNU_ID BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
450 450
 			VNU_address VARCHAR(255) DEFAULT NULL,
451 451
 			VNU_address2 VARCHAR(255) DEFAULT NULL,
@@ -463,52 +463,52 @@  discard block
 block discarded – undo
463 463
 			PRIMARY KEY  (VNUM_ID),
464 464
 			KEY STA_ID (STA_ID),
465 465
 			KEY CNT_ISO (CNT_ISO)";
466
-        $this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
467
-        //setting up the DEFAULT stats and countries is also essential for the data migrations to run
468
-        //(because many need to convert old string states to foreign keys into the states table)
469
-        $this->insert_default_states();
470
-        $this->insert_default_countries();
471
-        //setting up DEFAULT prices, price types, and tickets is also essential for the price migrations
472
-        $this->insert_default_price_types();
473
-        $this->insert_default_prices();
474
-        $this->insert_default_tickets();
475
-        //setting up the config wp option pretty well counts as a 'schema change', or at least should happen ehre
476
-        EE_Config::instance()->update_espresso_config(false, true);
477
-        return true;
478
-    }
479
-
480
-
481
-
482
-    /**
483
-     * Yes we could have cleaned up the ee3 tables here. But just in case someone
484
-     * didn't backup their DB, and decides they want ot keep using EE3, we'll
485
-     * leave them for now. Mayeb remove them in 4.5 or something.
486
-     *
487
-     * @return boolean
488
-     */
489
-    public function schema_changes_after_migration()
490
-    {
491
-        return true;
492
-    }
493
-
494
-
495
-
496
-    /**
497
-     * insert_default_states
498
-     *
499
-     * @access public
500
-     * @static
501
-     * @return void
502
-     */
503
-    public function insert_default_states()
504
-    {
505
-        global $wpdb;
506
-        $state_table = $wpdb->prefix . "esp_state";
507
-        if ($this->_get_table_analysis()->tableExists($state_table)) {
508
-            $SQL = "SELECT COUNT('STA_ID') FROM " . $state_table;
509
-            $states = $wpdb->get_var($SQL);
510
-            if ( ! $states) {
511
-                $SQL = "INSERT INTO " . $state_table . "
466
+		$this->_table_is_new_in_this_version($table_name, $sql, 'ENGINE=InnoDB');
467
+		//setting up the DEFAULT stats and countries is also essential for the data migrations to run
468
+		//(because many need to convert old string states to foreign keys into the states table)
469
+		$this->insert_default_states();
470
+		$this->insert_default_countries();
471
+		//setting up DEFAULT prices, price types, and tickets is also essential for the price migrations
472
+		$this->insert_default_price_types();
473
+		$this->insert_default_prices();
474
+		$this->insert_default_tickets();
475
+		//setting up the config wp option pretty well counts as a 'schema change', or at least should happen ehre
476
+		EE_Config::instance()->update_espresso_config(false, true);
477
+		return true;
478
+	}
479
+
480
+
481
+
482
+	/**
483
+	 * Yes we could have cleaned up the ee3 tables here. But just in case someone
484
+	 * didn't backup their DB, and decides they want ot keep using EE3, we'll
485
+	 * leave them for now. Mayeb remove them in 4.5 or something.
486
+	 *
487
+	 * @return boolean
488
+	 */
489
+	public function schema_changes_after_migration()
490
+	{
491
+		return true;
492
+	}
493
+
494
+
495
+
496
+	/**
497
+	 * insert_default_states
498
+	 *
499
+	 * @access public
500
+	 * @static
501
+	 * @return void
502
+	 */
503
+	public function insert_default_states()
504
+	{
505
+		global $wpdb;
506
+		$state_table = $wpdb->prefix . "esp_state";
507
+		if ($this->_get_table_analysis()->tableExists($state_table)) {
508
+			$SQL = "SELECT COUNT('STA_ID') FROM " . $state_table;
509
+			$states = $wpdb->get_var($SQL);
510
+			if ( ! $states) {
511
+				$SQL = "INSERT INTO " . $state_table . "
512 512
 				(STA_ID, CNT_ISO, STA_abbrev, STA_name, STA_active) VALUES
513 513
 				(1, 'US', 'AK', 'Alaska', 1),
514 514
 				(2, 'US', 'AL', 'Alabama', 1),
@@ -579,29 +579,29 @@  discard block
 block discarded – undo
579 579
 				(67, 'CA', 'PE', 'Prince Edward Island', 1),
580 580
 				(68, 'CA', 'QC', 'Quebec', 1),
581 581
 				(69, 'CA', 'SK', 'Saskatchewan', 1);";
582
-                $wpdb->query($SQL);
583
-            }
584
-        }
585
-    }
586
-
587
-
588
-
589
-    /**
590
-     * insert_default_countries
591
-     *
592
-     * @access public
593
-     * @static
594
-     * @return void
595
-     */
596
-    public function insert_default_countries()
597
-    {
598
-        global $wpdb;
599
-        $country_table = $wpdb->prefix . "esp_country";
600
-        if ($this->_get_table_analysis()->tableExists($country_table)) {
601
-            $SQL = "SELECT COUNT('CNT_ISO') FROM " . $country_table;
602
-            $countries = $wpdb->get_var($SQL);
603
-            if ( ! $countries) {
604
-                $SQL = "INSERT INTO " . $country_table . "
582
+				$wpdb->query($SQL);
583
+			}
584
+		}
585
+	}
586
+
587
+
588
+
589
+	/**
590
+	 * insert_default_countries
591
+	 *
592
+	 * @access public
593
+	 * @static
594
+	 * @return void
595
+	 */
596
+	public function insert_default_countries()
597
+	{
598
+		global $wpdb;
599
+		$country_table = $wpdb->prefix . "esp_country";
600
+		if ($this->_get_table_analysis()->tableExists($country_table)) {
601
+			$SQL = "SELECT COUNT('CNT_ISO') FROM " . $country_table;
602
+			$countries = $wpdb->get_var($SQL);
603
+			if ( ! $countries) {
604
+				$SQL = "INSERT INTO " . $country_table . "
605 605
 				(CNT_ISO, CNT_ISO3, RGN_ID, CNT_name, CNT_cur_code, CNT_cur_single, CNT_cur_plural, CNT_cur_sign, CNT_cur_sign_b4, CNT_cur_dec_plc, CNT_tel_code, CNT_is_EU, CNT_active) VALUES
606 606
 				('AD', 'AND', 0, 'Andorra', 'EUR', 'Euro', 'Euros', '€', 1, 2, '+376', 0, 0),
607 607
 				('AE', 'ARE', 0, 'United Arab Emirates', 'AED', 'Dirham', 'Dirhams', 'د.إ', 1, 2, '+971', 0, 0),
@@ -829,941 +829,941 @@  discard block
 block discarded – undo
829 829
 				('ZA', 'ZAF', 0, 'South Africa', 'ZAR', 'Rand', 'Rands', 'R', 1, 2, '+27', 0, 0),
830 830
 				('ZM', 'ZMB', 0, 'Zambia', 'ZMK', 'Kwacha', 'Kwachas', '', 1, 2, '+260', 0, 0),
831 831
 				('ZW', 'ZWE', 0, 'Zimbabwe', 'ZWD', 'Dollar', 'Dollars', 'Z$', 1, 2, '+263', 0, 0);";
832
-                $wpdb->query($SQL);
833
-            }
834
-        }
835
-    }
836
-
837
-
838
-
839
-    /**
840
-     * insert_default_price_types
841
-     *
842
-     * @access public
843
-     * @static
844
-     * @return void
845
-     */
846
-    public function insert_default_price_types()
847
-    {
848
-        global $wpdb;
849
-        $price_type_table = $wpdb->prefix . "esp_price_type";
850
-        if ($this->_get_table_analysis()->tableExists($price_type_table)) {
851
-            $SQL = 'SELECT COUNT(PRT_ID) FROM ' . $price_type_table;
852
-            $price_types_exist = $wpdb->get_var($SQL);
853
-            if ( ! $price_types_exist) {
854
-                $SQL = "INSERT INTO $price_type_table ( PRT_ID, PRT_name, PBT_ID, PRT_is_percent, PRT_order, PRT_deleted ) VALUES
832
+				$wpdb->query($SQL);
833
+			}
834
+		}
835
+	}
836
+
837
+
838
+
839
+	/**
840
+	 * insert_default_price_types
841
+	 *
842
+	 * @access public
843
+	 * @static
844
+	 * @return void
845
+	 */
846
+	public function insert_default_price_types()
847
+	{
848
+		global $wpdb;
849
+		$price_type_table = $wpdb->prefix . "esp_price_type";
850
+		if ($this->_get_table_analysis()->tableExists($price_type_table)) {
851
+			$SQL = 'SELECT COUNT(PRT_ID) FROM ' . $price_type_table;
852
+			$price_types_exist = $wpdb->get_var($SQL);
853
+			if ( ! $price_types_exist) {
854
+				$SQL = "INSERT INTO $price_type_table ( PRT_ID, PRT_name, PBT_ID, PRT_is_percent, PRT_order, PRT_deleted ) VALUES
855 855
 							(1, '" . esc_html__('Base Price', 'event_espresso') . "', 1,  0, 0, 0),
856 856
 							(2, '" . esc_html__('Percent Discount', 'event_espresso') . "', 2,  1, 20, 0),
857 857
 							(3, '" . esc_html__('Fixed Discount', 'event_espresso') . "', 2,  0, 30, 0),
858 858
 							(4, '" . esc_html__('Percent Surcharge', 'event_espresso') . "', 3,  1, 40, 0),
859 859
 							(5, '" . esc_html__('Fixed Surcharge', 'event_espresso') . "', 3,  0, 50, 0);";
860
-                $SQL = apply_filters('FHEE__EE_DMS_4_1_0__insert_default_price_types__SQL', $SQL);
861
-                $wpdb->query($SQL);
862
-            }
863
-        }
864
-    }
865
-
866
-
867
-
868
-    /**
869
-     * insert_default_prices. We assume we're upgrading to regular here.
870
-     * If we're INSTALLING 4.1 CAF, then we add a few extra DEFAULT prices
871
-     * when EEH_Activaion's initialize_db_content is called via  ahook in
872
-     * EE_BRewing_regular
873
-     *
874
-     * @access public
875
-     * @static
876
-     * @return void
877
-     */
878
-    public function insert_default_prices()
879
-    {
880
-        global $wpdb;
881
-        $price_table = $wpdb->prefix . "esp_price";
882
-        if ($this->_get_table_analysis()->tableExists($price_table)) {
883
-            $SQL = 'SELECT COUNT(PRC_ID) FROM ' . $price_table;
884
-            $prices_exist = $wpdb->get_var($SQL);
885
-            if ( ! $prices_exist) {
886
-                $SQL = "INSERT INTO $price_table
860
+				$SQL = apply_filters('FHEE__EE_DMS_4_1_0__insert_default_price_types__SQL', $SQL);
861
+				$wpdb->query($SQL);
862
+			}
863
+		}
864
+	}
865
+
866
+
867
+
868
+	/**
869
+	 * insert_default_prices. We assume we're upgrading to regular here.
870
+	 * If we're INSTALLING 4.1 CAF, then we add a few extra DEFAULT prices
871
+	 * when EEH_Activaion's initialize_db_content is called via  ahook in
872
+	 * EE_BRewing_regular
873
+	 *
874
+	 * @access public
875
+	 * @static
876
+	 * @return void
877
+	 */
878
+	public function insert_default_prices()
879
+	{
880
+		global $wpdb;
881
+		$price_table = $wpdb->prefix . "esp_price";
882
+		if ($this->_get_table_analysis()->tableExists($price_table)) {
883
+			$SQL = 'SELECT COUNT(PRC_ID) FROM ' . $price_table;
884
+			$prices_exist = $wpdb->get_var($SQL);
885
+			if ( ! $prices_exist) {
886
+				$SQL = "INSERT INTO $price_table
887 887
 							(PRC_ID, PRT_ID, PRC_amount, PRC_name, PRC_desc,  PRC_is_default, PRC_overrides, PRC_order, PRC_deleted, PRC_parent ) VALUES
888 888
 							(1, 1, '0.00', 'Free Admission', '', 1, NULL, 0, 0, 0);";
889
-                $SQL = apply_filters('FHEE__EE_DMS_4_1_0__insert_default_prices__SQL', $SQL);
890
-                $wpdb->query($SQL);
891
-            }
892
-        }
893
-    }
894
-
895
-
896
-
897
-    /**
898
-     * insert DEFAULT ticket
899
-     *
900
-     * @access public
901
-     * @static
902
-     * @return void
903
-     */
904
-    public function insert_default_tickets()
905
-    {
906
-        global $wpdb;
907
-        $ticket_table = $wpdb->prefix . "esp_ticket";
908
-        if ($this->_get_table_analysis()->tableExists($ticket_table)) {
909
-            $SQL = 'SELECT COUNT(TKT_ID) FROM ' . $ticket_table;
910
-            $tickets_exist = $wpdb->get_var($SQL);
911
-            if ( ! $tickets_exist) {
912
-                $SQL = "INSERT INTO $ticket_table
889
+				$SQL = apply_filters('FHEE__EE_DMS_4_1_0__insert_default_prices__SQL', $SQL);
890
+				$wpdb->query($SQL);
891
+			}
892
+		}
893
+	}
894
+
895
+
896
+
897
+	/**
898
+	 * insert DEFAULT ticket
899
+	 *
900
+	 * @access public
901
+	 * @static
902
+	 * @return void
903
+	 */
904
+	public function insert_default_tickets()
905
+	{
906
+		global $wpdb;
907
+		$ticket_table = $wpdb->prefix . "esp_ticket";
908
+		if ($this->_get_table_analysis()->tableExists($ticket_table)) {
909
+			$SQL = 'SELECT COUNT(TKT_ID) FROM ' . $ticket_table;
910
+			$tickets_exist = $wpdb->get_var($SQL);
911
+			if ( ! $tickets_exist) {
912
+				$SQL = "INSERT INTO $ticket_table
913 913
 					( TKT_ID, TTM_ID, TKT_name, TKT_description, TKT_qty, TKT_sold, TKT_uses, TKT_min, TKT_max, TKT_price, TKT_start_date, TKT_end_date, TKT_taxable, TKT_order, TKT_row, TKT_is_default, TKT_parent, TKT_deleted ) VALUES
914 914
 					( 1, 0, '"
915
-                       . esc_html__("Free Ticket", "event_espresso")
916
-                       . "', '', 100, 0, -1, 0, -1, 0.00, '0000-00-00 00:00:00', '0000-00-00 00:00:00', 0, 0, 1, 1, 0, 0);";
917
-                $SQL = apply_filters('FHEE__EE_DMS_4_1_0__insert_default_tickets__SQL', $SQL);
918
-                $wpdb->query($SQL);
919
-            }
920
-        }
921
-        $ticket_price_table = $wpdb->prefix . "esp_ticket_price";
922
-        if ($this->_get_table_analysis()->tableExists($ticket_price_table)) {
923
-            $SQL = 'SELECT COUNT(TKP_ID) FROM ' . $ticket_price_table;
924
-            $ticket_prc_exist = $wpdb->get_var($SQL);
925
-            if ( ! $ticket_prc_exist) {
926
-                $SQL = "INSERT INTO $ticket_price_table
915
+					   . esc_html__("Free Ticket", "event_espresso")
916
+					   . "', '', 100, 0, -1, 0, -1, 0.00, '0000-00-00 00:00:00', '0000-00-00 00:00:00', 0, 0, 1, 1, 0, 0);";
917
+				$SQL = apply_filters('FHEE__EE_DMS_4_1_0__insert_default_tickets__SQL', $SQL);
918
+				$wpdb->query($SQL);
919
+			}
920
+		}
921
+		$ticket_price_table = $wpdb->prefix . "esp_ticket_price";
922
+		if ($this->_get_table_analysis()->tableExists($ticket_price_table)) {
923
+			$SQL = 'SELECT COUNT(TKP_ID) FROM ' . $ticket_price_table;
924
+			$ticket_prc_exist = $wpdb->get_var($SQL);
925
+			if ( ! $ticket_prc_exist) {
926
+				$SQL = "INSERT INTO $ticket_price_table
927 927
 				( TKP_ID, TKT_ID, PRC_ID ) VALUES
928 928
 				( 1, 1, 1 )
929 929
 				";
930
-                $SQL = apply_filters('FHEE__EE_DMS_4_1_0__insert_default_tickets__SQL__ticket_price', $SQL);
931
-                $wpdb->query($SQL);
932
-            }
933
-        }
934
-    }
935
-
936
-
937
-
938
-    /**
939
-     * Gets a country entry as an array, or creates one if none is found. Much like EEM_Country::instance()->get_one(),
940
-     * but is independent of outside code which can change in future versions of EE. Also, $country_name CAN be a 3.1
941
-     * country ID (int), a 2-letter ISO, 3-letter ISO, or name
942
-     *
943
-     * @global type  $wpdb
944
-     * @param string $country_name
945
-     * @return array where keys are columns, values are column values
946
-     */
947
-    public function get_or_create_country($country_name)
948
-    {
949
-        if ( ! $country_name) {
950
-            throw new EE_Error(esc_html__("Could not get a country because country name is blank", "event_espresso"));
951
-        }
952
-        global $wpdb;
953
-        $country_table = $wpdb->prefix . "esp_country";
954
-        if (is_int($country_name)) {
955
-            $country_name = $this->get_iso_from_3_1_country_id($country_name);
956
-        }
957
-        $country = $wpdb->get_row($wpdb->prepare("SELECT * FROM $country_table WHERE
930
+				$SQL = apply_filters('FHEE__EE_DMS_4_1_0__insert_default_tickets__SQL__ticket_price', $SQL);
931
+				$wpdb->query($SQL);
932
+			}
933
+		}
934
+	}
935
+
936
+
937
+
938
+	/**
939
+	 * Gets a country entry as an array, or creates one if none is found. Much like EEM_Country::instance()->get_one(),
940
+	 * but is independent of outside code which can change in future versions of EE. Also, $country_name CAN be a 3.1
941
+	 * country ID (int), a 2-letter ISO, 3-letter ISO, or name
942
+	 *
943
+	 * @global type  $wpdb
944
+	 * @param string $country_name
945
+	 * @return array where keys are columns, values are column values
946
+	 */
947
+	public function get_or_create_country($country_name)
948
+	{
949
+		if ( ! $country_name) {
950
+			throw new EE_Error(esc_html__("Could not get a country because country name is blank", "event_espresso"));
951
+		}
952
+		global $wpdb;
953
+		$country_table = $wpdb->prefix . "esp_country";
954
+		if (is_int($country_name)) {
955
+			$country_name = $this->get_iso_from_3_1_country_id($country_name);
956
+		}
957
+		$country = $wpdb->get_row($wpdb->prepare("SELECT * FROM $country_table WHERE
958 958
 			CNT_ISO LIKE %s OR
959 959
 			CNT_ISO3 LIKE %s OR
960 960
 			CNT_name LIKE %s LIMIT 1", $country_name, $country_name, $country_name), ARRAY_A);
961
-        if ( ! $country) {
962
-            //insert a new one then
963
-            $cols_n_values = array(
964
-                    'CNT_ISO'         => $this->_find_available_country_iso(2),
965
-                    'CNT_ISO3'        => $this->_find_available_country_iso(3),
966
-                    'RGN_ID'          => 0,
967
-                    'CNT_name'        => $country_name,
968
-                    'CNT_cur_code'    => 'USD',
969
-                    'CNT_cur_single'  => 'Dollar',
970
-                    'CNT_cur_plural'  => 'Dollars',
971
-                    'CNT_cur_sign'    => '&#36;',
972
-                    'CNT_cur_sign_b4' => true,
973
-                    'CNT_cur_dec_plc' => 2,
974
-                    'CNT_cur_dec_mrk' => '.',
975
-                    'CNT_cur_thsnds'  => ',',
976
-                    'CNT_tel_code'    => '+1',
977
-                    'CNT_is_EU'       => false,
978
-                    'CNT_active'      => true,
979
-            );
980
-            $data_types = array(
981
-                    '%s',//CNT_ISO
982
-                    '%s',//CNT_ISO3
983
-                    '%d',//RGN_ID
984
-                    '%s',//CNT_name
985
-                    '%s',//CNT_cur_code
986
-                    '%s',//CNT_cur_single
987
-                    '%s',//CNT_cur_plural
988
-                    '%s',//CNT_cur_sign
989
-                    '%d',//CNT_cur_sign_b4
990
-                    '%d',//CNT_cur_dec_plc
991
-                    '%s',//CNT_cur_dec_mrk
992
-                    '%s',//CNT_cur_thsnds
993
-                    '%s',//CNT_tel_code
994
-                    '%d',//CNT_is_EU
995
-                    '%d',//CNT_active
996
-            );
997
-            $success = $wpdb->insert($country_table,
998
-                    $cols_n_values,
999
-                    $data_types);
1000
-            if ( ! $success) {
1001
-                throw new EE_Error($this->_create_error_message_for_db_insertion('N/A',
1002
-                        array('country_id' => $country_name), $country_table, $cols_n_values, $data_types));
1003
-            }
1004
-            $country = $cols_n_values;
1005
-        }
1006
-        return $country;
1007
-    }
1008
-
1009
-
1010
-
1011
-    /**
1012
-     * finds a country iso which hasnt been used yet
1013
-     *
1014
-     * @global type $wpdb
1015
-     * @return string
1016
-     */
1017
-    private function _find_available_country_iso($num_letters = 2)
1018
-    {
1019
-        global $wpdb;
1020
-        $country_table = $wpdb->prefix . "esp_country";
1021
-        $attempts = 0;
1022
-        do {
1023
-            $current_iso = strtoupper(wp_generate_password($num_letters, false));
1024
-            $country_with_that_iso = $wpdb->get_var($wpdb->prepare("SELECT count(CNT_ISO) FROM "
1025
-                                                                   . $country_table
1026
-                                                                   . " WHERE CNT_ISO=%s", $current_iso));
1027
-            $attempts++;
1028
-            //keep going until we find an available country code, or we arbitrarily
1029
-            //decide we've tried this enough. Somehow they have way too many countries
1030
-            //(probably because they're mis-using the EE3 country_id like a custom question)
1031
-        } while (intval($country_with_that_iso) && $attempts < 200);
1032
-        return $current_iso;
1033
-    }
1034
-
1035
-
1036
-
1037
-    /**
1038
-     * Gets a state entry as an array, or creates one if none is found. Much like EEM_State::instance()->get_one(), but
1039
-     * is independent of outside code which can change in future versions of EE
1040
-     *
1041
-     * @global type  $wpdb
1042
-     * @param string $state_name
1043
-     * @return array where keys are columns, values are column values
1044
-     */
1045
-    public function get_or_create_state($state_name, $country_name = '')
1046
-    {
1047
-        if ( ! $state_name) {
1048
-            throw new EE_Error(esc_html__("Could not get-or-create state because no state name was provided",
1049
-                    "event_espresso"));
1050
-        }
1051
-        try {
1052
-            $country = $this->get_or_create_country($country_name);
1053
-            $country_iso = $country['CNT_ISO'];
1054
-        } catch (EE_Error $e) {
1055
-            $country_iso = $this->get_default_country_iso();
1056
-        }
1057
-        global $wpdb;
1058
-        $state_table = $wpdb->prefix . "esp_state";
1059
-        $state = $wpdb->get_row($wpdb->prepare("SELECT * FROM $state_table WHERE
961
+		if ( ! $country) {
962
+			//insert a new one then
963
+			$cols_n_values = array(
964
+					'CNT_ISO'         => $this->_find_available_country_iso(2),
965
+					'CNT_ISO3'        => $this->_find_available_country_iso(3),
966
+					'RGN_ID'          => 0,
967
+					'CNT_name'        => $country_name,
968
+					'CNT_cur_code'    => 'USD',
969
+					'CNT_cur_single'  => 'Dollar',
970
+					'CNT_cur_plural'  => 'Dollars',
971
+					'CNT_cur_sign'    => '&#36;',
972
+					'CNT_cur_sign_b4' => true,
973
+					'CNT_cur_dec_plc' => 2,
974
+					'CNT_cur_dec_mrk' => '.',
975
+					'CNT_cur_thsnds'  => ',',
976
+					'CNT_tel_code'    => '+1',
977
+					'CNT_is_EU'       => false,
978
+					'CNT_active'      => true,
979
+			);
980
+			$data_types = array(
981
+					'%s',//CNT_ISO
982
+					'%s',//CNT_ISO3
983
+					'%d',//RGN_ID
984
+					'%s',//CNT_name
985
+					'%s',//CNT_cur_code
986
+					'%s',//CNT_cur_single
987
+					'%s',//CNT_cur_plural
988
+					'%s',//CNT_cur_sign
989
+					'%d',//CNT_cur_sign_b4
990
+					'%d',//CNT_cur_dec_plc
991
+					'%s',//CNT_cur_dec_mrk
992
+					'%s',//CNT_cur_thsnds
993
+					'%s',//CNT_tel_code
994
+					'%d',//CNT_is_EU
995
+					'%d',//CNT_active
996
+			);
997
+			$success = $wpdb->insert($country_table,
998
+					$cols_n_values,
999
+					$data_types);
1000
+			if ( ! $success) {
1001
+				throw new EE_Error($this->_create_error_message_for_db_insertion('N/A',
1002
+						array('country_id' => $country_name), $country_table, $cols_n_values, $data_types));
1003
+			}
1004
+			$country = $cols_n_values;
1005
+		}
1006
+		return $country;
1007
+	}
1008
+
1009
+
1010
+
1011
+	/**
1012
+	 * finds a country iso which hasnt been used yet
1013
+	 *
1014
+	 * @global type $wpdb
1015
+	 * @return string
1016
+	 */
1017
+	private function _find_available_country_iso($num_letters = 2)
1018
+	{
1019
+		global $wpdb;
1020
+		$country_table = $wpdb->prefix . "esp_country";
1021
+		$attempts = 0;
1022
+		do {
1023
+			$current_iso = strtoupper(wp_generate_password($num_letters, false));
1024
+			$country_with_that_iso = $wpdb->get_var($wpdb->prepare("SELECT count(CNT_ISO) FROM "
1025
+																   . $country_table
1026
+																   . " WHERE CNT_ISO=%s", $current_iso));
1027
+			$attempts++;
1028
+			//keep going until we find an available country code, or we arbitrarily
1029
+			//decide we've tried this enough. Somehow they have way too many countries
1030
+			//(probably because they're mis-using the EE3 country_id like a custom question)
1031
+		} while (intval($country_with_that_iso) && $attempts < 200);
1032
+		return $current_iso;
1033
+	}
1034
+
1035
+
1036
+
1037
+	/**
1038
+	 * Gets a state entry as an array, or creates one if none is found. Much like EEM_State::instance()->get_one(), but
1039
+	 * is independent of outside code which can change in future versions of EE
1040
+	 *
1041
+	 * @global type  $wpdb
1042
+	 * @param string $state_name
1043
+	 * @return array where keys are columns, values are column values
1044
+	 */
1045
+	public function get_or_create_state($state_name, $country_name = '')
1046
+	{
1047
+		if ( ! $state_name) {
1048
+			throw new EE_Error(esc_html__("Could not get-or-create state because no state name was provided",
1049
+					"event_espresso"));
1050
+		}
1051
+		try {
1052
+			$country = $this->get_or_create_country($country_name);
1053
+			$country_iso = $country['CNT_ISO'];
1054
+		} catch (EE_Error $e) {
1055
+			$country_iso = $this->get_default_country_iso();
1056
+		}
1057
+		global $wpdb;
1058
+		$state_table = $wpdb->prefix . "esp_state";
1059
+		$state = $wpdb->get_row($wpdb->prepare("SELECT * FROM $state_table WHERE
1060 1060
 			(STA_abbrev LIKE %s OR
1061 1061
 			STA_name LIKE %s) AND
1062 1062
 			CNT_ISO LIKE %s LIMIT 1", $state_name, $state_name, $country_iso), ARRAY_A);
1063
-        if ( ! $state) {
1064
-            //insert a new one then
1065
-            $cols_n_values = array(
1066
-                    'CNT_ISO'    => $country_iso,
1067
-                    'STA_abbrev' => substr($state_name, 0, 6),
1068
-                    'STA_name'   => $state_name,
1069
-                    'STA_active' => true,
1070
-            );
1071
-            $data_types = array(
1072
-                    '%s',//CNT_ISO
1073
-                    '%s',//STA_abbrev
1074
-                    '%s',//STA_name
1075
-                    '%d',//STA_active
1076
-            );
1077
-            $success = $wpdb->insert($state_table, $cols_n_values, $data_types);
1078
-            if ( ! $success) {
1079
-                throw new EE_Error($this->_create_error_message_for_db_insertion('N/A',
1080
-                        array('state' => $state_name, 'country_id' => $country_name), $state_table, $cols_n_values,
1081
-                        $data_types));
1082
-            }
1083
-            $state = $cols_n_values;
1084
-            $state['STA_ID'] = $wpdb->insert_id;
1085
-        }
1086
-        return $state;
1087
-    }
1088
-
1089
-
1090
-
1091
-    /**
1092
-     * Fixes times like "5:00 PM" into the expected 24-hour format "17:00".
1093
-     * THis is actually just copied from the 3.1 JSON API because it needed to do the exact same thing
1094
-     *
1095
-     * @param type $timeString
1096
-     * @return string in the php DATETIME format: "G:i" (24-hour format hour with leading zeros, a colon, and minutes
1097
-     *                with leading zeros)
1098
-     */
1099
-    public function convertTimeFromAMPM($timeString)
1100
-    {
1101
-        $matches = array();
1102
-        preg_match("~(\\d*):(\\d*)~", $timeString, $matches);
1103
-        if ( ! $matches || count($matches) < 3) {
1104
-            $hour = '00';
1105
-            $minutes = '00';
1106
-        } else {
1107
-            $hour = intval($matches[1]);
1108
-            $minutes = $matches[2];
1109
-        }
1110
-        if (strpos($timeString, 'PM') || strpos($timeString, 'pm')) {
1111
-            $hour = intval($hour) + 12;
1112
-        }
1113
-        $hour = str_pad("$hour", 2, '0', STR_PAD_LEFT);
1114
-        $minutes = str_pad("$minutes", 2, '0', STR_PAD_LEFT);
1115
-        return "$hour:$minutes";
1116
-    }
1117
-
1118
-
1119
-
1120
-    /**
1121
-     * Gets the ISO3 fora country given its 3.1 country ID.
1122
-     *
1123
-     * @param int $country_id
1124
-     * @return string the country's ISO3 code
1125
-     */
1126
-    public function get_iso_from_3_1_country_id($country_id)
1127
-    {
1128
-        $old_countries = array(
1129
-                array(64, 'United States', 'US', 'USA', 1),
1130
-                array(15, 'Australia', 'AU', 'AUS', 1),
1131
-                array(39, 'Canada', 'CA', 'CAN', 1),
1132
-                array(171, 'United Kingdom', 'GB', 'GBR', 1),
1133
-                array(70, 'France', 'FR', 'FRA', 2),
1134
-                array(111, 'Italy', 'IT', 'ITA', 2),
1135
-                array(63, 'Spain', 'ES', 'ESP', 2),
1136
-                array(1, 'Afghanistan', 'AF', 'AFG', 1),
1137
-                array(2, 'Albania', 'AL', 'ALB', 1),
1138
-                array(3, 'Germany', 'DE', 'DEU', 2),
1139
-                array(198, 'Switzerland', 'CH', 'CHE', 1),
1140
-                array(87, 'Netherlands', 'NL', 'NLD', 2),
1141
-                array(197, 'Sweden', 'SE', 'SWE', 1),
1142
-                array(230, 'Akrotiri and Dhekelia', 'CY', 'CYP', 2),
1143
-                array(4, 'Andorra', 'AD', 'AND', 2),
1144
-                array(5, 'Angola', 'AO', 'AGO', 1),
1145
-                array(6, 'Anguilla', 'AI', 'AIA', 1),
1146
-                array(7, 'Antarctica', 'AQ', 'ATA', 1),
1147
-                array(8, 'Antigua and Barbuda', 'AG', 'ATG', 1),
1148
-                array(10, 'Saudi Arabia', 'SA', 'SAU', 1),
1149
-                array(11, 'Algeria', 'DZ', 'DZA', 1),
1150
-                array(12, 'Argentina', 'AR', 'ARG', 1),
1151
-                array(13, 'Armenia', 'AM', 'ARM', 1),
1152
-                array(14, 'Aruba', 'AW', 'ABW', 1),
1153
-                array(16, 'Austria', 'AT', 'AUT', 2),
1154
-                array(17, 'Azerbaijan', 'AZ', 'AZE', 1),
1155
-                array(18, 'Bahamas', 'BS', 'BHS', 1),
1156
-                array(19, 'Bahrain', 'BH', 'BHR', 1),
1157
-                array(20, 'Bangladesh', 'BD', 'BGD', 1),
1158
-                array(21, 'Barbados', 'BB', 'BRB', 1),
1159
-                array(22, 'Belgium ', 'BE', 'BEL', 2),
1160
-                array(23, 'Belize', 'BZ', 'BLZ', 1),
1161
-                array(24, 'Benin', 'BJ', 'BEN', 1),
1162
-                array(25, 'Bermudas', 'BM', 'BMU', 1),
1163
-                array(26, 'Belarus', 'BY', 'BLR', 1),
1164
-                array(27, 'Bolivia', 'BO', 'BOL', 1),
1165
-                array(28, 'Bosnia and Herzegovina', 'BA', 'BIH', 1),
1166
-                array(29, 'Botswana', 'BW', 'BWA', 1),
1167
-                array(96, 'Bouvet Island', 'BV', 'BVT', 1),
1168
-                array(30, 'Brazil', 'BR', 'BRA', 1),
1169
-                array(31, 'Brunei', 'BN', 'BRN', 1),
1170
-                array(32, 'Bulgaria', 'BG', 'BGR', 1),
1171
-                array(33, 'Burkina Faso', 'BF', 'BFA', 1),
1172
-                array(34, 'Burundi', 'BI', 'BDI', 1),
1173
-                array(35, 'Bhutan', 'BT', 'BTN', 1),
1174
-                array(36, 'Cape Verde', 'CV', 'CPV', 1),
1175
-                array(37, 'Cambodia', 'KH', 'KHM', 1),
1176
-                array(38, 'Cameroon', 'CM', 'CMR', 1),
1177
-                array(98, 'Cayman Islands', 'KY', 'CYM', 1),
1178
-                array(172, 'Central African Republic', 'CF', 'CAF', 1),
1179
-                array(40, 'Chad', 'TD', 'TCD', 1),
1180
-                array(41, 'Chile', 'CL', 'CHL', 1),
1181
-                array(42, 'China', 'CN', 'CHN', 1),
1182
-                array(105, 'Christmas Island', 'CX', 'CXR', 1),
1183
-                array(43, 'Cyprus', 'CY', 'CYP', 2),
1184
-                array(99, 'Cocos Island', 'CC', 'CCK', 1),
1185
-                array(100, 'Cook Islands', 'CK', 'COK', 1),
1186
-                array(44, 'Colombia', 'CO', 'COL', 1),
1187
-                array(45, 'Comoros', 'KM', 'COM', 1),
1188
-                array(46, 'Congo', 'CG', 'COG', 1),
1189
-                array(47, 'North Korea', 'KP', 'PRK', 1),
1190
-                array(50, 'Costa Rica', 'CR', 'CRI', 1),
1191
-                array(51, 'Croatia', 'HR', 'HRV', 1),
1192
-                array(52, 'Cuba', 'CU', 'CUB', 1),
1193
-                array(173, 'Czech Republic', 'CZ', 'CZE', 1),
1194
-                array(53, 'Denmark', 'DK', 'DNK', 1),
1195
-                array(54, 'Djibouti', 'DJ', 'DJI', 1),
1196
-                array(55, 'Dominica', 'DM', 'DMA', 1),
1197
-                array(174, 'Dominican Republic', 'DO', 'DOM', 1),
1198
-                array(56, 'Ecuador', 'EC', 'ECU', 1),
1199
-                array(57, 'Egypt', 'EG', 'EGY', 1),
1200
-                array(58, 'El Salvador', 'SV', 'SLV', 1),
1201
-                array(60, 'Eritrea', 'ER', 'ERI', 1),
1202
-                array(61, 'Slovakia', 'SK', 'SVK', 2),
1203
-                array(62, 'Slovenia', 'SI', 'SVN', 2),
1204
-                array(65, 'Estonia', 'EE', 'EST', 2),
1205
-                array(66, 'Ethiopia', 'ET', 'ETH', 1),
1206
-                array(102, 'Faroe islands', 'FO', 'FRO', 1),
1207
-                array(103, 'Falkland Islands', 'FK', 'FLK', 1),
1208
-                array(67, 'Fiji', 'FJ', 'FJI', 1),
1209
-                array(69, 'Finland', 'FI', 'FIN', 2),
1210
-                array(71, 'Gabon', 'GA', 'GAB', 1),
1211
-                array(72, 'Gambia', 'GM', 'GMB', 1),
1212
-                array(73, 'Georgia', 'GE', 'GEO', 1),
1213
-                array(74, 'Ghana', 'GH', 'GHA', 1),
1214
-                array(75, 'Gibraltar', 'GI', 'GIB', 1),
1215
-                array(76, 'Greece', 'GR', 'GRC', 2),
1216
-                array(77, 'Grenada', 'GD', 'GRD', 1),
1217
-                array(78, 'Greenland', 'GL', 'GRL', 1),
1218
-                array(79, 'Guadeloupe', 'GP', 'GLP', 1),
1219
-                array(80, 'Guam', 'GU', 'GUM', 1),
1220
-                array(81, 'Guatemala', 'GT', 'GTM', 1),
1221
-                array(82, 'Guinea', 'GN', 'GIN', 1),
1222
-                array(83, 'Equatorial Guinea', 'GQ', 'GNQ', 1),
1223
-                array(84, 'Guinea-Bissau', 'GW', 'GNB', 1),
1224
-                array(85, 'Guyana', 'GY', 'GUY', 1),
1225
-                array(86, 'Haiti', 'HT', 'HTI', 1),
1226
-                array(88, 'Honduras', 'HN', 'HND', 1),
1227
-                array(89, 'Hong Kong', 'HK', 'HKG', 1),
1228
-                array(90, 'Hungary', 'HU', 'HUN', 1),
1229
-                array(91, 'India', 'IN', 'IND', 1),
1230
-                array(205, 'British Indian Ocean Territory', 'IO', 'IOT', 1),
1231
-                array(92, 'Indonesia', 'ID', 'IDN', 1),
1232
-                array(93, 'Iraq', 'IQ', 'IRQ', 1),
1233
-                array(94, 'Iran', 'IR', 'IRN', 1),
1234
-                array(95, 'Ireland', 'IE', 'IRL', 2),
1235
-                array(97, 'Iceland', 'IS', 'ISL', 1),
1236
-                array(110, 'Israel', 'IL', 'ISR', 1),
1237
-                array(49, 'Ivory Coast ', 'CI', 'CIV', 1),
1238
-                array(112, 'Jamaica', 'JM', 'JAM', 1),
1239
-                array(113, 'Japan', 'JP', 'JPN', 1),
1240
-                array(114, 'Jordan', 'JO', 'JOR', 1),
1241
-                array(115, 'Kazakhstan', 'KZ', 'KAZ', 1),
1242
-                array(116, 'Kenya', 'KE', 'KEN', 1),
1243
-                array(117, 'Kyrgyzstan', 'KG', 'KGZ', 1),
1244
-                array(118, 'Kiribati', 'KI', 'KIR', 1),
1245
-                array(48, 'South Korea', 'KR', 'KOR', 1),
1246
-                array(228, 'Kosovo', 'XK', 'XKV', 2),
1247
-                // there is no official ISO code for Kosovo yet (http://geonames.wordpress.com/2010/03/08/xk-country-code-for-kosovo/) so using a temporary country code and a modified 3 character code for ISO code -- this should be updated if/when Kosovo gets its own ISO code
1248
-                array(119, 'Kuwait', 'KW', 'KWT', 1),
1249
-                array(120, 'Laos', 'LA', 'LAO', 1),
1250
-                array(121, 'Latvia', 'LV', 'LVA', 2),
1251
-                array(122, 'Lesotho', 'LS', 'LSO', 1),
1252
-                array(123, 'Lebanon', 'LB', 'LBN', 1),
1253
-                array(124, 'Liberia', 'LR', 'LBR', 1),
1254
-                array(125, 'Libya', 'LY', 'LBY', 1),
1255
-                array(126, 'Liechtenstein', 'LI', 'LIE', 1),
1256
-                array(127, 'Lithuania', 'LT', 'LTU', 2),
1257
-                array(128, 'Luxemburg', 'LU', 'LUX', 2),
1258
-                array(129, 'Macao', 'MO', 'MAC', 1),
1259
-                array(130, 'Macedonia', 'MK', 'MKD', 1),
1260
-                array(131, 'Madagascar', 'MG', 'MDG', 1),
1261
-                array(132, 'Malaysia', 'MY', 'MYS', 1),
1262
-                array(133, 'Malawi', 'MW', 'MWI', 1),
1263
-                array(134, 'Maldivas', 'MV', 'MDV', 1),
1264
-                array(135, 'Mali', 'ML', 'MLI', 1),
1265
-                array(136, 'Malta', 'MT', 'MLT', 2),
1266
-                array(101, 'Northern Marianas', 'MP', 'MNP', 1),
1267
-                array(137, 'Morocco', 'MA', 'MAR', 1),
1268
-                array(104, 'Marshall islands', 'MH', 'MHL', 1),
1269
-                array(138, 'Martinique', 'MQ', 'MTQ', 1),
1270
-                array(139, 'Mauritius', 'MU', 'MUS', 1),
1271
-                array(140, 'Mauritania', 'MR', 'MRT', 1),
1272
-                array(141, 'Mayote', 'YT', 'MYT', 2),
1273
-                array(142, 'Mexico', 'MX', 'MEX', 1),
1274
-                array(143, 'Micronesia', 'FM', 'FSM', 1),
1275
-                array(144, 'Moldova', 'MD', 'MDA', 1),
1276
-                array(145, 'Monaco', 'MC', 'MCO', 2),
1277
-                array(146, 'Mongolia', 'MN', 'MNG', 1),
1278
-                array(147, 'Montserrat', 'MS', 'MSR', 1),
1279
-                array(227, 'Montenegro', 'ME', 'MNE', 2),
1280
-                array(148, 'Mozambique', 'MZ', 'MOZ', 1),
1281
-                array(149, 'Myanmar', 'MM', 'MMR', 1),
1282
-                array(150, 'Namibia', 'NA', 'NAM', 1),
1283
-                array(151, 'Nauru', 'NR', 'NRU', 1),
1284
-                array(152, 'Nepal', 'NP', 'NPL', 1),
1285
-                array(9, 'Netherlands Antilles', 'AN', 'ANT', 1),
1286
-                array(153, 'Nicaragua', 'NI', 'NIC', 1),
1287
-                array(154, 'Niger', 'NE', 'NER', 1),
1288
-                array(155, 'Nigeria', 'NG', 'NGA', 1),
1289
-                array(156, 'Niue', 'NU', 'NIU', 1),
1290
-                array(157, 'Norway', 'NO', 'NOR', 1),
1291
-                array(158, 'New Caledonia', 'NC', 'NCL', 1),
1292
-                array(159, 'New Zealand', 'NZ', 'NZL', 1),
1293
-                array(160, 'Oman', 'OM', 'OMN', 1),
1294
-                array(161, 'Pakistan', 'PK', 'PAK', 1),
1295
-                array(162, 'Palau', 'PW', 'PLW', 1),
1296
-                array(163, 'Panama', 'PA', 'PAN', 1),
1297
-                array(164, 'Papua New Guinea', 'PG', 'PNG', 1),
1298
-                array(165, 'Paraguay', 'PY', 'PRY', 1),
1299
-                array(166, 'Peru', 'PE', 'PER', 1),
1300
-                array(68, 'Philippines', 'PH', 'PHL', 1),
1301
-                array(167, 'Poland', 'PL', 'POL', 1),
1302
-                array(168, 'Portugal', 'PT', 'PRT', 2),
1303
-                array(169, 'Puerto Rico', 'PR', 'PRI', 1),
1304
-                array(170, 'Qatar', 'QA', 'QAT', 1),
1305
-                array(176, 'Rwanda', 'RW', 'RWA', 1),
1306
-                array(177, 'Romania', 'RO', 'ROM', 2),
1307
-                array(178, 'Russia', 'RU', 'RUS', 1),
1308
-                array(229, 'Saint Pierre and Miquelon', 'PM', 'SPM', 2),
1309
-                array(180, 'Samoa', 'WS', 'WSM', 1),
1310
-                array(181, 'American Samoa', 'AS', 'ASM', 1),
1311
-                array(183, 'San Marino', 'SM', 'SMR', 2),
1312
-                array(184, 'Saint Vincent and the Grenadines', 'VC', 'VCT', 1),
1313
-                array(185, 'Saint Helena', 'SH', 'SHN', 1),
1314
-                array(186, 'Saint Lucia', 'LC', 'LCA', 1),
1315
-                array(188, 'Senegal', 'SN', 'SEN', 1),
1316
-                array(189, 'Seychelles', 'SC', 'SYC', 1),
1317
-                array(190, 'Sierra Leona', 'SL', 'SLE', 1),
1318
-                array(191, 'Singapore', 'SG', 'SGP', 1),
1319
-                array(192, 'Syria', 'SY', 'SYR', 1),
1320
-                array(193, 'Somalia', 'SO', 'SOM', 1),
1321
-                array(194, 'Sri Lanka', 'LK', 'LKA', 1),
1322
-                array(195, 'South Africa', 'ZA', 'ZAF', 1),
1323
-                array(196, 'Sudan', 'SD', 'SDN', 1),
1324
-                array(199, 'Suriname', 'SR', 'SUR', 1),
1325
-                array(200, 'Swaziland', 'SZ', 'SWZ', 1),
1326
-                array(201, 'Thailand', 'TH', 'THA', 1),
1327
-                array(202, 'Taiwan', 'TW', 'TWN', 1),
1328
-                array(203, 'Tanzania', 'TZ', 'TZA', 1),
1329
-                array(204, 'Tajikistan', 'TJ', 'TJK', 1),
1330
-                array(206, 'Timor-Leste', 'TL', 'TLS', 1),
1331
-                array(207, 'Togo', 'TG', 'TGO', 1),
1332
-                array(208, 'Tokelau', 'TK', 'TKL', 1),
1333
-                array(209, 'Tonga', 'TO', 'TON', 1),
1334
-                array(210, 'Trinidad and Tobago', 'TT', 'TTO', 1),
1335
-                array(211, 'Tunisia', 'TN', 'TUN', 1),
1336
-                array(212, 'Turkmenistan', 'TM', 'TKM', 1),
1337
-                array(213, 'Turkey', 'TR', 'TUR', 1),
1338
-                array(214, 'Tuvalu', 'TV', 'TUV', 1),
1339
-                array(215, 'Ukraine', 'UA', 'UKR', 1),
1340
-                array(216, 'Uganda', 'UG', 'UGA', 1),
1341
-                array(59, 'United Arab Emirates', 'AE', 'ARE', 1),
1342
-                array(217, 'Uruguay', 'UY', 'URY', 1),
1343
-                array(218, 'Uzbekistan', 'UZ', 'UZB', 1),
1344
-                array(219, 'Vanuatu', 'VU', 'VUT', 1),
1345
-                array(220, 'Vatican City', 'VA', 'VAT', 2),
1346
-                array(221, 'Venezuela', 'VE', 'VEN', 1),
1347
-                array(222, 'Vietnam', 'VN', 'VNM', 1),
1348
-                array(108, 'Virgin Islands', 'VI', 'VIR', 1),
1349
-                array(223, 'Yemen', 'YE', 'YEM', 1),
1350
-                array(225, 'Zambia', 'ZM', 'ZMB', 1),
1351
-                array(226, 'Zimbabwe', 'ZW', 'ZWE', 1),
1352
-        );
1353
-        $country_iso = 'US';
1354
-        foreach ($old_countries as $country_array) {
1355
-            //note: index 0 is the 3.1 country ID
1356
-            if ($country_array[0] == $country_id) {
1357
-                //note: index 2 is the ISO
1358
-                $country_iso = $country_array[2];
1359
-                break;
1360
-            }
1361
-        }
1362
-        return $country_iso;
1363
-    }
1364
-
1365
-
1366
-
1367
-    /**
1368
-     * Gets the ISO3 for the
1369
-     *
1370
-     * @return string
1371
-     */
1372
-    public function get_default_country_iso()
1373
-    {
1374
-        $old_org_options = get_option('events_organization_settings');
1375
-        $iso = $this->get_iso_from_3_1_country_id($old_org_options['organization_country']);
1376
-        return $iso;
1377
-    }
1378
-
1379
-
1380
-
1381
-    /**
1382
-     * Converst a 3.1 payment status to its equivalent 4.1 regisration status
1383
-     *
1384
-     * @param string  $payment_status                   possible value for 3.1's evens_attendee.payment_status
1385
-     * @param boolean $this_thing_required_pre_approval whether the thing we're considering (the general setting's
1386
-     *                                                  DEFAULT payment status, the event's DEFAULT payment status, or
1387
-     *                                                  the attendee's payment status) required pre-approval.
1388
-     * @return string STS_ID for use in 4.1
1389
-     */
1390
-    public function convert_3_1_payment_status_to_4_1_STS_ID($payment_status, $this_thing_required_pre_approval = false)
1391
-    {
1392
-        //EE team can read the related discussion: https://app.asana.com/0/2400967562914/9418495544455
1393
-        if ($this_thing_required_pre_approval) {
1394
-            return 'RNA';
1395
-        } else {
1396
-            $mapping = $default_reg_stati_conversions = array(
1397
-                    'Completed'        => 'RAP',
1398
-                    ''                 => 'RPP',
1399
-                    'Incomplete'       => 'RPP',
1400
-                    'Pending'          => 'RAP',
1401
-                    //stati that only occurred on 3.1 attendees:
1402
-                    'Payment Declined' => 'RPP',
1403
-                    'Not Completed'    => 'RPP',
1404
-                    'Cancelled'        => 'RPP',
1405
-                    'Declined'         => 'RPP',
1406
-            );
1407
-        }
1408
-        return isset($mapping[$payment_status]) ? $mapping[$payment_status] : 'RNA';
1409
-    }
1410
-
1411
-
1412
-
1413
-    /**
1414
-     * Makes sure the 3.1's image url is converted to an image attachment post to the 4.1 CPT event
1415
-     * and sets it as the featured image on the CPT event
1416
-     *
1417
-     * @param type                            $old_event
1418
-     * @param type                            $new_cpt_id
1419
-     * @param  EE_Data_Migration_Script_Stage $migration_stage the stage which called this, where errors should be added
1420
-     * @return boolean whether or not we had to do the big job of creating an image attachment
1421
-     */
1422
-    public function convert_image_url_to_attachment_and_attach_to_post(
1423
-            $guid,
1424
-            $new_cpt_id,
1425
-            EE_Data_Migration_Script_Stage $migration_stage
1426
-    ) {
1427
-        $created_attachment_post = false;
1428
-        $guid = $this->_get_original_guid($guid);
1429
-        if ($guid) {
1430
-            //check for an existing attachment post with this guid
1431
-            $attachment_post_id = $this->_get_image_attachment_id_by_GUID($guid);
1432
-            if ( ! $attachment_post_id) {
1433
-                //post thumbnail with that GUID doesn't exist, we should create one
1434
-                $attachment_post_id = $this->_create_image_attachment_from_GUID($guid, $migration_stage);
1435
-                $created_attachment_post = true;
1436
-            }
1437
-            //double-check we actually have an attachment post
1438
-            if ($attachment_post_id) {
1439
-                update_post_meta($new_cpt_id, '_thumbnail_id', $attachment_post_id);
1440
-            } else {
1441
-                $migration_stage->add_error(sprintf(esc_html__("Could not update event image %s for CPT with ID %d, but attachments post ID is %d",
1442
-                        "event_espresso"), $guid, $new_cpt_id, $attachment_post_id));
1443
-            }
1444
-        }
1445
-        return $created_attachment_post;
1446
-    }
1447
-
1448
-
1449
-
1450
-    /**
1451
-     * In 3.1, the event thumbnail image DOESN'T point to the orignal image, but instead
1452
-     * to a large thumbnail (which has nearly the same GUID, except it adds "-{width}x{height}" before the filetype,
1453
-     * or whatever dimensions it is. Eg 'http://mysite.com/image1-300x400.jpg' instead of
1454
-     * 'http://mysite.com/image1.jpg' ). This function attempts to strip that off and get the original file, if it
1455
-     * exists
1456
-     *
1457
-     * @param string $guid_in_old_event
1458
-     * @return string either the original guid, or $guid_in_old_event if we couldn't figure out what the original was
1459
-     */
1460
-    private function _get_original_guid($guid_in_old_event)
1461
-    {
1462
-        $original_guid = preg_replace('~-\d*x\d*\.~', '.', $guid_in_old_event, 1);
1463
-        //do a head request to verify the file exists
1464
-        $head_response = wp_remote_head($original_guid);
1465
-        if ( ! $head_response instanceof WP_Error && $head_response['response']['message'] == 'OK') {
1466
-            return $original_guid;
1467
-        } else {
1468
-            return $guid_in_old_event;
1469
-        }
1470
-    }
1471
-
1472
-
1473
-
1474
-    /**
1475
-     * Creates an image attachment post for the GUID. If the GUID points to a remote image,
1476
-     * we download it to our uploads directory so that it can be properly processed (eg, creates different sizes of
1477
-     * thumbnails)
1478
-     *
1479
-     * @param type                           $guid
1480
-     * @param EE_Data_Migration_Script_Stage $migration_stage
1481
-     * @return int
1482
-     */
1483
-    private function _create_image_attachment_from_GUID($guid, EE_Data_Migration_Script_Stage $migration_stage)
1484
-    {
1485
-        if ( ! $guid) {
1486
-            $migration_stage->add_error(sprintf(esc_html__("Cannot create image attachment for a blank GUID!",
1487
-                    "event_espresso")));
1488
-            return 0;
1489
-        }
1490
-        $wp_filetype = wp_check_filetype(basename($guid), null);
1491
-        $wp_upload_dir = wp_upload_dir();
1492
-        //if the file is located remotely, download it to our uploads DIR, because wp_genereate_attachmnet_metadata needs the file to be local
1493
-        if (strpos($guid, $wp_upload_dir['url']) === false) {
1494
-            //image is located remotely. download it and place it in the uploads directory
1495
-            if ( ! is_readable($guid)) {
1496
-                $migration_stage->add_error(sprintf(esc_html__("Could not create image attachment from non-existent file: %s",
1497
-                        "event_espresso"), $guid));
1498
-                return 0;
1499
-            }
1500
-            $contents = file_get_contents($guid);
1501
-            if ($contents === false) {
1502
-                $migration_stage->add_error(sprintf(esc_html__("Could not read image at %s, and therefore couldnt create an attachment post for it.",
1503
-                        "event_espresso"), $guid));
1504
-                return false;
1505
-            }
1506
-            $local_filepath = $wp_upload_dir['path'] . DS . basename($guid);
1507
-            $savefile = fopen($local_filepath, 'w');
1508
-            fwrite($savefile, $contents);
1509
-            fclose($savefile);
1510
-            $guid = str_replace($wp_upload_dir['path'], $wp_upload_dir['url'], $local_filepath);
1511
-        } else {
1512
-            $local_filepath = str_replace($wp_upload_dir['url'], $wp_upload_dir['path'], $guid);
1513
-        }
1514
-        $attachment = array(
1515
-                'guid'           => $guid,
1516
-                'post_mime_type' => $wp_filetype['type'],
1517
-                'post_title'     => preg_replace('/\.[^.]+$/', '', basename($guid)),
1518
-                'post_content'   => '',
1519
-                'post_status'    => 'inherit',
1520
-        );
1521
-        $attach_id = wp_insert_attachment($attachment, $guid);
1522
-        if ( ! $attach_id) {
1523
-            $migration_stage->add_error(sprintf(esc_html__("Could not create image attachment post from image '%s'. Attachment data was %s.",
1524
-                    "event_espresso"), $guid, $this->_json_encode($attachment)));
1525
-            return $attach_id;
1526
-        }
1527
-        // you must first include the image.php file
1528
-        // for the function wp_generate_attachment_metadata() to work
1529
-        require_once(ABSPATH . 'wp-admin/includes/image.php');
1530
-        $attach_data = wp_generate_attachment_metadata($attach_id, $local_filepath);
1531
-        if ( ! $attach_data) {
1532
-            $migration_stage->add_error(sprintf(esc_html__("Coudl not genereate attachment metadata for attachment post %d with filepath %s and GUID %s. Please check the file was downloaded properly.",
1533
-                    "event_espresso"), $attach_id, $local_filepath, $guid));
1534
-            return $attach_id;
1535
-        }
1536
-        $metadata_save_result = wp_update_attachment_metadata($attach_id, $attach_data);
1537
-        if ( ! $metadata_save_result) {
1538
-            $migration_stage->add_error(sprintf(esc_html__("Could not update attachment metadata for attachment %d with data %s",
1539
-                    "event_espresso"), $attach_id, $this->_json_encode($attach_data)));
1540
-        }
1541
-        return $attach_id;
1542
-    }
1543
-
1544
-
1545
-
1546
-    /**
1547
-     * Finds the attachment post containing info about an image attachment given the GUID (link to the image itself),
1548
-     * and returns its ID.
1549
-     *
1550
-     * @global type  $wpdb
1551
-     * @param string $guid
1552
-     * @return int
1553
-     */
1554
-    private function _get_image_attachment_id_by_GUID($guid)
1555
-    {
1556
-        global $wpdb;
1557
-        $attachment_id = $wpdb->get_var($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE guid=%s LIMIT 1", $guid));
1558
-        return $attachment_id;
1559
-    }
1560
-
1561
-
1562
-
1563
-    /**
1564
-     * Returns a mysql-formatted DATETIME in UTC time, given a $DATETIME_string
1565
-     * (and optionally a timezone; if none is given, the wp DEFAULT is used)
1566
-     *
1567
-     * @param EE_Data_Migration_Script_base $stage
1568
-     * @param array                         $row_of_data , the row from the DB (as an array) we're trying to find the
1569
-     *                                                   UTC time for
1570
-     * @param string                        $DATETIME_string
1571
-     * @param string                        $timezone
1572
-     * @return string
1573
-     */
1574
-    public function convert_date_string_to_utc(
1575
-            EE_Data_Migration_Script_Stage $stage,
1576
-            $row_of_data,
1577
-            $DATETIME_string,
1578
-            $timezone = null
1579
-    ) {
1580
-        $original_tz = $timezone;
1581
-        if ( ! $timezone) {
1582
-            $timezone = $this->_get_wp_timezone();
1583
-        }
1584
-        if ( ! $timezone) {
1585
-            $stage->add_error(sprintf(esc_html__("Could not find timezone given %s for %s", "event_espresso"), $original_tz,
1586
-                    $row_of_data));
1587
-            $timezone = 'UTC';
1588
-        }
1589
-        try {
1590
-            $date_obj = new DateTime($DATETIME_string, new DateTimeZone($timezone));
1591
-            EEH_DTT_Helper::setTimezone($date_obj, new DateTimeZone('UTC'));
1592
-        } catch (Exception $e) {
1593
-            $stage->add_error(sprintf(esc_html__("Could not convert time string '%s' using timezone '%s' into a proper DATETIME. Using current time instead.",
1594
-                    "event_espresso"), $DATETIME_string, $timezone));
1595
-            $date_obj = new DateTime();
1596
-        }
1597
-        return $date_obj->format('Y-m-d H:i:s');
1598
-    }
1599
-
1600
-
1601
-
1602
-    /**
1603
-     * Gets the DEFAULT timezone string from wordpress (even if they set a gmt offset)
1604
-     *
1605
-     * @return string
1606
-     */
1607
-    private function _get_wp_timezone()
1608
-    {
1609
-        $timezone = empty($timezone) ? get_option('timezone_string') : $timezone;
1610
-        //if timezone is STILL empty then let's get the GMT offset and then set the timezone_string using our converter
1611
-        if (empty($timezone)) {
1612
-            //let's get a the WordPress UTC offset
1613
-            $offset = get_option('gmt_offset');
1614
-            $timezone = $this->timezone_convert_to_string_from_offset($offset);
1615
-        }
1616
-        return $timezone;
1617
-    }
1618
-
1619
-
1620
-
1621
-    /**
1622
-     * Gets the wordpress timezone string from a UTC offset
1623
-     *
1624
-     * @param int $offset
1625
-     * @return boolean
1626
-     */
1627
-    private function timezone_convert_to_string_from_offset($offset)
1628
-    {
1629
-        //shamelessly taken from bottom comment at http://ca1.php.net/manual/en/function.timezone-name-from-abbr.php because timezone_name_from_abbr() did NOT work as expected - its not reliable
1630
-        $offset *= 3600; // convert hour offset to seconds
1631
-        $abbrarray = timezone_abbreviations_list();
1632
-        foreach ($abbrarray as $abbr) {
1633
-            foreach ($abbr as $city) {
1634
-                if ($city['offset'] == $offset) {
1635
-                    return $city['timezone_id'];
1636
-                }
1637
-            }
1638
-        }
1639
-        return false;
1640
-    }
1641
-
1642
-
1643
-
1644
-    public function migration_page_hooks()
1645
-    {
1646
-        add_filter(
1647
-                'FHEE__ee_migration_page__header',
1648
-                array($this, '_migrate_page_hook_simplify_version_strings'),
1649
-                10,
1650
-                3
1651
-        );
1652
-        add_filter(
1653
-                'FHEE__ee_migration_page__p_after_header',
1654
-                array($this, '_migration_page_hook_simplify_next_db_state'),
1655
-                10,
1656
-                2
1657
-        );
1658
-        add_filter(
1659
-                'FHEE__ee_migration_page__option_1_main',
1660
-                array($this, '_migrate_page_hook_simplify_version_strings'),
1661
-                10,
1662
-                3
1663
-        );
1664
-        add_filter(
1665
-                'FHEE__ee_migration_page__option_1_button_text',
1666
-                array($this, '_migrate_page_hook_simplify_version_strings'),
1667
-                10,
1668
-                3
1669
-        );
1670
-        add_action(
1671
-                'AHEE__ee_migration_page__option_1_extra_details',
1672
-                array($this, '_migration_page_hook_option_1_extra_details'),
1673
-                10,
1674
-                3
1675
-        );
1676
-        add_filter(
1677
-                'FHEE__ee_migration_page__option_2_main',
1678
-                array($this, '_migrate_page_hook_simplify_version_strings'),
1679
-                10,
1680
-                4
1681
-        );
1682
-        add_filter(
1683
-                'FHEE__ee_migration_page__option_2_button_text',
1684
-                array($this, '_migration_page_hook_simplify_next_db_state'),
1685
-                10,
1686
-                2
1687
-        );
1688
-        add_filter(
1689
-                'FHEE__ee_migration_page__option_2_details',
1690
-                array($this, '_migration_page_hook_simplify_next_db_state'),
1691
-                10,
1692
-                2
1693
-        );
1694
-        add_action(
1695
-                'AHEE__ee_migration_page__after_migration_options_table',
1696
-                array($this, '_migration_page_hook_after_migration_options_table')
1697
-        );
1698
-        add_filter(
1699
-                'FHEE__ee_migration_page__done_migration_header',
1700
-                array($this, '_migration_page_hook_simplify_next_db_state'),
1701
-                10,
1702
-                2
1703
-        );
1704
-        add_filter(
1705
-                'FHEE__ee_migration_page__p_after_done_migration_header',
1706
-                array($this, '_migration_page_hook_simplify_next_db_state'),
1707
-                10,
1708
-                2
1709
-        );
1710
-        add_filter(
1711
-                'FHEE__ee_migration_page__migration_options_template',
1712
-                array($this,'use_migration_options_from_ee3_template')
1713
-        );
1714
-    }
1715
-
1716
-
1717
-
1718
-    public function _migrate_page_hook_simplify_version_strings(
1719
-            $old_content,
1720
-            $current_db_state,
1721
-            $next_db_state,
1722
-            $ultimate_db_state = null
1723
-    ) {
1724
-        return str_replace(array($current_db_state, $next_db_state, $ultimate_db_state),
1725
-                array(esc_html__('EE3', 'event_espresso'), esc_html__('EE4', 'event_espresso'), esc_html__("EE4", 'event_espresso')),
1726
-                $old_content);
1727
-    }
1728
-
1729
-
1730
-
1731
-    public function _migration_page_hook_simplify_next_db_state($old_content, $next_db_state)
1732
-    {
1733
-        return str_replace($next_db_state, esc_html__("EE4", 'event_espresso'), $old_content);
1734
-    }
1735
-
1736
-
1737
-
1738
-    public function _migration_page_hook_option_1_extra_details()
1739
-    {
1740
-        ?>
1063
+		if ( ! $state) {
1064
+			//insert a new one then
1065
+			$cols_n_values = array(
1066
+					'CNT_ISO'    => $country_iso,
1067
+					'STA_abbrev' => substr($state_name, 0, 6),
1068
+					'STA_name'   => $state_name,
1069
+					'STA_active' => true,
1070
+			);
1071
+			$data_types = array(
1072
+					'%s',//CNT_ISO
1073
+					'%s',//STA_abbrev
1074
+					'%s',//STA_name
1075
+					'%d',//STA_active
1076
+			);
1077
+			$success = $wpdb->insert($state_table, $cols_n_values, $data_types);
1078
+			if ( ! $success) {
1079
+				throw new EE_Error($this->_create_error_message_for_db_insertion('N/A',
1080
+						array('state' => $state_name, 'country_id' => $country_name), $state_table, $cols_n_values,
1081
+						$data_types));
1082
+			}
1083
+			$state = $cols_n_values;
1084
+			$state['STA_ID'] = $wpdb->insert_id;
1085
+		}
1086
+		return $state;
1087
+	}
1088
+
1089
+
1090
+
1091
+	/**
1092
+	 * Fixes times like "5:00 PM" into the expected 24-hour format "17:00".
1093
+	 * THis is actually just copied from the 3.1 JSON API because it needed to do the exact same thing
1094
+	 *
1095
+	 * @param type $timeString
1096
+	 * @return string in the php DATETIME format: "G:i" (24-hour format hour with leading zeros, a colon, and minutes
1097
+	 *                with leading zeros)
1098
+	 */
1099
+	public function convertTimeFromAMPM($timeString)
1100
+	{
1101
+		$matches = array();
1102
+		preg_match("~(\\d*):(\\d*)~", $timeString, $matches);
1103
+		if ( ! $matches || count($matches) < 3) {
1104
+			$hour = '00';
1105
+			$minutes = '00';
1106
+		} else {
1107
+			$hour = intval($matches[1]);
1108
+			$minutes = $matches[2];
1109
+		}
1110
+		if (strpos($timeString, 'PM') || strpos($timeString, 'pm')) {
1111
+			$hour = intval($hour) + 12;
1112
+		}
1113
+		$hour = str_pad("$hour", 2, '0', STR_PAD_LEFT);
1114
+		$minutes = str_pad("$minutes", 2, '0', STR_PAD_LEFT);
1115
+		return "$hour:$minutes";
1116
+	}
1117
+
1118
+
1119
+
1120
+	/**
1121
+	 * Gets the ISO3 fora country given its 3.1 country ID.
1122
+	 *
1123
+	 * @param int $country_id
1124
+	 * @return string the country's ISO3 code
1125
+	 */
1126
+	public function get_iso_from_3_1_country_id($country_id)
1127
+	{
1128
+		$old_countries = array(
1129
+				array(64, 'United States', 'US', 'USA', 1),
1130
+				array(15, 'Australia', 'AU', 'AUS', 1),
1131
+				array(39, 'Canada', 'CA', 'CAN', 1),
1132
+				array(171, 'United Kingdom', 'GB', 'GBR', 1),
1133
+				array(70, 'France', 'FR', 'FRA', 2),
1134
+				array(111, 'Italy', 'IT', 'ITA', 2),
1135
+				array(63, 'Spain', 'ES', 'ESP', 2),
1136
+				array(1, 'Afghanistan', 'AF', 'AFG', 1),
1137
+				array(2, 'Albania', 'AL', 'ALB', 1),
1138
+				array(3, 'Germany', 'DE', 'DEU', 2),
1139
+				array(198, 'Switzerland', 'CH', 'CHE', 1),
1140
+				array(87, 'Netherlands', 'NL', 'NLD', 2),
1141
+				array(197, 'Sweden', 'SE', 'SWE', 1),
1142
+				array(230, 'Akrotiri and Dhekelia', 'CY', 'CYP', 2),
1143
+				array(4, 'Andorra', 'AD', 'AND', 2),
1144
+				array(5, 'Angola', 'AO', 'AGO', 1),
1145
+				array(6, 'Anguilla', 'AI', 'AIA', 1),
1146
+				array(7, 'Antarctica', 'AQ', 'ATA', 1),
1147
+				array(8, 'Antigua and Barbuda', 'AG', 'ATG', 1),
1148
+				array(10, 'Saudi Arabia', 'SA', 'SAU', 1),
1149
+				array(11, 'Algeria', 'DZ', 'DZA', 1),
1150
+				array(12, 'Argentina', 'AR', 'ARG', 1),
1151
+				array(13, 'Armenia', 'AM', 'ARM', 1),
1152
+				array(14, 'Aruba', 'AW', 'ABW', 1),
1153
+				array(16, 'Austria', 'AT', 'AUT', 2),
1154
+				array(17, 'Azerbaijan', 'AZ', 'AZE', 1),
1155
+				array(18, 'Bahamas', 'BS', 'BHS', 1),
1156
+				array(19, 'Bahrain', 'BH', 'BHR', 1),
1157
+				array(20, 'Bangladesh', 'BD', 'BGD', 1),
1158
+				array(21, 'Barbados', 'BB', 'BRB', 1),
1159
+				array(22, 'Belgium ', 'BE', 'BEL', 2),
1160
+				array(23, 'Belize', 'BZ', 'BLZ', 1),
1161
+				array(24, 'Benin', 'BJ', 'BEN', 1),
1162
+				array(25, 'Bermudas', 'BM', 'BMU', 1),
1163
+				array(26, 'Belarus', 'BY', 'BLR', 1),
1164
+				array(27, 'Bolivia', 'BO', 'BOL', 1),
1165
+				array(28, 'Bosnia and Herzegovina', 'BA', 'BIH', 1),
1166
+				array(29, 'Botswana', 'BW', 'BWA', 1),
1167
+				array(96, 'Bouvet Island', 'BV', 'BVT', 1),
1168
+				array(30, 'Brazil', 'BR', 'BRA', 1),
1169
+				array(31, 'Brunei', 'BN', 'BRN', 1),
1170
+				array(32, 'Bulgaria', 'BG', 'BGR', 1),
1171
+				array(33, 'Burkina Faso', 'BF', 'BFA', 1),
1172
+				array(34, 'Burundi', 'BI', 'BDI', 1),
1173
+				array(35, 'Bhutan', 'BT', 'BTN', 1),
1174
+				array(36, 'Cape Verde', 'CV', 'CPV', 1),
1175
+				array(37, 'Cambodia', 'KH', 'KHM', 1),
1176
+				array(38, 'Cameroon', 'CM', 'CMR', 1),
1177
+				array(98, 'Cayman Islands', 'KY', 'CYM', 1),
1178
+				array(172, 'Central African Republic', 'CF', 'CAF', 1),
1179
+				array(40, 'Chad', 'TD', 'TCD', 1),
1180
+				array(41, 'Chile', 'CL', 'CHL', 1),
1181
+				array(42, 'China', 'CN', 'CHN', 1),
1182
+				array(105, 'Christmas Island', 'CX', 'CXR', 1),
1183
+				array(43, 'Cyprus', 'CY', 'CYP', 2),
1184
+				array(99, 'Cocos Island', 'CC', 'CCK', 1),
1185
+				array(100, 'Cook Islands', 'CK', 'COK', 1),
1186
+				array(44, 'Colombia', 'CO', 'COL', 1),
1187
+				array(45, 'Comoros', 'KM', 'COM', 1),
1188
+				array(46, 'Congo', 'CG', 'COG', 1),
1189
+				array(47, 'North Korea', 'KP', 'PRK', 1),
1190
+				array(50, 'Costa Rica', 'CR', 'CRI', 1),
1191
+				array(51, 'Croatia', 'HR', 'HRV', 1),
1192
+				array(52, 'Cuba', 'CU', 'CUB', 1),
1193
+				array(173, 'Czech Republic', 'CZ', 'CZE', 1),
1194
+				array(53, 'Denmark', 'DK', 'DNK', 1),
1195
+				array(54, 'Djibouti', 'DJ', 'DJI', 1),
1196
+				array(55, 'Dominica', 'DM', 'DMA', 1),
1197
+				array(174, 'Dominican Republic', 'DO', 'DOM', 1),
1198
+				array(56, 'Ecuador', 'EC', 'ECU', 1),
1199
+				array(57, 'Egypt', 'EG', 'EGY', 1),
1200
+				array(58, 'El Salvador', 'SV', 'SLV', 1),
1201
+				array(60, 'Eritrea', 'ER', 'ERI', 1),
1202
+				array(61, 'Slovakia', 'SK', 'SVK', 2),
1203
+				array(62, 'Slovenia', 'SI', 'SVN', 2),
1204
+				array(65, 'Estonia', 'EE', 'EST', 2),
1205
+				array(66, 'Ethiopia', 'ET', 'ETH', 1),
1206
+				array(102, 'Faroe islands', 'FO', 'FRO', 1),
1207
+				array(103, 'Falkland Islands', 'FK', 'FLK', 1),
1208
+				array(67, 'Fiji', 'FJ', 'FJI', 1),
1209
+				array(69, 'Finland', 'FI', 'FIN', 2),
1210
+				array(71, 'Gabon', 'GA', 'GAB', 1),
1211
+				array(72, 'Gambia', 'GM', 'GMB', 1),
1212
+				array(73, 'Georgia', 'GE', 'GEO', 1),
1213
+				array(74, 'Ghana', 'GH', 'GHA', 1),
1214
+				array(75, 'Gibraltar', 'GI', 'GIB', 1),
1215
+				array(76, 'Greece', 'GR', 'GRC', 2),
1216
+				array(77, 'Grenada', 'GD', 'GRD', 1),
1217
+				array(78, 'Greenland', 'GL', 'GRL', 1),
1218
+				array(79, 'Guadeloupe', 'GP', 'GLP', 1),
1219
+				array(80, 'Guam', 'GU', 'GUM', 1),
1220
+				array(81, 'Guatemala', 'GT', 'GTM', 1),
1221
+				array(82, 'Guinea', 'GN', 'GIN', 1),
1222
+				array(83, 'Equatorial Guinea', 'GQ', 'GNQ', 1),
1223
+				array(84, 'Guinea-Bissau', 'GW', 'GNB', 1),
1224
+				array(85, 'Guyana', 'GY', 'GUY', 1),
1225
+				array(86, 'Haiti', 'HT', 'HTI', 1),
1226
+				array(88, 'Honduras', 'HN', 'HND', 1),
1227
+				array(89, 'Hong Kong', 'HK', 'HKG', 1),
1228
+				array(90, 'Hungary', 'HU', 'HUN', 1),
1229
+				array(91, 'India', 'IN', 'IND', 1),
1230
+				array(205, 'British Indian Ocean Territory', 'IO', 'IOT', 1),
1231
+				array(92, 'Indonesia', 'ID', 'IDN', 1),
1232
+				array(93, 'Iraq', 'IQ', 'IRQ', 1),
1233
+				array(94, 'Iran', 'IR', 'IRN', 1),
1234
+				array(95, 'Ireland', 'IE', 'IRL', 2),
1235
+				array(97, 'Iceland', 'IS', 'ISL', 1),
1236
+				array(110, 'Israel', 'IL', 'ISR', 1),
1237
+				array(49, 'Ivory Coast ', 'CI', 'CIV', 1),
1238
+				array(112, 'Jamaica', 'JM', 'JAM', 1),
1239
+				array(113, 'Japan', 'JP', 'JPN', 1),
1240
+				array(114, 'Jordan', 'JO', 'JOR', 1),
1241
+				array(115, 'Kazakhstan', 'KZ', 'KAZ', 1),
1242
+				array(116, 'Kenya', 'KE', 'KEN', 1),
1243
+				array(117, 'Kyrgyzstan', 'KG', 'KGZ', 1),
1244
+				array(118, 'Kiribati', 'KI', 'KIR', 1),
1245
+				array(48, 'South Korea', 'KR', 'KOR', 1),
1246
+				array(228, 'Kosovo', 'XK', 'XKV', 2),
1247
+				// there is no official ISO code for Kosovo yet (http://geonames.wordpress.com/2010/03/08/xk-country-code-for-kosovo/) so using a temporary country code and a modified 3 character code for ISO code -- this should be updated if/when Kosovo gets its own ISO code
1248
+				array(119, 'Kuwait', 'KW', 'KWT', 1),
1249
+				array(120, 'Laos', 'LA', 'LAO', 1),
1250
+				array(121, 'Latvia', 'LV', 'LVA', 2),
1251
+				array(122, 'Lesotho', 'LS', 'LSO', 1),
1252
+				array(123, 'Lebanon', 'LB', 'LBN', 1),
1253
+				array(124, 'Liberia', 'LR', 'LBR', 1),
1254
+				array(125, 'Libya', 'LY', 'LBY', 1),
1255
+				array(126, 'Liechtenstein', 'LI', 'LIE', 1),
1256
+				array(127, 'Lithuania', 'LT', 'LTU', 2),
1257
+				array(128, 'Luxemburg', 'LU', 'LUX', 2),
1258
+				array(129, 'Macao', 'MO', 'MAC', 1),
1259
+				array(130, 'Macedonia', 'MK', 'MKD', 1),
1260
+				array(131, 'Madagascar', 'MG', 'MDG', 1),
1261
+				array(132, 'Malaysia', 'MY', 'MYS', 1),
1262
+				array(133, 'Malawi', 'MW', 'MWI', 1),
1263
+				array(134, 'Maldivas', 'MV', 'MDV', 1),
1264
+				array(135, 'Mali', 'ML', 'MLI', 1),
1265
+				array(136, 'Malta', 'MT', 'MLT', 2),
1266
+				array(101, 'Northern Marianas', 'MP', 'MNP', 1),
1267
+				array(137, 'Morocco', 'MA', 'MAR', 1),
1268
+				array(104, 'Marshall islands', 'MH', 'MHL', 1),
1269
+				array(138, 'Martinique', 'MQ', 'MTQ', 1),
1270
+				array(139, 'Mauritius', 'MU', 'MUS', 1),
1271
+				array(140, 'Mauritania', 'MR', 'MRT', 1),
1272
+				array(141, 'Mayote', 'YT', 'MYT', 2),
1273
+				array(142, 'Mexico', 'MX', 'MEX', 1),
1274
+				array(143, 'Micronesia', 'FM', 'FSM', 1),
1275
+				array(144, 'Moldova', 'MD', 'MDA', 1),
1276
+				array(145, 'Monaco', 'MC', 'MCO', 2),
1277
+				array(146, 'Mongolia', 'MN', 'MNG', 1),
1278
+				array(147, 'Montserrat', 'MS', 'MSR', 1),
1279
+				array(227, 'Montenegro', 'ME', 'MNE', 2),
1280
+				array(148, 'Mozambique', 'MZ', 'MOZ', 1),
1281
+				array(149, 'Myanmar', 'MM', 'MMR', 1),
1282
+				array(150, 'Namibia', 'NA', 'NAM', 1),
1283
+				array(151, 'Nauru', 'NR', 'NRU', 1),
1284
+				array(152, 'Nepal', 'NP', 'NPL', 1),
1285
+				array(9, 'Netherlands Antilles', 'AN', 'ANT', 1),
1286
+				array(153, 'Nicaragua', 'NI', 'NIC', 1),
1287
+				array(154, 'Niger', 'NE', 'NER', 1),
1288
+				array(155, 'Nigeria', 'NG', 'NGA', 1),
1289
+				array(156, 'Niue', 'NU', 'NIU', 1),
1290
+				array(157, 'Norway', 'NO', 'NOR', 1),
1291
+				array(158, 'New Caledonia', 'NC', 'NCL', 1),
1292
+				array(159, 'New Zealand', 'NZ', 'NZL', 1),
1293
+				array(160, 'Oman', 'OM', 'OMN', 1),
1294
+				array(161, 'Pakistan', 'PK', 'PAK', 1),
1295
+				array(162, 'Palau', 'PW', 'PLW', 1),
1296
+				array(163, 'Panama', 'PA', 'PAN', 1),
1297
+				array(164, 'Papua New Guinea', 'PG', 'PNG', 1),
1298
+				array(165, 'Paraguay', 'PY', 'PRY', 1),
1299
+				array(166, 'Peru', 'PE', 'PER', 1),
1300
+				array(68, 'Philippines', 'PH', 'PHL', 1),
1301
+				array(167, 'Poland', 'PL', 'POL', 1),
1302
+				array(168, 'Portugal', 'PT', 'PRT', 2),
1303
+				array(169, 'Puerto Rico', 'PR', 'PRI', 1),
1304
+				array(170, 'Qatar', 'QA', 'QAT', 1),
1305
+				array(176, 'Rwanda', 'RW', 'RWA', 1),
1306
+				array(177, 'Romania', 'RO', 'ROM', 2),
1307
+				array(178, 'Russia', 'RU', 'RUS', 1),
1308
+				array(229, 'Saint Pierre and Miquelon', 'PM', 'SPM', 2),
1309
+				array(180, 'Samoa', 'WS', 'WSM', 1),
1310
+				array(181, 'American Samoa', 'AS', 'ASM', 1),
1311
+				array(183, 'San Marino', 'SM', 'SMR', 2),
1312
+				array(184, 'Saint Vincent and the Grenadines', 'VC', 'VCT', 1),
1313
+				array(185, 'Saint Helena', 'SH', 'SHN', 1),
1314
+				array(186, 'Saint Lucia', 'LC', 'LCA', 1),
1315
+				array(188, 'Senegal', 'SN', 'SEN', 1),
1316
+				array(189, 'Seychelles', 'SC', 'SYC', 1),
1317
+				array(190, 'Sierra Leona', 'SL', 'SLE', 1),
1318
+				array(191, 'Singapore', 'SG', 'SGP', 1),
1319
+				array(192, 'Syria', 'SY', 'SYR', 1),
1320
+				array(193, 'Somalia', 'SO', 'SOM', 1),
1321
+				array(194, 'Sri Lanka', 'LK', 'LKA', 1),
1322
+				array(195, 'South Africa', 'ZA', 'ZAF', 1),
1323
+				array(196, 'Sudan', 'SD', 'SDN', 1),
1324
+				array(199, 'Suriname', 'SR', 'SUR', 1),
1325
+				array(200, 'Swaziland', 'SZ', 'SWZ', 1),
1326
+				array(201, 'Thailand', 'TH', 'THA', 1),
1327
+				array(202, 'Taiwan', 'TW', 'TWN', 1),
1328
+				array(203, 'Tanzania', 'TZ', 'TZA', 1),
1329
+				array(204, 'Tajikistan', 'TJ', 'TJK', 1),
1330
+				array(206, 'Timor-Leste', 'TL', 'TLS', 1),
1331
+				array(207, 'Togo', 'TG', 'TGO', 1),
1332
+				array(208, 'Tokelau', 'TK', 'TKL', 1),
1333
+				array(209, 'Tonga', 'TO', 'TON', 1),
1334
+				array(210, 'Trinidad and Tobago', 'TT', 'TTO', 1),
1335
+				array(211, 'Tunisia', 'TN', 'TUN', 1),
1336
+				array(212, 'Turkmenistan', 'TM', 'TKM', 1),
1337
+				array(213, 'Turkey', 'TR', 'TUR', 1),
1338
+				array(214, 'Tuvalu', 'TV', 'TUV', 1),
1339
+				array(215, 'Ukraine', 'UA', 'UKR', 1),
1340
+				array(216, 'Uganda', 'UG', 'UGA', 1),
1341
+				array(59, 'United Arab Emirates', 'AE', 'ARE', 1),
1342
+				array(217, 'Uruguay', 'UY', 'URY', 1),
1343
+				array(218, 'Uzbekistan', 'UZ', 'UZB', 1),
1344
+				array(219, 'Vanuatu', 'VU', 'VUT', 1),
1345
+				array(220, 'Vatican City', 'VA', 'VAT', 2),
1346
+				array(221, 'Venezuela', 'VE', 'VEN', 1),
1347
+				array(222, 'Vietnam', 'VN', 'VNM', 1),
1348
+				array(108, 'Virgin Islands', 'VI', 'VIR', 1),
1349
+				array(223, 'Yemen', 'YE', 'YEM', 1),
1350
+				array(225, 'Zambia', 'ZM', 'ZMB', 1),
1351
+				array(226, 'Zimbabwe', 'ZW', 'ZWE', 1),
1352
+		);
1353
+		$country_iso = 'US';
1354
+		foreach ($old_countries as $country_array) {
1355
+			//note: index 0 is the 3.1 country ID
1356
+			if ($country_array[0] == $country_id) {
1357
+				//note: index 2 is the ISO
1358
+				$country_iso = $country_array[2];
1359
+				break;
1360
+			}
1361
+		}
1362
+		return $country_iso;
1363
+	}
1364
+
1365
+
1366
+
1367
+	/**
1368
+	 * Gets the ISO3 for the
1369
+	 *
1370
+	 * @return string
1371
+	 */
1372
+	public function get_default_country_iso()
1373
+	{
1374
+		$old_org_options = get_option('events_organization_settings');
1375
+		$iso = $this->get_iso_from_3_1_country_id($old_org_options['organization_country']);
1376
+		return $iso;
1377
+	}
1378
+
1379
+
1380
+
1381
+	/**
1382
+	 * Converst a 3.1 payment status to its equivalent 4.1 regisration status
1383
+	 *
1384
+	 * @param string  $payment_status                   possible value for 3.1's evens_attendee.payment_status
1385
+	 * @param boolean $this_thing_required_pre_approval whether the thing we're considering (the general setting's
1386
+	 *                                                  DEFAULT payment status, the event's DEFAULT payment status, or
1387
+	 *                                                  the attendee's payment status) required pre-approval.
1388
+	 * @return string STS_ID for use in 4.1
1389
+	 */
1390
+	public function convert_3_1_payment_status_to_4_1_STS_ID($payment_status, $this_thing_required_pre_approval = false)
1391
+	{
1392
+		//EE team can read the related discussion: https://app.asana.com/0/2400967562914/9418495544455
1393
+		if ($this_thing_required_pre_approval) {
1394
+			return 'RNA';
1395
+		} else {
1396
+			$mapping = $default_reg_stati_conversions = array(
1397
+					'Completed'        => 'RAP',
1398
+					''                 => 'RPP',
1399
+					'Incomplete'       => 'RPP',
1400
+					'Pending'          => 'RAP',
1401
+					//stati that only occurred on 3.1 attendees:
1402
+					'Payment Declined' => 'RPP',
1403
+					'Not Completed'    => 'RPP',
1404
+					'Cancelled'        => 'RPP',
1405
+					'Declined'         => 'RPP',
1406
+			);
1407
+		}
1408
+		return isset($mapping[$payment_status]) ? $mapping[$payment_status] : 'RNA';
1409
+	}
1410
+
1411
+
1412
+
1413
+	/**
1414
+	 * Makes sure the 3.1's image url is converted to an image attachment post to the 4.1 CPT event
1415
+	 * and sets it as the featured image on the CPT event
1416
+	 *
1417
+	 * @param type                            $old_event
1418
+	 * @param type                            $new_cpt_id
1419
+	 * @param  EE_Data_Migration_Script_Stage $migration_stage the stage which called this, where errors should be added
1420
+	 * @return boolean whether or not we had to do the big job of creating an image attachment
1421
+	 */
1422
+	public function convert_image_url_to_attachment_and_attach_to_post(
1423
+			$guid,
1424
+			$new_cpt_id,
1425
+			EE_Data_Migration_Script_Stage $migration_stage
1426
+	) {
1427
+		$created_attachment_post = false;
1428
+		$guid = $this->_get_original_guid($guid);
1429
+		if ($guid) {
1430
+			//check for an existing attachment post with this guid
1431
+			$attachment_post_id = $this->_get_image_attachment_id_by_GUID($guid);
1432
+			if ( ! $attachment_post_id) {
1433
+				//post thumbnail with that GUID doesn't exist, we should create one
1434
+				$attachment_post_id = $this->_create_image_attachment_from_GUID($guid, $migration_stage);
1435
+				$created_attachment_post = true;
1436
+			}
1437
+			//double-check we actually have an attachment post
1438
+			if ($attachment_post_id) {
1439
+				update_post_meta($new_cpt_id, '_thumbnail_id', $attachment_post_id);
1440
+			} else {
1441
+				$migration_stage->add_error(sprintf(esc_html__("Could not update event image %s for CPT with ID %d, but attachments post ID is %d",
1442
+						"event_espresso"), $guid, $new_cpt_id, $attachment_post_id));
1443
+			}
1444
+		}
1445
+		return $created_attachment_post;
1446
+	}
1447
+
1448
+
1449
+
1450
+	/**
1451
+	 * In 3.1, the event thumbnail image DOESN'T point to the orignal image, but instead
1452
+	 * to a large thumbnail (which has nearly the same GUID, except it adds "-{width}x{height}" before the filetype,
1453
+	 * or whatever dimensions it is. Eg 'http://mysite.com/image1-300x400.jpg' instead of
1454
+	 * 'http://mysite.com/image1.jpg' ). This function attempts to strip that off and get the original file, if it
1455
+	 * exists
1456
+	 *
1457
+	 * @param string $guid_in_old_event
1458
+	 * @return string either the original guid, or $guid_in_old_event if we couldn't figure out what the original was
1459
+	 */
1460
+	private function _get_original_guid($guid_in_old_event)
1461
+	{
1462
+		$original_guid = preg_replace('~-\d*x\d*\.~', '.', $guid_in_old_event, 1);
1463
+		//do a head request to verify the file exists
1464
+		$head_response = wp_remote_head($original_guid);
1465
+		if ( ! $head_response instanceof WP_Error && $head_response['response']['message'] == 'OK') {
1466
+			return $original_guid;
1467
+		} else {
1468
+			return $guid_in_old_event;
1469
+		}
1470
+	}
1471
+
1472
+
1473
+
1474
+	/**
1475
+	 * Creates an image attachment post for the GUID. If the GUID points to a remote image,
1476
+	 * we download it to our uploads directory so that it can be properly processed (eg, creates different sizes of
1477
+	 * thumbnails)
1478
+	 *
1479
+	 * @param type                           $guid
1480
+	 * @param EE_Data_Migration_Script_Stage $migration_stage
1481
+	 * @return int
1482
+	 */
1483
+	private function _create_image_attachment_from_GUID($guid, EE_Data_Migration_Script_Stage $migration_stage)
1484
+	{
1485
+		if ( ! $guid) {
1486
+			$migration_stage->add_error(sprintf(esc_html__("Cannot create image attachment for a blank GUID!",
1487
+					"event_espresso")));
1488
+			return 0;
1489
+		}
1490
+		$wp_filetype = wp_check_filetype(basename($guid), null);
1491
+		$wp_upload_dir = wp_upload_dir();
1492
+		//if the file is located remotely, download it to our uploads DIR, because wp_genereate_attachmnet_metadata needs the file to be local
1493
+		if (strpos($guid, $wp_upload_dir['url']) === false) {
1494
+			//image is located remotely. download it and place it in the uploads directory
1495
+			if ( ! is_readable($guid)) {
1496
+				$migration_stage->add_error(sprintf(esc_html__("Could not create image attachment from non-existent file: %s",
1497
+						"event_espresso"), $guid));
1498
+				return 0;
1499
+			}
1500
+			$contents = file_get_contents($guid);
1501
+			if ($contents === false) {
1502
+				$migration_stage->add_error(sprintf(esc_html__("Could not read image at %s, and therefore couldnt create an attachment post for it.",
1503
+						"event_espresso"), $guid));
1504
+				return false;
1505
+			}
1506
+			$local_filepath = $wp_upload_dir['path'] . DS . basename($guid);
1507
+			$savefile = fopen($local_filepath, 'w');
1508
+			fwrite($savefile, $contents);
1509
+			fclose($savefile);
1510
+			$guid = str_replace($wp_upload_dir['path'], $wp_upload_dir['url'], $local_filepath);
1511
+		} else {
1512
+			$local_filepath = str_replace($wp_upload_dir['url'], $wp_upload_dir['path'], $guid);
1513
+		}
1514
+		$attachment = array(
1515
+				'guid'           => $guid,
1516
+				'post_mime_type' => $wp_filetype['type'],
1517
+				'post_title'     => preg_replace('/\.[^.]+$/', '', basename($guid)),
1518
+				'post_content'   => '',
1519
+				'post_status'    => 'inherit',
1520
+		);
1521
+		$attach_id = wp_insert_attachment($attachment, $guid);
1522
+		if ( ! $attach_id) {
1523
+			$migration_stage->add_error(sprintf(esc_html__("Could not create image attachment post from image '%s'. Attachment data was %s.",
1524
+					"event_espresso"), $guid, $this->_json_encode($attachment)));
1525
+			return $attach_id;
1526
+		}
1527
+		// you must first include the image.php file
1528
+		// for the function wp_generate_attachment_metadata() to work
1529
+		require_once(ABSPATH . 'wp-admin/includes/image.php');
1530
+		$attach_data = wp_generate_attachment_metadata($attach_id, $local_filepath);
1531
+		if ( ! $attach_data) {
1532
+			$migration_stage->add_error(sprintf(esc_html__("Coudl not genereate attachment metadata for attachment post %d with filepath %s and GUID %s. Please check the file was downloaded properly.",
1533
+					"event_espresso"), $attach_id, $local_filepath, $guid));
1534
+			return $attach_id;
1535
+		}
1536
+		$metadata_save_result = wp_update_attachment_metadata($attach_id, $attach_data);
1537
+		if ( ! $metadata_save_result) {
1538
+			$migration_stage->add_error(sprintf(esc_html__("Could not update attachment metadata for attachment %d with data %s",
1539
+					"event_espresso"), $attach_id, $this->_json_encode($attach_data)));
1540
+		}
1541
+		return $attach_id;
1542
+	}
1543
+
1544
+
1545
+
1546
+	/**
1547
+	 * Finds the attachment post containing info about an image attachment given the GUID (link to the image itself),
1548
+	 * and returns its ID.
1549
+	 *
1550
+	 * @global type  $wpdb
1551
+	 * @param string $guid
1552
+	 * @return int
1553
+	 */
1554
+	private function _get_image_attachment_id_by_GUID($guid)
1555
+	{
1556
+		global $wpdb;
1557
+		$attachment_id = $wpdb->get_var($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE guid=%s LIMIT 1", $guid));
1558
+		return $attachment_id;
1559
+	}
1560
+
1561
+
1562
+
1563
+	/**
1564
+	 * Returns a mysql-formatted DATETIME in UTC time, given a $DATETIME_string
1565
+	 * (and optionally a timezone; if none is given, the wp DEFAULT is used)
1566
+	 *
1567
+	 * @param EE_Data_Migration_Script_base $stage
1568
+	 * @param array                         $row_of_data , the row from the DB (as an array) we're trying to find the
1569
+	 *                                                   UTC time for
1570
+	 * @param string                        $DATETIME_string
1571
+	 * @param string                        $timezone
1572
+	 * @return string
1573
+	 */
1574
+	public function convert_date_string_to_utc(
1575
+			EE_Data_Migration_Script_Stage $stage,
1576
+			$row_of_data,
1577
+			$DATETIME_string,
1578
+			$timezone = null
1579
+	) {
1580
+		$original_tz = $timezone;
1581
+		if ( ! $timezone) {
1582
+			$timezone = $this->_get_wp_timezone();
1583
+		}
1584
+		if ( ! $timezone) {
1585
+			$stage->add_error(sprintf(esc_html__("Could not find timezone given %s for %s", "event_espresso"), $original_tz,
1586
+					$row_of_data));
1587
+			$timezone = 'UTC';
1588
+		}
1589
+		try {
1590
+			$date_obj = new DateTime($DATETIME_string, new DateTimeZone($timezone));
1591
+			EEH_DTT_Helper::setTimezone($date_obj, new DateTimeZone('UTC'));
1592
+		} catch (Exception $e) {
1593
+			$stage->add_error(sprintf(esc_html__("Could not convert time string '%s' using timezone '%s' into a proper DATETIME. Using current time instead.",
1594
+					"event_espresso"), $DATETIME_string, $timezone));
1595
+			$date_obj = new DateTime();
1596
+		}
1597
+		return $date_obj->format('Y-m-d H:i:s');
1598
+	}
1599
+
1600
+
1601
+
1602
+	/**
1603
+	 * Gets the DEFAULT timezone string from wordpress (even if they set a gmt offset)
1604
+	 *
1605
+	 * @return string
1606
+	 */
1607
+	private function _get_wp_timezone()
1608
+	{
1609
+		$timezone = empty($timezone) ? get_option('timezone_string') : $timezone;
1610
+		//if timezone is STILL empty then let's get the GMT offset and then set the timezone_string using our converter
1611
+		if (empty($timezone)) {
1612
+			//let's get a the WordPress UTC offset
1613
+			$offset = get_option('gmt_offset');
1614
+			$timezone = $this->timezone_convert_to_string_from_offset($offset);
1615
+		}
1616
+		return $timezone;
1617
+	}
1618
+
1619
+
1620
+
1621
+	/**
1622
+	 * Gets the wordpress timezone string from a UTC offset
1623
+	 *
1624
+	 * @param int $offset
1625
+	 * @return boolean
1626
+	 */
1627
+	private function timezone_convert_to_string_from_offset($offset)
1628
+	{
1629
+		//shamelessly taken from bottom comment at http://ca1.php.net/manual/en/function.timezone-name-from-abbr.php because timezone_name_from_abbr() did NOT work as expected - its not reliable
1630
+		$offset *= 3600; // convert hour offset to seconds
1631
+		$abbrarray = timezone_abbreviations_list();
1632
+		foreach ($abbrarray as $abbr) {
1633
+			foreach ($abbr as $city) {
1634
+				if ($city['offset'] == $offset) {
1635
+					return $city['timezone_id'];
1636
+				}
1637
+			}
1638
+		}
1639
+		return false;
1640
+	}
1641
+
1642
+
1643
+
1644
+	public function migration_page_hooks()
1645
+	{
1646
+		add_filter(
1647
+				'FHEE__ee_migration_page__header',
1648
+				array($this, '_migrate_page_hook_simplify_version_strings'),
1649
+				10,
1650
+				3
1651
+		);
1652
+		add_filter(
1653
+				'FHEE__ee_migration_page__p_after_header',
1654
+				array($this, '_migration_page_hook_simplify_next_db_state'),
1655
+				10,
1656
+				2
1657
+		);
1658
+		add_filter(
1659
+				'FHEE__ee_migration_page__option_1_main',
1660
+				array($this, '_migrate_page_hook_simplify_version_strings'),
1661
+				10,
1662
+				3
1663
+		);
1664
+		add_filter(
1665
+				'FHEE__ee_migration_page__option_1_button_text',
1666
+				array($this, '_migrate_page_hook_simplify_version_strings'),
1667
+				10,
1668
+				3
1669
+		);
1670
+		add_action(
1671
+				'AHEE__ee_migration_page__option_1_extra_details',
1672
+				array($this, '_migration_page_hook_option_1_extra_details'),
1673
+				10,
1674
+				3
1675
+		);
1676
+		add_filter(
1677
+				'FHEE__ee_migration_page__option_2_main',
1678
+				array($this, '_migrate_page_hook_simplify_version_strings'),
1679
+				10,
1680
+				4
1681
+		);
1682
+		add_filter(
1683
+				'FHEE__ee_migration_page__option_2_button_text',
1684
+				array($this, '_migration_page_hook_simplify_next_db_state'),
1685
+				10,
1686
+				2
1687
+		);
1688
+		add_filter(
1689
+				'FHEE__ee_migration_page__option_2_details',
1690
+				array($this, '_migration_page_hook_simplify_next_db_state'),
1691
+				10,
1692
+				2
1693
+		);
1694
+		add_action(
1695
+				'AHEE__ee_migration_page__after_migration_options_table',
1696
+				array($this, '_migration_page_hook_after_migration_options_table')
1697
+		);
1698
+		add_filter(
1699
+				'FHEE__ee_migration_page__done_migration_header',
1700
+				array($this, '_migration_page_hook_simplify_next_db_state'),
1701
+				10,
1702
+				2
1703
+		);
1704
+		add_filter(
1705
+				'FHEE__ee_migration_page__p_after_done_migration_header',
1706
+				array($this, '_migration_page_hook_simplify_next_db_state'),
1707
+				10,
1708
+				2
1709
+		);
1710
+		add_filter(
1711
+				'FHEE__ee_migration_page__migration_options_template',
1712
+				array($this,'use_migration_options_from_ee3_template')
1713
+		);
1714
+	}
1715
+
1716
+
1717
+
1718
+	public function _migrate_page_hook_simplify_version_strings(
1719
+			$old_content,
1720
+			$current_db_state,
1721
+			$next_db_state,
1722
+			$ultimate_db_state = null
1723
+	) {
1724
+		return str_replace(array($current_db_state, $next_db_state, $ultimate_db_state),
1725
+				array(esc_html__('EE3', 'event_espresso'), esc_html__('EE4', 'event_espresso'), esc_html__("EE4", 'event_espresso')),
1726
+				$old_content);
1727
+	}
1728
+
1729
+
1730
+
1731
+	public function _migration_page_hook_simplify_next_db_state($old_content, $next_db_state)
1732
+	{
1733
+		return str_replace($next_db_state, esc_html__("EE4", 'event_espresso'), $old_content);
1734
+	}
1735
+
1736
+
1737
+
1738
+	public function _migration_page_hook_option_1_extra_details()
1739
+	{
1740
+		?>
1741 1741
         <p><?php printf(esc_html__("Note: many of your EE3 shortcodes will be changed to EE4 shortcodes during this migration (among many other things). Should you revert to EE3, then you should restore to your backup or manually change the EE4 shortcodes back to their EE3 equivalents",
1742
-            "event_espresso")); ?></p><?php
1743
-    }
1742
+			"event_espresso")); ?></p><?php
1743
+	}
1744 1744
 
1745 1745
 
1746 1746
 
1747
-    public function _migration_page_hook_after_migration_options_table()
1748
-    {
1749
-        ?><p class="ee-attention">
1747
+	public function _migration_page_hook_after_migration_options_table()
1748
+	{
1749
+		?><p class="ee-attention">
1750 1750
         <strong><span class="reminder-spn"><?php _e("Important note to those using Event Espresso 3 addons: ",
1751
-                        "event_espresso"); ?></span></strong>
1751
+						"event_espresso"); ?></span></strong>
1752 1752
         <br/><?php _e("Unless an addon's description on our website explicitly states that it is compatible with EE4, you should consider it incompatible and know that it WILL NOT WORK correctly with this new version of Event Espresso 4 (EE4). As well, any data for incompatible addons will NOT BE MIGRATED until an updated EE4 compatible version of the addon is available. If you want, or need to keep using your EE3 addons, you should simply continue using EE3 until EE4 compatible versions of your addons become available. To continue using EE3 for now, just deactivate EE4 and reactivate EE3.",
1753
-            "event_espresso"); ?>
1753
+			"event_espresso"); ?>
1754 1754
         </p><?php
1755
-    }
1755
+	}
1756 1756
 
1757 1757
 
1758 1758
 
1759
-    /**
1760
-     * When showing the migration options, show more options and info than normal (ie, give folks the option
1761
-     * to start using EE4 without migrating. From EE3 that's fine, because it doesn't actually remove any data, because
1762
-     * EE4 doesn't have any yet. But when migrating from EE4 it would remove old data, so its not a great idea).
1763
-     * @param $template_filepath
1764
-     * @return string
1765
-     */
1766
-    public function use_migration_options_from_ee3_template( $template_filepath ) {
1767
-        return EE_MAINTENANCE_TEMPLATE_PATH . 'migration_options_from_ee3.template.php';
1768
-    }
1759
+	/**
1760
+	 * When showing the migration options, show more options and info than normal (ie, give folks the option
1761
+	 * to start using EE4 without migrating. From EE3 that's fine, because it doesn't actually remove any data, because
1762
+	 * EE4 doesn't have any yet. But when migrating from EE4 it would remove old data, so its not a great idea).
1763
+	 * @param $template_filepath
1764
+	 * @return string
1765
+	 */
1766
+	public function use_migration_options_from_ee3_template( $template_filepath ) {
1767
+		return EE_MAINTENANCE_TEMPLATE_PATH . 'migration_options_from_ee3.template.php';
1768
+	}
1769 1769
 }
Please login to merge, or discard this patch.
core/helpers/EEH_DTT_Helper.helper.php 1 patch
Indentation   +1027 added lines, -1027 removed lines patch added patch discarded remove patch
@@ -21,1031 +21,1031 @@
 block discarded – undo
21 21
 {
22 22
 
23 23
 
24
-    /**
25
-     * return the timezone set for the WP install
26
-     *
27
-     * @return string valid timezone string for PHP DateTimeZone() class
28
-     * @throws InvalidArgumentException
29
-     * @throws InvalidDataTypeException
30
-     * @throws InvalidInterfaceException
31
-     */
32
-    public static function get_timezone()
33
-    {
34
-        return EEH_DTT_Helper::get_valid_timezone_string();
35
-    }
36
-
37
-
38
-    /**
39
-     * get_valid_timezone_string
40
-     *    ensures that a valid timezone string is returned
41
-     *
42
-     * @param string $timezone_string
43
-     * @return string
44
-     * @throws InvalidArgumentException
45
-     * @throws InvalidDataTypeException
46
-     * @throws InvalidInterfaceException
47
-     */
48
-    public static function get_valid_timezone_string($timezone_string = '')
49
-    {
50
-        return self::getHelperAdapter()->getValidTimezoneString($timezone_string);
51
-    }
52
-
53
-
54
-    /**
55
-     * This only purpose for this static method is to validate that the incoming timezone is a valid php timezone.
56
-     *
57
-     * @static
58
-     * @param  string $timezone_string Timezone string to check
59
-     * @param bool    $throw_error
60
-     * @return bool
61
-     * @throws InvalidArgumentException
62
-     * @throws InvalidDataTypeException
63
-     * @throws InvalidInterfaceException
64
-     */
65
-    public static function validate_timezone($timezone_string, $throw_error = true)
66
-    {
67
-        return self::getHelperAdapter()->validateTimezone($timezone_string, $throw_error);
68
-    }
69
-
70
-
71
-    /**
72
-     * This returns a string that can represent the provided gmt offset in format that can be passed into
73
-     * DateTimeZone.  This is NOT a string that can be passed as a value on the WordPress timezone_string option.
74
-     *
75
-     * @param float|string $gmt_offset
76
-     * @return string
77
-     * @throws InvalidArgumentException
78
-     * @throws InvalidDataTypeException
79
-     * @throws InvalidInterfaceException
80
-     */
81
-    public static function get_timezone_string_from_gmt_offset($gmt_offset = '')
82
-    {
83
-        return self::getHelperAdapter()->getTimezoneStringFromGmtOffset($gmt_offset);
84
-    }
85
-
86
-
87
-    /**
88
-     * Gets the site's GMT offset based on either the timezone string
89
-     * (in which case teh gmt offset will vary depending on the location's
90
-     * observance of daylight savings time) or the gmt_offset wp option
91
-     *
92
-     * @return int seconds offset
93
-     * @throws InvalidArgumentException
94
-     * @throws InvalidDataTypeException
95
-     * @throws InvalidInterfaceException
96
-     */
97
-    public static function get_site_timezone_gmt_offset()
98
-    {
99
-        return self::getHelperAdapter()->getSiteTimezoneGmtOffset();
100
-    }
101
-
102
-
103
-    /**
104
-     * Depending on PHP version,
105
-     * there might not be valid current timezone strings to match these gmt_offsets in its timezone tables.
106
-     * To get around that, for these fringe timezones we bump them to a known valid offset.
107
-     * This method should ONLY be called after first verifying an timezone_string cannot be retrieved for the offset.
108
-     *
109
-     * @deprecated 4.9.54.rc    Developers this was always meant to only be an internally used method.  This will be
110
-     *                          removed in a future version of EE.
111
-     * @param int $gmt_offset
112
-     * @return int
113
-     * @throws InvalidArgumentException
114
-     * @throws InvalidDataTypeException
115
-     * @throws InvalidInterfaceException
116
-     */
117
-    public static function adjust_invalid_gmt_offsets($gmt_offset = 0)
118
-    {
119
-        return self::getHelperAdapter()->adjustInvalidGmtOffsets($gmt_offset);
120
-    }
121
-
122
-
123
-    /**
124
-     * get_timezone_string_from_abbreviations_list
125
-     *
126
-     * @deprecated 4.9.54.rc  Developers, this was never intended to be public.  This is a soft deprecation for now.
127
-     *                        If you are using this, you'll want to work out an alternate way of getting the value.
128
-     * @param int  $gmt_offset
129
-     * @param bool $coerce If true, we attempt to coerce with our adjustment table @see self::adjust_invalid_gmt_offset.
130
-     * @return string
131
-     * @throws EE_Error
132
-     * @throws InvalidArgumentException
133
-     * @throws InvalidDataTypeException
134
-     * @throws InvalidInterfaceException
135
-     */
136
-    public static function get_timezone_string_from_abbreviations_list($gmt_offset = 0, $coerce = true)
137
-    {
138
-        $gmt_offset =  (int) $gmt_offset;
139
-        /** @var array[] $abbreviations */
140
-        $abbreviations = DateTimeZone::listAbbreviations();
141
-        foreach ($abbreviations as $abbreviation) {
142
-            foreach ($abbreviation as $timezone) {
143
-                if ((int) $timezone['offset'] === $gmt_offset && (bool) $timezone['dst'] === false) {
144
-                    try {
145
-                        $offset = self::get_timezone_offset(new DateTimeZone($timezone['timezone_id']));
146
-                        if ($offset !== $gmt_offset) {
147
-                            continue;
148
-                        }
149
-                        return $timezone['timezone_id'];
150
-                    } catch (Exception $e) {
151
-                        continue;
152
-                    }
153
-                }
154
-            }
155
-        }
156
-        //if $coerce is true, let's see if we can get a timezone string after the offset is adjusted
157
-        if ($coerce === true) {
158
-            $timezone_string = self::get_timezone_string_from_abbreviations_list(
159
-                self::adjust_invalid_gmt_offsets($gmt_offset),
160
-                false
161
-            );
162
-            if ($timezone_string) {
163
-                return $timezone_string;
164
-            }
165
-        }
166
-        throw new EE_Error(
167
-            sprintf(
168
-                esc_html__(
169
-                    'The provided GMT offset (%1$s), is invalid, please check with %2$sthis list%3$s for what valid timezones can be used',
170
-                    'event_espresso'
171
-                ),
172
-                $gmt_offset / HOUR_IN_SECONDS,
173
-                '<a href="http://www.php.net/manual/en/timezones.php">',
174
-                '</a>'
175
-            )
176
-        );
177
-    }
178
-
179
-
180
-    /**
181
-     * Get Timezone Transitions
182
-     *
183
-     * @param DateTimeZone $date_time_zone
184
-     * @param int|null     $time
185
-     * @param bool         $first_only
186
-     * @return array
187
-     * @throws InvalidArgumentException
188
-     * @throws InvalidDataTypeException
189
-     * @throws InvalidInterfaceException
190
-     */
191
-    public static function get_timezone_transitions(DateTimeZone $date_time_zone, $time = null, $first_only = true)
192
-    {
193
-        return self::getHelperAdapter()->getTimezoneTransitions($date_time_zone, $time, $first_only);
194
-    }
195
-
196
-
197
-    /**
198
-     * Get Timezone Offset for given timezone object.
199
-     *
200
-     * @param DateTimeZone $date_time_zone
201
-     * @param null         $time
202
-     * @return mixed
203
-     * @throws InvalidArgumentException
204
-     * @throws InvalidDataTypeException
205
-     * @throws InvalidInterfaceException
206
-     */
207
-    public static function get_timezone_offset(DateTimeZone $date_time_zone, $time = null)
208
-    {
209
-        return self::getHelperAdapter()->getTimezoneOffset($date_time_zone, $time);
210
-    }
211
-
212
-
213
-    /**
214
-     * Prints a select input for the given timezone string.
215
-     * @param string $timezone_string
216
-     * @deprecatd 4.9.54.rc   Soft deprecation.  Consider using \EEH_DTT_Helper::wp_timezone_choice instead.
217
-     * @throws InvalidArgumentException
218
-     * @throws InvalidDataTypeException
219
-     * @throws InvalidInterfaceException
220
-     */
221
-    public static function timezone_select_input($timezone_string = '')
222
-    {
223
-        self::getHelperAdapter()->timezoneSelectInput($timezone_string);
224
-    }
225
-
226
-
227
-    /**
228
-     * This method will take an incoming unix timestamp and add the offset to it for the given timezone_string.
229
-     * If no unix timestamp is given then time() is used.  If no timezone is given then the set timezone string for
230
-     * the site is used.
231
-     * This is used typically when using a Unix timestamp any core WP functions that expect their specially
232
-     * computed timestamp (i.e. date_i18n() )
233
-     *
234
-     * @param int    $unix_timestamp                  if 0, then time() will be used.
235
-     * @param string $timezone_string                 timezone_string. If empty, then the current set timezone for the
236
-     *                                                site will be used.
237
-     * @return int $unix_timestamp with the offset applied for the given timezone.
238
-     * @throws InvalidArgumentException
239
-     * @throws InvalidDataTypeException
240
-     * @throws InvalidInterfaceException
241
-     */
242
-    public static function get_timestamp_with_offset($unix_timestamp = 0, $timezone_string = '')
243
-    {
244
-        return self::getHelperAdapter()->getTimestampWithOffset($unix_timestamp, $timezone_string);
245
-    }
246
-
247
-
248
-    /**
249
-     *    _set_date_time_field
250
-     *    modifies EE_Base_Class EE_Datetime_Field objects
251
-     *
252
-     * @param  EE_Base_Class $obj                 EE_Base_Class object
253
-     * @param    DateTime    $DateTime            PHP DateTime object
254
-     * @param  string        $datetime_field_name the datetime fieldname to be manipulated
255
-     * @return EE_Base_Class
256
-     * @throws EE_Error
257
-     */
258
-    protected static function _set_date_time_field(EE_Base_Class $obj, DateTime $DateTime, $datetime_field_name)
259
-    {
260
-        // grab current datetime format
261
-        $current_format = $obj->get_format();
262
-        // set new full timestamp format
263
-        $obj->set_date_format(EE_Datetime_Field::mysql_date_format);
264
-        $obj->set_time_format(EE_Datetime_Field::mysql_time_format);
265
-        // set the new date value using a full timestamp format so that no data is lost
266
-        $obj->set($datetime_field_name, $DateTime->format(EE_Datetime_Field::mysql_timestamp_format));
267
-        // reset datetime formats
268
-        $obj->set_date_format($current_format[0]);
269
-        $obj->set_time_format($current_format[1]);
270
-        return $obj;
271
-    }
272
-
273
-
274
-    /**
275
-     *    date_time_add
276
-     *    helper for doing simple datetime calculations on a given datetime from EE_Base_Class
277
-     *    and modifying it IN the EE_Base_Class so you don't have to do anything else.
278
-     *
279
-     * @param  EE_Base_Class $obj                 EE_Base_Class object
280
-     * @param  string        $datetime_field_name name of the EE_Datetime_Filed datatype db column to be manipulated
281
-     * @param  string        $period              what you are adding. The options are (years, months, days, hours,
282
-     *                                            minutes, seconds) defaults to years
283
-     * @param  integer       $value               what you want to increment the time by
284
-     * @return EE_Base_Class return the EE_Base_Class object so right away you can do something with it
285
-     *                                            (chaining)
286
-     * @throws EE_Error
287
-     * @throws Exception
288
-     */
289
-    public static function date_time_add(EE_Base_Class $obj, $datetime_field_name, $period = 'years', $value = 1)
290
-    {
291
-        //get the raw UTC date.
292
-        $DateTime = $obj->get_DateTime_object($datetime_field_name);
293
-        $DateTime = EEH_DTT_Helper::calc_date($DateTime, $period, $value);
294
-        return EEH_DTT_Helper::_set_date_time_field($obj, $DateTime, $datetime_field_name);
295
-    }
296
-
297
-
298
-    /**
299
-     *    date_time_subtract
300
-     *    same as date_time_add except subtracting value instead of adding.
301
-     *
302
-     * @param EE_Base_Class $obj
303
-     * @param  string       $datetime_field_name name of the EE_Datetime_Filed datatype db column to be manipulated
304
-     * @param string        $period
305
-     * @param int           $value
306
-     * @return EE_Base_Class
307
-     * @throws EE_Error
308
-     * @throws Exception
309
-     */
310
-    public static function date_time_subtract(EE_Base_Class $obj, $datetime_field_name, $period = 'years', $value = 1)
311
-    {
312
-        //get the raw UTC date
313
-        $DateTime = $obj->get_DateTime_object($datetime_field_name);
314
-        $DateTime = EEH_DTT_Helper::calc_date($DateTime, $period, $value, '-');
315
-        return EEH_DTT_Helper::_set_date_time_field($obj, $DateTime, $datetime_field_name);
316
-    }
317
-
318
-
319
-    /**
320
-     * Simply takes an incoming DateTime object and does calculations on it based on the incoming parameters
321
-     *
322
-     * @param  DateTime   $DateTime DateTime object
323
-     * @param  string     $period   a value to indicate what interval is being used in the calculation. The options are
324
-     *                              'years', 'months', 'days', 'hours', 'minutes', 'seconds'. Defaults to years.
325
-     * @param  int|string $value    What you want to increment the date by
326
-     * @param  string     $operand  What operand you wish to use for the calculation
327
-     * @return DateTime return whatever type came in.
328
-     * @throws Exception
329
-     * @throws EE_Error
330
-     */
331
-    protected static function _modify_datetime_object(DateTime $DateTime, $period = 'years', $value = 1, $operand = '+')
332
-    {
333
-        if (! $DateTime instanceof DateTime) {
334
-            throw new EE_Error(
335
-                sprintf(
336
-                    esc_html__('Expected a PHP DateTime object, but instead received %1$s', 'event_espresso'),
337
-                    print_r($DateTime, true)
338
-                )
339
-            );
340
-        }
341
-        switch ($period) {
342
-            case 'years' :
343
-                $value = 'P' . $value . 'Y';
344
-                break;
345
-            case 'months' :
346
-                $value = 'P' . $value . 'M';
347
-                break;
348
-            case 'weeks' :
349
-                $value = 'P' . $value . 'W';
350
-                break;
351
-            case 'days' :
352
-                $value = 'P' . $value . 'D';
353
-                break;
354
-            case 'hours' :
355
-                $value = 'PT' . $value . 'H';
356
-                break;
357
-            case 'minutes' :
358
-                $value = 'PT' . $value . 'M';
359
-                break;
360
-            case 'seconds' :
361
-                $value = 'PT' . $value . 'S';
362
-                break;
363
-        }
364
-        switch ($operand) {
365
-            case '+':
366
-                $DateTime->add(new DateInterval($value));
367
-                break;
368
-            case '-':
369
-                $DateTime->sub(new DateInterval($value));
370
-                break;
371
-        }
372
-        return $DateTime;
373
-    }
374
-
375
-
376
-    /**
377
-     * Simply takes an incoming Unix timestamp and does calculations on it based on the incoming parameters
378
-     *
379
-     * @param  int     $timestamp Unix timestamp
380
-     * @param  string  $period    a value to indicate what interval is being used in the calculation. The options are
381
-     *                            'years', 'months', 'days', 'hours', 'minutes', 'seconds'. Defaults to years.
382
-     * @param  integer $value     What you want to increment the date by
383
-     * @param  string  $operand   What operand you wish to use for the calculation
384
-     * @return int
385
-     * @throws EE_Error
386
-     */
387
-    protected static function _modify_timestamp($timestamp, $period = 'years', $value = 1, $operand = '+')
388
-    {
389
-        if (! preg_match(EE_Datetime_Field::unix_timestamp_regex, $timestamp)) {
390
-            throw new EE_Error(
391
-                sprintf(
392
-                    esc_html__('Expected a Unix timestamp, but instead received %1$s', 'event_espresso'),
393
-                    print_r($timestamp, true)
394
-                )
395
-            );
396
-        }
397
-        switch ($period) {
398
-            case 'years' :
399
-                $value = YEAR_IN_SECONDS * $value;
400
-                break;
401
-            case 'months' :
402
-                $value = YEAR_IN_SECONDS / 12 * $value;
403
-                break;
404
-            case 'weeks' :
405
-                $value = WEEK_IN_SECONDS * $value;
406
-                break;
407
-            case 'days' :
408
-                $value = DAY_IN_SECONDS * $value;
409
-                break;
410
-            case 'hours' :
411
-                $value = HOUR_IN_SECONDS * $value;
412
-                break;
413
-            case 'minutes' :
414
-                $value = MINUTE_IN_SECONDS * $value;
415
-                break;
416
-        }
417
-        switch ($operand) {
418
-            case '+':
419
-                $timestamp += $value;
420
-                break;
421
-            case '-':
422
-                $timestamp -= $value;
423
-                break;
424
-        }
425
-        return $timestamp;
426
-    }
427
-
428
-
429
-    /**
430
-     * Simply takes an incoming UTC timestamp or DateTime object and does calculations on it based on the incoming
431
-     * parameters and returns the new timestamp or DateTime.
432
-     *
433
-     * @param  int | DateTime $DateTime_or_timestamp DateTime object or Unix timestamp
434
-     * @param  string         $period                a value to indicate what interval is being used in the
435
-     *                                               calculation. The options are 'years', 'months', 'days', 'hours',
436
-     *                                               'minutes', 'seconds'. Defaults to years.
437
-     * @param  integer        $value                 What you want to increment the date by
438
-     * @param  string         $operand               What operand you wish to use for the calculation
439
-     * @return mixed string|DateTime          return whatever type came in.
440
-     * @throws Exception
441
-     * @throws EE_Error
442
-     */
443
-    public static function calc_date($DateTime_or_timestamp, $period = 'years', $value = 1, $operand = '+')
444
-    {
445
-        if ($DateTime_or_timestamp instanceof DateTime) {
446
-            return EEH_DTT_Helper::_modify_datetime_object(
447
-                $DateTime_or_timestamp,
448
-                $period,
449
-                $value,
450
-                $operand
451
-            );
452
-        }
453
-        if (preg_match(EE_Datetime_Field::unix_timestamp_regex, $DateTime_or_timestamp)) {
454
-            return EEH_DTT_Helper::_modify_timestamp(
455
-                $DateTime_or_timestamp,
456
-                $period,
457
-                $value,
458
-                $operand
459
-            );
460
-        }
461
-        //error
462
-        return $DateTime_or_timestamp;
463
-    }
464
-
465
-
466
-    /**
467
-     * The purpose of this helper method is to receive an incoming format string in php date/time format
468
-     * and spit out the js and moment.js equivalent formats.
469
-     * Note, if no format string is given, then it is assumed the user wants what is set for WP.
470
-     * Note, js date and time formats are those used by the jquery-ui datepicker and the jquery-ui date-
471
-     * time picker.
472
-     *
473
-     * @see http://stackoverflow.com/posts/16725290/ for the code inspiration.
474
-     * @param string $date_format_string
475
-     * @param string $time_format_string
476
-     * @return array
477
-     *              array(
478
-     *              'js' => array (
479
-     *              'date' => //date format
480
-     *              'time' => //time format
481
-     *              ),
482
-     *              'moment' => //date and time format.
483
-     *              )
484
-     */
485
-    public static function convert_php_to_js_and_moment_date_formats(
486
-        $date_format_string = null,
487
-        $time_format_string = null
488
-    ) {
489
-        if ($date_format_string === null) {
490
-            $date_format_string = (string) get_option('date_format');
491
-        }
492
-        if ($time_format_string === null) {
493
-            $time_format_string = (string) get_option('time_format');
494
-        }
495
-        $date_format = self::_php_to_js_moment_converter($date_format_string);
496
-        $time_format = self::_php_to_js_moment_converter($time_format_string);
497
-        return array(
498
-            'js'     => array(
499
-                'date' => $date_format['js'],
500
-                'time' => $time_format['js'],
501
-            ),
502
-            'moment' => $date_format['moment'] . ' ' . $time_format['moment'],
503
-        );
504
-    }
505
-
506
-
507
-    /**
508
-     * This converts incoming format string into js and moment variations.
509
-     *
510
-     * @param string $format_string incoming php format string
511
-     * @return array js and moment formats.
512
-     */
513
-    protected static function _php_to_js_moment_converter($format_string)
514
-    {
515
-        /**
516
-         * This is a map of symbols for formats.
517
-         * The index is the php symbol, the equivalent values are in the array.
518
-         *
519
-         * @var array
520
-         */
521
-        $symbols_map          = array(
522
-            // Day
523
-            //01
524
-            'd' => array(
525
-                'js'     => 'dd',
526
-                'moment' => 'DD',
527
-            ),
528
-            //Mon
529
-            'D' => array(
530
-                'js'     => 'D',
531
-                'moment' => 'ddd',
532
-            ),
533
-            //1,2,...31
534
-            'j' => array(
535
-                'js'     => 'd',
536
-                'moment' => 'D',
537
-            ),
538
-            //Monday
539
-            'l' => array(
540
-                'js'     => 'DD',
541
-                'moment' => 'dddd',
542
-            ),
543
-            //ISO numeric representation of the day of the week (1-6)
544
-            'N' => array(
545
-                'js'     => '',
546
-                'moment' => 'E',
547
-            ),
548
-            //st,nd.rd
549
-            'S' => array(
550
-                'js'     => '',
551
-                'moment' => 'o',
552
-            ),
553
-            //numeric representation of day of week (0-6)
554
-            'w' => array(
555
-                'js'     => '',
556
-                'moment' => 'd',
557
-            ),
558
-            //day of year starting from 0 (0-365)
559
-            'z' => array(
560
-                'js'     => 'o',
561
-                'moment' => 'DDD' //note moment does not start with 0 so will need to modify by subtracting 1
562
-            ),
563
-            // Week
564
-            //ISO-8601 week number of year (weeks starting on monday)
565
-            'W' => array(
566
-                'js'     => '',
567
-                'moment' => 'w',
568
-            ),
569
-            // Month
570
-            // January...December
571
-            'F' => array(
572
-                'js'     => 'MM',
573
-                'moment' => 'MMMM',
574
-            ),
575
-            //01...12
576
-            'm' => array(
577
-                'js'     => 'mm',
578
-                'moment' => 'MM',
579
-            ),
580
-            //Jan...Dec
581
-            'M' => array(
582
-                'js'     => 'M',
583
-                'moment' => 'MMM',
584
-            ),
585
-            //1-12
586
-            'n' => array(
587
-                'js'     => 'm',
588
-                'moment' => 'M',
589
-            ),
590
-            //number of days in given month
591
-            't' => array(
592
-                'js'     => '',
593
-                'moment' => '',
594
-            ),
595
-            // Year
596
-            //whether leap year or not 1/0
597
-            'L' => array(
598
-                'js'     => '',
599
-                'moment' => '',
600
-            ),
601
-            //ISO-8601 year number
602
-            'o' => array(
603
-                'js'     => '',
604
-                'moment' => 'GGGG',
605
-            ),
606
-            //1999...2003
607
-            'Y' => array(
608
-                'js'     => 'yy',
609
-                'moment' => 'YYYY',
610
-            ),
611
-            //99...03
612
-            'y' => array(
613
-                'js'     => 'y',
614
-                'moment' => 'YY',
615
-            ),
616
-            // Time
617
-            // am/pm
618
-            'a' => array(
619
-                'js'     => 'tt',
620
-                'moment' => 'a',
621
-            ),
622
-            // AM/PM
623
-            'A' => array(
624
-                'js'     => 'TT',
625
-                'moment' => 'A',
626
-            ),
627
-            // Swatch Internet Time?!?
628
-            'B' => array(
629
-                'js'     => '',
630
-                'moment' => '',
631
-            ),
632
-            //1...12
633
-            'g' => array(
634
-                'js'     => 'h',
635
-                'moment' => 'h',
636
-            ),
637
-            //0...23
638
-            'G' => array(
639
-                'js'     => 'H',
640
-                'moment' => 'H',
641
-            ),
642
-            //01...12
643
-            'h' => array(
644
-                'js'     => 'hh',
645
-                'moment' => 'hh',
646
-            ),
647
-            //00...23
648
-            'H' => array(
649
-                'js'     => 'HH',
650
-                'moment' => 'HH',
651
-            ),
652
-            //00..59
653
-            'i' => array(
654
-                'js'     => 'mm',
655
-                'moment' => 'mm',
656
-            ),
657
-            //seconds... 00...59
658
-            's' => array(
659
-                'js'     => 'ss',
660
-                'moment' => 'ss',
661
-            ),
662
-            //microseconds
663
-            'u' => array(
664
-                'js'     => '',
665
-                'moment' => '',
666
-            ),
667
-        );
668
-        $jquery_ui_format     = '';
669
-        $moment_format        = '';
670
-        $escaping             = false;
671
-        $format_string_length = strlen($format_string);
672
-        for ($i = 0; $i < $format_string_length; $i++) {
673
-            $char = $format_string[ $i ];
674
-            if ($char === '\\') { // PHP date format escaping character
675
-                $i++;
676
-                if ($escaping) {
677
-                    $jquery_ui_format .= $format_string[ $i ];
678
-                    $moment_format    .= $format_string[ $i ];
679
-                } else {
680
-                    $jquery_ui_format .= '\'' . $format_string[ $i ];
681
-                    $moment_format    .= $format_string[ $i ];
682
-                }
683
-                $escaping = true;
684
-            } else {
685
-                if ($escaping) {
686
-                    $jquery_ui_format .= "'";
687
-                    $moment_format    .= "'";
688
-                    $escaping         = false;
689
-                }
690
-                if (isset($symbols_map[ $char ])) {
691
-                    $jquery_ui_format .= $symbols_map[ $char ]['js'];
692
-                    $moment_format    .= $symbols_map[ $char ]['moment'];
693
-                } else {
694
-                    $jquery_ui_format .= $char;
695
-                    $moment_format    .= $char;
696
-                }
697
-            }
698
-        }
699
-        return array('js' => $jquery_ui_format, 'moment' => $moment_format);
700
-    }
701
-
702
-
703
-    /**
704
-     * This takes an incoming format string and validates it to ensure it will work fine with PHP.
705
-     *
706
-     * @param string $format_string   Incoming format string for php date().
707
-     * @return mixed bool|array  If all is okay then TRUE is returned.  Otherwise an array of validation
708
-     *                                errors is returned.  So for client code calling, check for is_array() to
709
-     *                                indicate failed validations.
710
-     */
711
-    public static function validate_format_string($format_string)
712
-    {
713
-        $error_msg = array();
714
-        //time format checks
715
-        switch (true) {
716
-            case   strpos($format_string, 'h') !== false  :
717
-            case   strpos($format_string, 'g') !== false :
718
-                /**
719
-                 * if the time string has a lowercase 'h' which == 12 hour time format and there
720
-                 * is not any ante meridiem format ('a' or 'A').  Then throw an error because its
721
-                 * too ambiguous and PHP won't be able to figure out whether 1 = 1pm or 1am.
722
-                 */
723
-                if (stripos($format_string, 'A') === false) {
724
-                    $error_msg[] = esc_html__(
725
-                        'There is a  time format for 12 hour time but no  "a" or "A" to indicate am/pm.  Without this distinction, PHP is unable to determine if a "1" for the hour value equals "1pm" or "1am".',
726
-                        'event_espresso'
727
-                    );
728
-                }
729
-                break;
730
-        }
731
-        return empty($error_msg) ? true : $error_msg;
732
-    }
733
-
734
-
735
-    /**
736
-     *     If the the first date starts at midnight on one day, and the next date ends at midnight on the
737
-     *     very next day then this method will return true.
738
-     *    If $date_1 = 2015-12-15 00:00:00 and $date_2 = 2015-12-16 00:00:00 then this function will return true.
739
-     *    If $date_1 = 2015-12-15 03:00:00 and $date_2 = 2015-12_16 03:00:00 then this function will return false.
740
-     *    If $date_1 = 2015-12-15 00:00:00 and $date_2 = 2015-12-15 00:00:00 then this function will return true.
741
-     *
742
-     * @param mixed $date_1
743
-     * @param mixed $date_2
744
-     * @return bool
745
-     */
746
-    public static function dates_represent_one_24_hour_date($date_1, $date_2)
747
-    {
748
-
749
-        if (
750
-            (! $date_1 instanceof DateTime || ! $date_2 instanceof DateTime)
751
-            || ($date_1->format(EE_Datetime_Field::mysql_time_format) !== '00:00:00'
752
-                || $date_2->format(
753
-                    EE_Datetime_Field::mysql_time_format
754
-                ) !== '00:00:00')
755
-        ) {
756
-            return false;
757
-        }
758
-        return $date_2->format('U') - $date_1->format('U') === 86400;
759
-    }
760
-
761
-
762
-    /**
763
-     * This returns the appropriate query interval string that can be used in sql queries involving mysql Date
764
-     * Functions.
765
-     *
766
-     * @param string $timezone_string    A timezone string in a valid format to instantiate a DateTimeZone object.
767
-     * @param string $field_for_interval The Database field that is the interval is applied to in the query.
768
-     * @return string
769
-     */
770
-    public static function get_sql_query_interval_for_offset($timezone_string, $field_for_interval)
771
-    {
772
-        try {
773
-            /** need to account for timezone offset on the selects */
774
-            $DateTimeZone = new DateTimeZone($timezone_string);
775
-        } catch (Exception $e) {
776
-            $DateTimeZone = null;
777
-        }
778
-        /**
779
-         * Note get_option( 'gmt_offset') returns a value in hours, whereas DateTimeZone::getOffset returns values in seconds.
780
-         * Hence we do the calc for DateTimeZone::getOffset.
781
-         */
782
-        $offset         = $DateTimeZone instanceof DateTimeZone
783
-            ? $DateTimeZone->getOffset(new DateTime('now')) / HOUR_IN_SECONDS
784
-            : (float) get_option('gmt_offset');
785
-        $query_interval = $offset < 0
786
-            ? 'DATE_SUB(' . $field_for_interval . ', INTERVAL ' . $offset * -1 . ' HOUR)'
787
-            : 'DATE_ADD(' . $field_for_interval . ', INTERVAL ' . $offset . ' HOUR)';
788
-        return $query_interval;
789
-    }
790
-
791
-
792
-    /**
793
-     * Retrieves the site's default timezone and returns it formatted so it's ready for display
794
-     * to users. If you want to customize how its displayed feel free to fetch the 'timezone_string'
795
-     * and 'gmt_offset' WordPress options directly; or use the filter
796
-     * FHEE__EEH_DTT_Helper__get_timezone_string_for_display
797
-     * (although note that we remove any HTML that may be added)
798
-     *
799
-     * @return string
800
-     */
801
-    public static function get_timezone_string_for_display()
802
-    {
803
-        $pretty_timezone = apply_filters('FHEE__EEH_DTT_Helper__get_timezone_string_for_display', '');
804
-        if (! empty($pretty_timezone)) {
805
-            return esc_html($pretty_timezone);
806
-        }
807
-        $timezone_string = get_option('timezone_string');
808
-        if ($timezone_string) {
809
-            static $mo_loaded = false;
810
-            // Load translations for continents and cities just like wp_timezone_choice does
811
-            if (! $mo_loaded) {
812
-                $locale = get_locale();
813
-                $mofile = WP_LANG_DIR . '/continents-cities-' . $locale . '.mo';
814
-                load_textdomain('continents-cities', $mofile);
815
-                $mo_loaded = true;
816
-            }
817
-            //well that was easy.
818
-            $parts = explode('/', $timezone_string);
819
-            //remove the continent
820
-            unset($parts[0]);
821
-            $t_parts = array();
822
-            foreach ($parts as $part) {
823
-                $t_parts[] = translate(str_replace('_', ' ', $part), 'continents-cities');
824
-            }
825
-            return implode(' - ', $t_parts);
826
-        }
827
-        //they haven't set the timezone string, so let's return a string like "UTC+1"
828
-        $gmt_offset = get_option('gmt_offset');
829
-        $prefix     = (int) $gmt_offset >= 0 ? '+' : '';
830
-        $parts      = explode('.', (string) $gmt_offset);
831
-        if (count($parts) === 1) {
832
-            $parts[1] = '00';
833
-        } else {
834
-            //convert the part after the decimal, eg "5" (from x.5) or "25" (from x.25)
835
-            //to minutes, eg 30 or 15, respectively
836
-            $hour_fraction = (float) ('0.' . $parts[1]);
837
-            $parts[1]      = (string) $hour_fraction * 60;
838
-        }
839
-        return sprintf(__('UTC%1$s', 'event_espresso'), $prefix . implode(':', $parts));
840
-    }
841
-
842
-
843
-
844
-    /**
845
-     * So PHP does this awesome thing where if you are trying to get a timestamp
846
-     * for a month using a string like "February" or "February 2017",
847
-     * and you don't specify a day as part of your string,
848
-     * then PHP will use whatever the current day of the month is.
849
-     * IF the current day of the month happens to be the 30th or 31st,
850
-     * then PHP gets really confused by a date like February 30,
851
-     * so instead of saying
852
-     *      "Hey February only has 28 days (this year)...
853
-     *      ...you must have meant the last day of the month!"
854
-     * PHP does the next most logical thing, and bumps the date up to March 2nd,
855
-     * because someone requesting February 30th obviously meant March 1st!
856
-     * The way around this is to always set the day to the first,
857
-     * so that the month will stay on the month you wanted.
858
-     * this method will add that "1" into your date regardless of the format.
859
-     *
860
-     * @param string $month
861
-     * @return string
862
-     */
863
-    public static function first_of_month_timestamp($month = '')
864
-    {
865
-        $month = (string) $month;
866
-        $year  = '';
867
-        // check if the incoming string has a year in it or not
868
-        if (preg_match('/\b\d{4}\b/', $month, $matches)) {
869
-            $year = $matches[0];
870
-            // ten remove that from the month string as well as any spaces
871
-            $month = trim(str_replace($year, '', $month));
872
-            // add a space before the year
873
-            $year = " {$year}";
874
-        }
875
-        // return timestamp for something like "February 1 2017"
876
-        return strtotime("{$month} 1{$year}");
877
-    }
878
-
879
-
880
-    /**
881
-     * This simply returns the timestamp for tomorrow (midnight next day) in this sites timezone.  So it may be midnight
882
-     * for this sites timezone, but the timestamp could be some other time GMT.
883
-     */
884
-    public static function tomorrow()
885
-    {
886
-        //The multiplication of -1 ensures that we switch positive offsets to negative and negative offsets to positive
887
-        //before adding to the timestamp.  Why? Because we want tomorrow to be for midnight the next day in THIS timezone
888
-        //not an offset from midnight in UTC.  So if we're starting with UTC 00:00:00, then we want to make sure the
889
-        //final timestamp is equivalent to midnight in this timezone as represented in GMT.
890
-        return strtotime('tomorrow') + (self::get_site_timezone_gmt_offset() * -1);
891
-    }
892
-
893
-
894
-    /**
895
-     * **
896
-     * Gives a nicely-formatted list of timezone strings.
897
-     * Copied from the core wp function by the same name so we could customize to remove UTC offsets.
898
-     *
899
-     * @since     4.9.40.rc.008
900
-     * @staticvar bool $mo_loaded
901
-     * @staticvar string $locale_loaded
902
-     * @param string $selected_zone Selected timezone.
903
-     * @param string $locale        Optional. Locale to load the timezones in. Default current site locale.
904
-     * @return string
905
-     */
906
-    public static function wp_timezone_choice($selected_zone, $locale = null)
907
-    {
908
-        static $mo_loaded = false, $locale_loaded = null;
909
-        $continents = array(
910
-            'Africa',
911
-            'America',
912
-            'Antarctica',
913
-            'Arctic',
914
-            'Asia',
915
-            'Atlantic',
916
-            'Australia',
917
-            'Europe',
918
-            'Indian',
919
-            'Pacific',
920
-        );
921
-        // Load translations for continents and cities.
922
-        if (! $mo_loaded || $locale !== $locale_loaded) {
923
-            $locale_loaded = $locale ? $locale : get_locale();
924
-            $mofile        = WP_LANG_DIR . '/continents-cities-' . $locale_loaded . '.mo';
925
-            unload_textdomain('continents-cities');
926
-            load_textdomain('continents-cities', $mofile);
927
-            $mo_loaded = true;
928
-        }
929
-        $zone_data = array();
930
-        foreach (timezone_identifiers_list() as $zone) {
931
-            $zone = explode('/', $zone);
932
-            if (! in_array($zone[0], $continents, true)) {
933
-                continue;
934
-            }
935
-            // This determines what gets set and translated - we don't translate Etc/* strings here, they are done later
936
-            $exists      = array(
937
-                0 => isset($zone[0]) && $zone[0],
938
-                1 => isset($zone[1]) && $zone[1],
939
-                2 => isset($zone[2]) && $zone[2],
940
-            );
941
-            $exists[3]   = $exists[0] && $zone[0] !== 'Etc';
942
-            $exists[4]   = $exists[1] && $exists[3];
943
-            $exists[5]   = $exists[2] && $exists[3];
944
-            $zone_data[] = array(
945
-                'continent'   => $exists[0] ? $zone[0] : '',
946
-                'city'        => $exists[1] ? $zone[1] : '',
947
-                'subcity'     => $exists[2] ? $zone[2] : '',
948
-                't_continent' => $exists[3]
949
-                    ? translate(str_replace('_', ' ', $zone[0]), 'continents-cities')
950
-                    : '',
951
-                't_city'      => $exists[4]
952
-                    ? translate(str_replace('_', ' ', $zone[1]), 'continents-cities')
953
-                    : '',
954
-                't_subcity'   => $exists[5]
955
-                    ? translate(str_replace('_', ' ', $zone[2]), 'continents-cities')
956
-                    : '',
957
-            );
958
-        }
959
-        usort($zone_data, '_wp_timezone_choice_usort_callback');
960
-        $structure = array();
961
-        if (empty($selected_zone)) {
962
-            $structure[] = '<option selected="selected" value="">' . __('Select a city') . '</option>';
963
-        }
964
-        foreach ($zone_data as $key => $zone) {
965
-            // Build value in an array to join later
966
-            $value = array($zone['continent']);
967
-            if (empty($zone['city'])) {
968
-                // It's at the continent level (generally won't happen)
969
-                $display = $zone['t_continent'];
970
-            } else {
971
-                // It's inside a continent group
972
-                // Continent optgroup
973
-                if (! isset($zone_data[ $key - 1 ]) || $zone_data[ $key - 1 ]['continent'] !== $zone['continent']) {
974
-                    $label       = $zone['t_continent'];
975
-                    $structure[] = '<optgroup label="' . esc_attr($label) . '">';
976
-                }
977
-                // Add the city to the value
978
-                $value[] = $zone['city'];
979
-                $display = $zone['t_city'];
980
-                if (! empty($zone['subcity'])) {
981
-                    // Add the subcity to the value
982
-                    $value[] = $zone['subcity'];
983
-                    $display .= ' - ' . $zone['t_subcity'];
984
-                }
985
-            }
986
-            // Build the value
987
-            $value       = implode('/', $value);
988
-            $selected    = $value === $selected_zone ? ' selected="selected"' : '';
989
-            $structure[] = '<option value="' . esc_attr($value) . '"' . $selected . '>'
990
-                           . esc_html($display)
991
-                           . '</option>';
992
-            // Close continent optgroup
993
-            if (! empty($zone['city'])
994
-                && (
995
-                    ! isset($zone_data[ $key + 1 ])
996
-                    || (isset($zone_data[ $key + 1 ]) && $zone_data[ $key + 1 ]['continent'] !== $zone['continent'])
997
-                )
998
-            ) {
999
-                $structure[] = '</optgroup>';
1000
-            }
1001
-        }
1002
-        return implode("\n", $structure);
1003
-    }
1004
-
1005
-
1006
-    /**
1007
-     * Shim for the WP function `get_user_locale` that was added in WordPress 4.7.0
1008
-     *
1009
-     * @param int|WP_User $user_id
1010
-     * @return string
1011
-     */
1012
-    public static function get_user_locale($user_id = 0)
1013
-    {
1014
-        if (function_exists('get_user_locale')) {
1015
-            return get_user_locale($user_id);
1016
-        }
1017
-        return get_locale();
1018
-    }
1019
-
1020
-
1021
-    /**
1022
-     * Return the appropriate helper adapter for DTT related things.
1023
-     *
1024
-     * @return HelperInterface
1025
-     * @throws InvalidArgumentException
1026
-     * @throws InvalidDataTypeException
1027
-     * @throws InvalidInterfaceException
1028
-     */
1029
-    private static function getHelperAdapter() {
1030
-        $dtt_helper_fqcn = PHP_VERSION_ID < 50600
1031
-            ? 'EventEspresso\core\services\helpers\datetime\PhpCompatLessFiveSixHelper'
1032
-            : 'EventEspresso\core\services\helpers\datetime\PhpCompatGreaterFiveSixHelper';
1033
-        return LoaderFactory::getLoader()->getShared($dtt_helper_fqcn);
1034
-    }
1035
-
1036
-
1037
-    /**
1038
-     * Helper function for setting the timezone on a DateTime object.
1039
-     * This is implemented to standardize a workaround for a PHP bug outlined in
1040
-     * https://events.codebasehq.com/projects/event-espresso/tickets/11407 and
1041
-     * https://events.codebasehq.com/projects/event-espresso/tickets/11233
1042
-     *
1043
-     * @param DateTime     $datetime
1044
-     * @param DateTimeZone $timezone
1045
-     */
1046
-    public static function setTimezone(DateTime $datetime, DateTimeZone $timezone)
1047
-    {
1048
-        $datetime->setTimezone($timezone);
1049
-        $datetime->getTimestamp();
1050
-    }
24
+	/**
25
+	 * return the timezone set for the WP install
26
+	 *
27
+	 * @return string valid timezone string for PHP DateTimeZone() class
28
+	 * @throws InvalidArgumentException
29
+	 * @throws InvalidDataTypeException
30
+	 * @throws InvalidInterfaceException
31
+	 */
32
+	public static function get_timezone()
33
+	{
34
+		return EEH_DTT_Helper::get_valid_timezone_string();
35
+	}
36
+
37
+
38
+	/**
39
+	 * get_valid_timezone_string
40
+	 *    ensures that a valid timezone string is returned
41
+	 *
42
+	 * @param string $timezone_string
43
+	 * @return string
44
+	 * @throws InvalidArgumentException
45
+	 * @throws InvalidDataTypeException
46
+	 * @throws InvalidInterfaceException
47
+	 */
48
+	public static function get_valid_timezone_string($timezone_string = '')
49
+	{
50
+		return self::getHelperAdapter()->getValidTimezoneString($timezone_string);
51
+	}
52
+
53
+
54
+	/**
55
+	 * This only purpose for this static method is to validate that the incoming timezone is a valid php timezone.
56
+	 *
57
+	 * @static
58
+	 * @param  string $timezone_string Timezone string to check
59
+	 * @param bool    $throw_error
60
+	 * @return bool
61
+	 * @throws InvalidArgumentException
62
+	 * @throws InvalidDataTypeException
63
+	 * @throws InvalidInterfaceException
64
+	 */
65
+	public static function validate_timezone($timezone_string, $throw_error = true)
66
+	{
67
+		return self::getHelperAdapter()->validateTimezone($timezone_string, $throw_error);
68
+	}
69
+
70
+
71
+	/**
72
+	 * This returns a string that can represent the provided gmt offset in format that can be passed into
73
+	 * DateTimeZone.  This is NOT a string that can be passed as a value on the WordPress timezone_string option.
74
+	 *
75
+	 * @param float|string $gmt_offset
76
+	 * @return string
77
+	 * @throws InvalidArgumentException
78
+	 * @throws InvalidDataTypeException
79
+	 * @throws InvalidInterfaceException
80
+	 */
81
+	public static function get_timezone_string_from_gmt_offset($gmt_offset = '')
82
+	{
83
+		return self::getHelperAdapter()->getTimezoneStringFromGmtOffset($gmt_offset);
84
+	}
85
+
86
+
87
+	/**
88
+	 * Gets the site's GMT offset based on either the timezone string
89
+	 * (in which case teh gmt offset will vary depending on the location's
90
+	 * observance of daylight savings time) or the gmt_offset wp option
91
+	 *
92
+	 * @return int seconds offset
93
+	 * @throws InvalidArgumentException
94
+	 * @throws InvalidDataTypeException
95
+	 * @throws InvalidInterfaceException
96
+	 */
97
+	public static function get_site_timezone_gmt_offset()
98
+	{
99
+		return self::getHelperAdapter()->getSiteTimezoneGmtOffset();
100
+	}
101
+
102
+
103
+	/**
104
+	 * Depending on PHP version,
105
+	 * there might not be valid current timezone strings to match these gmt_offsets in its timezone tables.
106
+	 * To get around that, for these fringe timezones we bump them to a known valid offset.
107
+	 * This method should ONLY be called after first verifying an timezone_string cannot be retrieved for the offset.
108
+	 *
109
+	 * @deprecated 4.9.54.rc    Developers this was always meant to only be an internally used method.  This will be
110
+	 *                          removed in a future version of EE.
111
+	 * @param int $gmt_offset
112
+	 * @return int
113
+	 * @throws InvalidArgumentException
114
+	 * @throws InvalidDataTypeException
115
+	 * @throws InvalidInterfaceException
116
+	 */
117
+	public static function adjust_invalid_gmt_offsets($gmt_offset = 0)
118
+	{
119
+		return self::getHelperAdapter()->adjustInvalidGmtOffsets($gmt_offset);
120
+	}
121
+
122
+
123
+	/**
124
+	 * get_timezone_string_from_abbreviations_list
125
+	 *
126
+	 * @deprecated 4.9.54.rc  Developers, this was never intended to be public.  This is a soft deprecation for now.
127
+	 *                        If you are using this, you'll want to work out an alternate way of getting the value.
128
+	 * @param int  $gmt_offset
129
+	 * @param bool $coerce If true, we attempt to coerce with our adjustment table @see self::adjust_invalid_gmt_offset.
130
+	 * @return string
131
+	 * @throws EE_Error
132
+	 * @throws InvalidArgumentException
133
+	 * @throws InvalidDataTypeException
134
+	 * @throws InvalidInterfaceException
135
+	 */
136
+	public static function get_timezone_string_from_abbreviations_list($gmt_offset = 0, $coerce = true)
137
+	{
138
+		$gmt_offset =  (int) $gmt_offset;
139
+		/** @var array[] $abbreviations */
140
+		$abbreviations = DateTimeZone::listAbbreviations();
141
+		foreach ($abbreviations as $abbreviation) {
142
+			foreach ($abbreviation as $timezone) {
143
+				if ((int) $timezone['offset'] === $gmt_offset && (bool) $timezone['dst'] === false) {
144
+					try {
145
+						$offset = self::get_timezone_offset(new DateTimeZone($timezone['timezone_id']));
146
+						if ($offset !== $gmt_offset) {
147
+							continue;
148
+						}
149
+						return $timezone['timezone_id'];
150
+					} catch (Exception $e) {
151
+						continue;
152
+					}
153
+				}
154
+			}
155
+		}
156
+		//if $coerce is true, let's see if we can get a timezone string after the offset is adjusted
157
+		if ($coerce === true) {
158
+			$timezone_string = self::get_timezone_string_from_abbreviations_list(
159
+				self::adjust_invalid_gmt_offsets($gmt_offset),
160
+				false
161
+			);
162
+			if ($timezone_string) {
163
+				return $timezone_string;
164
+			}
165
+		}
166
+		throw new EE_Error(
167
+			sprintf(
168
+				esc_html__(
169
+					'The provided GMT offset (%1$s), is invalid, please check with %2$sthis list%3$s for what valid timezones can be used',
170
+					'event_espresso'
171
+				),
172
+				$gmt_offset / HOUR_IN_SECONDS,
173
+				'<a href="http://www.php.net/manual/en/timezones.php">',
174
+				'</a>'
175
+			)
176
+		);
177
+	}
178
+
179
+
180
+	/**
181
+	 * Get Timezone Transitions
182
+	 *
183
+	 * @param DateTimeZone $date_time_zone
184
+	 * @param int|null     $time
185
+	 * @param bool         $first_only
186
+	 * @return array
187
+	 * @throws InvalidArgumentException
188
+	 * @throws InvalidDataTypeException
189
+	 * @throws InvalidInterfaceException
190
+	 */
191
+	public static function get_timezone_transitions(DateTimeZone $date_time_zone, $time = null, $first_only = true)
192
+	{
193
+		return self::getHelperAdapter()->getTimezoneTransitions($date_time_zone, $time, $first_only);
194
+	}
195
+
196
+
197
+	/**
198
+	 * Get Timezone Offset for given timezone object.
199
+	 *
200
+	 * @param DateTimeZone $date_time_zone
201
+	 * @param null         $time
202
+	 * @return mixed
203
+	 * @throws InvalidArgumentException
204
+	 * @throws InvalidDataTypeException
205
+	 * @throws InvalidInterfaceException
206
+	 */
207
+	public static function get_timezone_offset(DateTimeZone $date_time_zone, $time = null)
208
+	{
209
+		return self::getHelperAdapter()->getTimezoneOffset($date_time_zone, $time);
210
+	}
211
+
212
+
213
+	/**
214
+	 * Prints a select input for the given timezone string.
215
+	 * @param string $timezone_string
216
+	 * @deprecatd 4.9.54.rc   Soft deprecation.  Consider using \EEH_DTT_Helper::wp_timezone_choice instead.
217
+	 * @throws InvalidArgumentException
218
+	 * @throws InvalidDataTypeException
219
+	 * @throws InvalidInterfaceException
220
+	 */
221
+	public static function timezone_select_input($timezone_string = '')
222
+	{
223
+		self::getHelperAdapter()->timezoneSelectInput($timezone_string);
224
+	}
225
+
226
+
227
+	/**
228
+	 * This method will take an incoming unix timestamp and add the offset to it for the given timezone_string.
229
+	 * If no unix timestamp is given then time() is used.  If no timezone is given then the set timezone string for
230
+	 * the site is used.
231
+	 * This is used typically when using a Unix timestamp any core WP functions that expect their specially
232
+	 * computed timestamp (i.e. date_i18n() )
233
+	 *
234
+	 * @param int    $unix_timestamp                  if 0, then time() will be used.
235
+	 * @param string $timezone_string                 timezone_string. If empty, then the current set timezone for the
236
+	 *                                                site will be used.
237
+	 * @return int $unix_timestamp with the offset applied for the given timezone.
238
+	 * @throws InvalidArgumentException
239
+	 * @throws InvalidDataTypeException
240
+	 * @throws InvalidInterfaceException
241
+	 */
242
+	public static function get_timestamp_with_offset($unix_timestamp = 0, $timezone_string = '')
243
+	{
244
+		return self::getHelperAdapter()->getTimestampWithOffset($unix_timestamp, $timezone_string);
245
+	}
246
+
247
+
248
+	/**
249
+	 *    _set_date_time_field
250
+	 *    modifies EE_Base_Class EE_Datetime_Field objects
251
+	 *
252
+	 * @param  EE_Base_Class $obj                 EE_Base_Class object
253
+	 * @param    DateTime    $DateTime            PHP DateTime object
254
+	 * @param  string        $datetime_field_name the datetime fieldname to be manipulated
255
+	 * @return EE_Base_Class
256
+	 * @throws EE_Error
257
+	 */
258
+	protected static function _set_date_time_field(EE_Base_Class $obj, DateTime $DateTime, $datetime_field_name)
259
+	{
260
+		// grab current datetime format
261
+		$current_format = $obj->get_format();
262
+		// set new full timestamp format
263
+		$obj->set_date_format(EE_Datetime_Field::mysql_date_format);
264
+		$obj->set_time_format(EE_Datetime_Field::mysql_time_format);
265
+		// set the new date value using a full timestamp format so that no data is lost
266
+		$obj->set($datetime_field_name, $DateTime->format(EE_Datetime_Field::mysql_timestamp_format));
267
+		// reset datetime formats
268
+		$obj->set_date_format($current_format[0]);
269
+		$obj->set_time_format($current_format[1]);
270
+		return $obj;
271
+	}
272
+
273
+
274
+	/**
275
+	 *    date_time_add
276
+	 *    helper for doing simple datetime calculations on a given datetime from EE_Base_Class
277
+	 *    and modifying it IN the EE_Base_Class so you don't have to do anything else.
278
+	 *
279
+	 * @param  EE_Base_Class $obj                 EE_Base_Class object
280
+	 * @param  string        $datetime_field_name name of the EE_Datetime_Filed datatype db column to be manipulated
281
+	 * @param  string        $period              what you are adding. The options are (years, months, days, hours,
282
+	 *                                            minutes, seconds) defaults to years
283
+	 * @param  integer       $value               what you want to increment the time by
284
+	 * @return EE_Base_Class return the EE_Base_Class object so right away you can do something with it
285
+	 *                                            (chaining)
286
+	 * @throws EE_Error
287
+	 * @throws Exception
288
+	 */
289
+	public static function date_time_add(EE_Base_Class $obj, $datetime_field_name, $period = 'years', $value = 1)
290
+	{
291
+		//get the raw UTC date.
292
+		$DateTime = $obj->get_DateTime_object($datetime_field_name);
293
+		$DateTime = EEH_DTT_Helper::calc_date($DateTime, $period, $value);
294
+		return EEH_DTT_Helper::_set_date_time_field($obj, $DateTime, $datetime_field_name);
295
+	}
296
+
297
+
298
+	/**
299
+	 *    date_time_subtract
300
+	 *    same as date_time_add except subtracting value instead of adding.
301
+	 *
302
+	 * @param EE_Base_Class $obj
303
+	 * @param  string       $datetime_field_name name of the EE_Datetime_Filed datatype db column to be manipulated
304
+	 * @param string        $period
305
+	 * @param int           $value
306
+	 * @return EE_Base_Class
307
+	 * @throws EE_Error
308
+	 * @throws Exception
309
+	 */
310
+	public static function date_time_subtract(EE_Base_Class $obj, $datetime_field_name, $period = 'years', $value = 1)
311
+	{
312
+		//get the raw UTC date
313
+		$DateTime = $obj->get_DateTime_object($datetime_field_name);
314
+		$DateTime = EEH_DTT_Helper::calc_date($DateTime, $period, $value, '-');
315
+		return EEH_DTT_Helper::_set_date_time_field($obj, $DateTime, $datetime_field_name);
316
+	}
317
+
318
+
319
+	/**
320
+	 * Simply takes an incoming DateTime object and does calculations on it based on the incoming parameters
321
+	 *
322
+	 * @param  DateTime   $DateTime DateTime object
323
+	 * @param  string     $period   a value to indicate what interval is being used in the calculation. The options are
324
+	 *                              'years', 'months', 'days', 'hours', 'minutes', 'seconds'. Defaults to years.
325
+	 * @param  int|string $value    What you want to increment the date by
326
+	 * @param  string     $operand  What operand you wish to use for the calculation
327
+	 * @return DateTime return whatever type came in.
328
+	 * @throws Exception
329
+	 * @throws EE_Error
330
+	 */
331
+	protected static function _modify_datetime_object(DateTime $DateTime, $period = 'years', $value = 1, $operand = '+')
332
+	{
333
+		if (! $DateTime instanceof DateTime) {
334
+			throw new EE_Error(
335
+				sprintf(
336
+					esc_html__('Expected a PHP DateTime object, but instead received %1$s', 'event_espresso'),
337
+					print_r($DateTime, true)
338
+				)
339
+			);
340
+		}
341
+		switch ($period) {
342
+			case 'years' :
343
+				$value = 'P' . $value . 'Y';
344
+				break;
345
+			case 'months' :
346
+				$value = 'P' . $value . 'M';
347
+				break;
348
+			case 'weeks' :
349
+				$value = 'P' . $value . 'W';
350
+				break;
351
+			case 'days' :
352
+				$value = 'P' . $value . 'D';
353
+				break;
354
+			case 'hours' :
355
+				$value = 'PT' . $value . 'H';
356
+				break;
357
+			case 'minutes' :
358
+				$value = 'PT' . $value . 'M';
359
+				break;
360
+			case 'seconds' :
361
+				$value = 'PT' . $value . 'S';
362
+				break;
363
+		}
364
+		switch ($operand) {
365
+			case '+':
366
+				$DateTime->add(new DateInterval($value));
367
+				break;
368
+			case '-':
369
+				$DateTime->sub(new DateInterval($value));
370
+				break;
371
+		}
372
+		return $DateTime;
373
+	}
374
+
375
+
376
+	/**
377
+	 * Simply takes an incoming Unix timestamp and does calculations on it based on the incoming parameters
378
+	 *
379
+	 * @param  int     $timestamp Unix timestamp
380
+	 * @param  string  $period    a value to indicate what interval is being used in the calculation. The options are
381
+	 *                            'years', 'months', 'days', 'hours', 'minutes', 'seconds'. Defaults to years.
382
+	 * @param  integer $value     What you want to increment the date by
383
+	 * @param  string  $operand   What operand you wish to use for the calculation
384
+	 * @return int
385
+	 * @throws EE_Error
386
+	 */
387
+	protected static function _modify_timestamp($timestamp, $period = 'years', $value = 1, $operand = '+')
388
+	{
389
+		if (! preg_match(EE_Datetime_Field::unix_timestamp_regex, $timestamp)) {
390
+			throw new EE_Error(
391
+				sprintf(
392
+					esc_html__('Expected a Unix timestamp, but instead received %1$s', 'event_espresso'),
393
+					print_r($timestamp, true)
394
+				)
395
+			);
396
+		}
397
+		switch ($period) {
398
+			case 'years' :
399
+				$value = YEAR_IN_SECONDS * $value;
400
+				break;
401
+			case 'months' :
402
+				$value = YEAR_IN_SECONDS / 12 * $value;
403
+				break;
404
+			case 'weeks' :
405
+				$value = WEEK_IN_SECONDS * $value;
406
+				break;
407
+			case 'days' :
408
+				$value = DAY_IN_SECONDS * $value;
409
+				break;
410
+			case 'hours' :
411
+				$value = HOUR_IN_SECONDS * $value;
412
+				break;
413
+			case 'minutes' :
414
+				$value = MINUTE_IN_SECONDS * $value;
415
+				break;
416
+		}
417
+		switch ($operand) {
418
+			case '+':
419
+				$timestamp += $value;
420
+				break;
421
+			case '-':
422
+				$timestamp -= $value;
423
+				break;
424
+		}
425
+		return $timestamp;
426
+	}
427
+
428
+
429
+	/**
430
+	 * Simply takes an incoming UTC timestamp or DateTime object and does calculations on it based on the incoming
431
+	 * parameters and returns the new timestamp or DateTime.
432
+	 *
433
+	 * @param  int | DateTime $DateTime_or_timestamp DateTime object or Unix timestamp
434
+	 * @param  string         $period                a value to indicate what interval is being used in the
435
+	 *                                               calculation. The options are 'years', 'months', 'days', 'hours',
436
+	 *                                               'minutes', 'seconds'. Defaults to years.
437
+	 * @param  integer        $value                 What you want to increment the date by
438
+	 * @param  string         $operand               What operand you wish to use for the calculation
439
+	 * @return mixed string|DateTime          return whatever type came in.
440
+	 * @throws Exception
441
+	 * @throws EE_Error
442
+	 */
443
+	public static function calc_date($DateTime_or_timestamp, $period = 'years', $value = 1, $operand = '+')
444
+	{
445
+		if ($DateTime_or_timestamp instanceof DateTime) {
446
+			return EEH_DTT_Helper::_modify_datetime_object(
447
+				$DateTime_or_timestamp,
448
+				$period,
449
+				$value,
450
+				$operand
451
+			);
452
+		}
453
+		if (preg_match(EE_Datetime_Field::unix_timestamp_regex, $DateTime_or_timestamp)) {
454
+			return EEH_DTT_Helper::_modify_timestamp(
455
+				$DateTime_or_timestamp,
456
+				$period,
457
+				$value,
458
+				$operand
459
+			);
460
+		}
461
+		//error
462
+		return $DateTime_or_timestamp;
463
+	}
464
+
465
+
466
+	/**
467
+	 * The purpose of this helper method is to receive an incoming format string in php date/time format
468
+	 * and spit out the js and moment.js equivalent formats.
469
+	 * Note, if no format string is given, then it is assumed the user wants what is set for WP.
470
+	 * Note, js date and time formats are those used by the jquery-ui datepicker and the jquery-ui date-
471
+	 * time picker.
472
+	 *
473
+	 * @see http://stackoverflow.com/posts/16725290/ for the code inspiration.
474
+	 * @param string $date_format_string
475
+	 * @param string $time_format_string
476
+	 * @return array
477
+	 *              array(
478
+	 *              'js' => array (
479
+	 *              'date' => //date format
480
+	 *              'time' => //time format
481
+	 *              ),
482
+	 *              'moment' => //date and time format.
483
+	 *              )
484
+	 */
485
+	public static function convert_php_to_js_and_moment_date_formats(
486
+		$date_format_string = null,
487
+		$time_format_string = null
488
+	) {
489
+		if ($date_format_string === null) {
490
+			$date_format_string = (string) get_option('date_format');
491
+		}
492
+		if ($time_format_string === null) {
493
+			$time_format_string = (string) get_option('time_format');
494
+		}
495
+		$date_format = self::_php_to_js_moment_converter($date_format_string);
496
+		$time_format = self::_php_to_js_moment_converter($time_format_string);
497
+		return array(
498
+			'js'     => array(
499
+				'date' => $date_format['js'],
500
+				'time' => $time_format['js'],
501
+			),
502
+			'moment' => $date_format['moment'] . ' ' . $time_format['moment'],
503
+		);
504
+	}
505
+
506
+
507
+	/**
508
+	 * This converts incoming format string into js and moment variations.
509
+	 *
510
+	 * @param string $format_string incoming php format string
511
+	 * @return array js and moment formats.
512
+	 */
513
+	protected static function _php_to_js_moment_converter($format_string)
514
+	{
515
+		/**
516
+		 * This is a map of symbols for formats.
517
+		 * The index is the php symbol, the equivalent values are in the array.
518
+		 *
519
+		 * @var array
520
+		 */
521
+		$symbols_map          = array(
522
+			// Day
523
+			//01
524
+			'd' => array(
525
+				'js'     => 'dd',
526
+				'moment' => 'DD',
527
+			),
528
+			//Mon
529
+			'D' => array(
530
+				'js'     => 'D',
531
+				'moment' => 'ddd',
532
+			),
533
+			//1,2,...31
534
+			'j' => array(
535
+				'js'     => 'd',
536
+				'moment' => 'D',
537
+			),
538
+			//Monday
539
+			'l' => array(
540
+				'js'     => 'DD',
541
+				'moment' => 'dddd',
542
+			),
543
+			//ISO numeric representation of the day of the week (1-6)
544
+			'N' => array(
545
+				'js'     => '',
546
+				'moment' => 'E',
547
+			),
548
+			//st,nd.rd
549
+			'S' => array(
550
+				'js'     => '',
551
+				'moment' => 'o',
552
+			),
553
+			//numeric representation of day of week (0-6)
554
+			'w' => array(
555
+				'js'     => '',
556
+				'moment' => 'd',
557
+			),
558
+			//day of year starting from 0 (0-365)
559
+			'z' => array(
560
+				'js'     => 'o',
561
+				'moment' => 'DDD' //note moment does not start with 0 so will need to modify by subtracting 1
562
+			),
563
+			// Week
564
+			//ISO-8601 week number of year (weeks starting on monday)
565
+			'W' => array(
566
+				'js'     => '',
567
+				'moment' => 'w',
568
+			),
569
+			// Month
570
+			// January...December
571
+			'F' => array(
572
+				'js'     => 'MM',
573
+				'moment' => 'MMMM',
574
+			),
575
+			//01...12
576
+			'm' => array(
577
+				'js'     => 'mm',
578
+				'moment' => 'MM',
579
+			),
580
+			//Jan...Dec
581
+			'M' => array(
582
+				'js'     => 'M',
583
+				'moment' => 'MMM',
584
+			),
585
+			//1-12
586
+			'n' => array(
587
+				'js'     => 'm',
588
+				'moment' => 'M',
589
+			),
590
+			//number of days in given month
591
+			't' => array(
592
+				'js'     => '',
593
+				'moment' => '',
594
+			),
595
+			// Year
596
+			//whether leap year or not 1/0
597
+			'L' => array(
598
+				'js'     => '',
599
+				'moment' => '',
600
+			),
601
+			//ISO-8601 year number
602
+			'o' => array(
603
+				'js'     => '',
604
+				'moment' => 'GGGG',
605
+			),
606
+			//1999...2003
607
+			'Y' => array(
608
+				'js'     => 'yy',
609
+				'moment' => 'YYYY',
610
+			),
611
+			//99...03
612
+			'y' => array(
613
+				'js'     => 'y',
614
+				'moment' => 'YY',
615
+			),
616
+			// Time
617
+			// am/pm
618
+			'a' => array(
619
+				'js'     => 'tt',
620
+				'moment' => 'a',
621
+			),
622
+			// AM/PM
623
+			'A' => array(
624
+				'js'     => 'TT',
625
+				'moment' => 'A',
626
+			),
627
+			// Swatch Internet Time?!?
628
+			'B' => array(
629
+				'js'     => '',
630
+				'moment' => '',
631
+			),
632
+			//1...12
633
+			'g' => array(
634
+				'js'     => 'h',
635
+				'moment' => 'h',
636
+			),
637
+			//0...23
638
+			'G' => array(
639
+				'js'     => 'H',
640
+				'moment' => 'H',
641
+			),
642
+			//01...12
643
+			'h' => array(
644
+				'js'     => 'hh',
645
+				'moment' => 'hh',
646
+			),
647
+			//00...23
648
+			'H' => array(
649
+				'js'     => 'HH',
650
+				'moment' => 'HH',
651
+			),
652
+			//00..59
653
+			'i' => array(
654
+				'js'     => 'mm',
655
+				'moment' => 'mm',
656
+			),
657
+			//seconds... 00...59
658
+			's' => array(
659
+				'js'     => 'ss',
660
+				'moment' => 'ss',
661
+			),
662
+			//microseconds
663
+			'u' => array(
664
+				'js'     => '',
665
+				'moment' => '',
666
+			),
667
+		);
668
+		$jquery_ui_format     = '';
669
+		$moment_format        = '';
670
+		$escaping             = false;
671
+		$format_string_length = strlen($format_string);
672
+		for ($i = 0; $i < $format_string_length; $i++) {
673
+			$char = $format_string[ $i ];
674
+			if ($char === '\\') { // PHP date format escaping character
675
+				$i++;
676
+				if ($escaping) {
677
+					$jquery_ui_format .= $format_string[ $i ];
678
+					$moment_format    .= $format_string[ $i ];
679
+				} else {
680
+					$jquery_ui_format .= '\'' . $format_string[ $i ];
681
+					$moment_format    .= $format_string[ $i ];
682
+				}
683
+				$escaping = true;
684
+			} else {
685
+				if ($escaping) {
686
+					$jquery_ui_format .= "'";
687
+					$moment_format    .= "'";
688
+					$escaping         = false;
689
+				}
690
+				if (isset($symbols_map[ $char ])) {
691
+					$jquery_ui_format .= $symbols_map[ $char ]['js'];
692
+					$moment_format    .= $symbols_map[ $char ]['moment'];
693
+				} else {
694
+					$jquery_ui_format .= $char;
695
+					$moment_format    .= $char;
696
+				}
697
+			}
698
+		}
699
+		return array('js' => $jquery_ui_format, 'moment' => $moment_format);
700
+	}
701
+
702
+
703
+	/**
704
+	 * This takes an incoming format string and validates it to ensure it will work fine with PHP.
705
+	 *
706
+	 * @param string $format_string   Incoming format string for php date().
707
+	 * @return mixed bool|array  If all is okay then TRUE is returned.  Otherwise an array of validation
708
+	 *                                errors is returned.  So for client code calling, check for is_array() to
709
+	 *                                indicate failed validations.
710
+	 */
711
+	public static function validate_format_string($format_string)
712
+	{
713
+		$error_msg = array();
714
+		//time format checks
715
+		switch (true) {
716
+			case   strpos($format_string, 'h') !== false  :
717
+			case   strpos($format_string, 'g') !== false :
718
+				/**
719
+				 * if the time string has a lowercase 'h' which == 12 hour time format and there
720
+				 * is not any ante meridiem format ('a' or 'A').  Then throw an error because its
721
+				 * too ambiguous and PHP won't be able to figure out whether 1 = 1pm or 1am.
722
+				 */
723
+				if (stripos($format_string, 'A') === false) {
724
+					$error_msg[] = esc_html__(
725
+						'There is a  time format for 12 hour time but no  "a" or "A" to indicate am/pm.  Without this distinction, PHP is unable to determine if a "1" for the hour value equals "1pm" or "1am".',
726
+						'event_espresso'
727
+					);
728
+				}
729
+				break;
730
+		}
731
+		return empty($error_msg) ? true : $error_msg;
732
+	}
733
+
734
+
735
+	/**
736
+	 *     If the the first date starts at midnight on one day, and the next date ends at midnight on the
737
+	 *     very next day then this method will return true.
738
+	 *    If $date_1 = 2015-12-15 00:00:00 and $date_2 = 2015-12-16 00:00:00 then this function will return true.
739
+	 *    If $date_1 = 2015-12-15 03:00:00 and $date_2 = 2015-12_16 03:00:00 then this function will return false.
740
+	 *    If $date_1 = 2015-12-15 00:00:00 and $date_2 = 2015-12-15 00:00:00 then this function will return true.
741
+	 *
742
+	 * @param mixed $date_1
743
+	 * @param mixed $date_2
744
+	 * @return bool
745
+	 */
746
+	public static function dates_represent_one_24_hour_date($date_1, $date_2)
747
+	{
748
+
749
+		if (
750
+			(! $date_1 instanceof DateTime || ! $date_2 instanceof DateTime)
751
+			|| ($date_1->format(EE_Datetime_Field::mysql_time_format) !== '00:00:00'
752
+				|| $date_2->format(
753
+					EE_Datetime_Field::mysql_time_format
754
+				) !== '00:00:00')
755
+		) {
756
+			return false;
757
+		}
758
+		return $date_2->format('U') - $date_1->format('U') === 86400;
759
+	}
760
+
761
+
762
+	/**
763
+	 * This returns the appropriate query interval string that can be used in sql queries involving mysql Date
764
+	 * Functions.
765
+	 *
766
+	 * @param string $timezone_string    A timezone string in a valid format to instantiate a DateTimeZone object.
767
+	 * @param string $field_for_interval The Database field that is the interval is applied to in the query.
768
+	 * @return string
769
+	 */
770
+	public static function get_sql_query_interval_for_offset($timezone_string, $field_for_interval)
771
+	{
772
+		try {
773
+			/** need to account for timezone offset on the selects */
774
+			$DateTimeZone = new DateTimeZone($timezone_string);
775
+		} catch (Exception $e) {
776
+			$DateTimeZone = null;
777
+		}
778
+		/**
779
+		 * Note get_option( 'gmt_offset') returns a value in hours, whereas DateTimeZone::getOffset returns values in seconds.
780
+		 * Hence we do the calc for DateTimeZone::getOffset.
781
+		 */
782
+		$offset         = $DateTimeZone instanceof DateTimeZone
783
+			? $DateTimeZone->getOffset(new DateTime('now')) / HOUR_IN_SECONDS
784
+			: (float) get_option('gmt_offset');
785
+		$query_interval = $offset < 0
786
+			? 'DATE_SUB(' . $field_for_interval . ', INTERVAL ' . $offset * -1 . ' HOUR)'
787
+			: 'DATE_ADD(' . $field_for_interval . ', INTERVAL ' . $offset . ' HOUR)';
788
+		return $query_interval;
789
+	}
790
+
791
+
792
+	/**
793
+	 * Retrieves the site's default timezone and returns it formatted so it's ready for display
794
+	 * to users. If you want to customize how its displayed feel free to fetch the 'timezone_string'
795
+	 * and 'gmt_offset' WordPress options directly; or use the filter
796
+	 * FHEE__EEH_DTT_Helper__get_timezone_string_for_display
797
+	 * (although note that we remove any HTML that may be added)
798
+	 *
799
+	 * @return string
800
+	 */
801
+	public static function get_timezone_string_for_display()
802
+	{
803
+		$pretty_timezone = apply_filters('FHEE__EEH_DTT_Helper__get_timezone_string_for_display', '');
804
+		if (! empty($pretty_timezone)) {
805
+			return esc_html($pretty_timezone);
806
+		}
807
+		$timezone_string = get_option('timezone_string');
808
+		if ($timezone_string) {
809
+			static $mo_loaded = false;
810
+			// Load translations for continents and cities just like wp_timezone_choice does
811
+			if (! $mo_loaded) {
812
+				$locale = get_locale();
813
+				$mofile = WP_LANG_DIR . '/continents-cities-' . $locale . '.mo';
814
+				load_textdomain('continents-cities', $mofile);
815
+				$mo_loaded = true;
816
+			}
817
+			//well that was easy.
818
+			$parts = explode('/', $timezone_string);
819
+			//remove the continent
820
+			unset($parts[0]);
821
+			$t_parts = array();
822
+			foreach ($parts as $part) {
823
+				$t_parts[] = translate(str_replace('_', ' ', $part), 'continents-cities');
824
+			}
825
+			return implode(' - ', $t_parts);
826
+		}
827
+		//they haven't set the timezone string, so let's return a string like "UTC+1"
828
+		$gmt_offset = get_option('gmt_offset');
829
+		$prefix     = (int) $gmt_offset >= 0 ? '+' : '';
830
+		$parts      = explode('.', (string) $gmt_offset);
831
+		if (count($parts) === 1) {
832
+			$parts[1] = '00';
833
+		} else {
834
+			//convert the part after the decimal, eg "5" (from x.5) or "25" (from x.25)
835
+			//to minutes, eg 30 or 15, respectively
836
+			$hour_fraction = (float) ('0.' . $parts[1]);
837
+			$parts[1]      = (string) $hour_fraction * 60;
838
+		}
839
+		return sprintf(__('UTC%1$s', 'event_espresso'), $prefix . implode(':', $parts));
840
+	}
841
+
842
+
843
+
844
+	/**
845
+	 * So PHP does this awesome thing where if you are trying to get a timestamp
846
+	 * for a month using a string like "February" or "February 2017",
847
+	 * and you don't specify a day as part of your string,
848
+	 * then PHP will use whatever the current day of the month is.
849
+	 * IF the current day of the month happens to be the 30th or 31st,
850
+	 * then PHP gets really confused by a date like February 30,
851
+	 * so instead of saying
852
+	 *      "Hey February only has 28 days (this year)...
853
+	 *      ...you must have meant the last day of the month!"
854
+	 * PHP does the next most logical thing, and bumps the date up to March 2nd,
855
+	 * because someone requesting February 30th obviously meant March 1st!
856
+	 * The way around this is to always set the day to the first,
857
+	 * so that the month will stay on the month you wanted.
858
+	 * this method will add that "1" into your date regardless of the format.
859
+	 *
860
+	 * @param string $month
861
+	 * @return string
862
+	 */
863
+	public static function first_of_month_timestamp($month = '')
864
+	{
865
+		$month = (string) $month;
866
+		$year  = '';
867
+		// check if the incoming string has a year in it or not
868
+		if (preg_match('/\b\d{4}\b/', $month, $matches)) {
869
+			$year = $matches[0];
870
+			// ten remove that from the month string as well as any spaces
871
+			$month = trim(str_replace($year, '', $month));
872
+			// add a space before the year
873
+			$year = " {$year}";
874
+		}
875
+		// return timestamp for something like "February 1 2017"
876
+		return strtotime("{$month} 1{$year}");
877
+	}
878
+
879
+
880
+	/**
881
+	 * This simply returns the timestamp for tomorrow (midnight next day) in this sites timezone.  So it may be midnight
882
+	 * for this sites timezone, but the timestamp could be some other time GMT.
883
+	 */
884
+	public static function tomorrow()
885
+	{
886
+		//The multiplication of -1 ensures that we switch positive offsets to negative and negative offsets to positive
887
+		//before adding to the timestamp.  Why? Because we want tomorrow to be for midnight the next day in THIS timezone
888
+		//not an offset from midnight in UTC.  So if we're starting with UTC 00:00:00, then we want to make sure the
889
+		//final timestamp is equivalent to midnight in this timezone as represented in GMT.
890
+		return strtotime('tomorrow') + (self::get_site_timezone_gmt_offset() * -1);
891
+	}
892
+
893
+
894
+	/**
895
+	 * **
896
+	 * Gives a nicely-formatted list of timezone strings.
897
+	 * Copied from the core wp function by the same name so we could customize to remove UTC offsets.
898
+	 *
899
+	 * @since     4.9.40.rc.008
900
+	 * @staticvar bool $mo_loaded
901
+	 * @staticvar string $locale_loaded
902
+	 * @param string $selected_zone Selected timezone.
903
+	 * @param string $locale        Optional. Locale to load the timezones in. Default current site locale.
904
+	 * @return string
905
+	 */
906
+	public static function wp_timezone_choice($selected_zone, $locale = null)
907
+	{
908
+		static $mo_loaded = false, $locale_loaded = null;
909
+		$continents = array(
910
+			'Africa',
911
+			'America',
912
+			'Antarctica',
913
+			'Arctic',
914
+			'Asia',
915
+			'Atlantic',
916
+			'Australia',
917
+			'Europe',
918
+			'Indian',
919
+			'Pacific',
920
+		);
921
+		// Load translations for continents and cities.
922
+		if (! $mo_loaded || $locale !== $locale_loaded) {
923
+			$locale_loaded = $locale ? $locale : get_locale();
924
+			$mofile        = WP_LANG_DIR . '/continents-cities-' . $locale_loaded . '.mo';
925
+			unload_textdomain('continents-cities');
926
+			load_textdomain('continents-cities', $mofile);
927
+			$mo_loaded = true;
928
+		}
929
+		$zone_data = array();
930
+		foreach (timezone_identifiers_list() as $zone) {
931
+			$zone = explode('/', $zone);
932
+			if (! in_array($zone[0], $continents, true)) {
933
+				continue;
934
+			}
935
+			// This determines what gets set and translated - we don't translate Etc/* strings here, they are done later
936
+			$exists      = array(
937
+				0 => isset($zone[0]) && $zone[0],
938
+				1 => isset($zone[1]) && $zone[1],
939
+				2 => isset($zone[2]) && $zone[2],
940
+			);
941
+			$exists[3]   = $exists[0] && $zone[0] !== 'Etc';
942
+			$exists[4]   = $exists[1] && $exists[3];
943
+			$exists[5]   = $exists[2] && $exists[3];
944
+			$zone_data[] = array(
945
+				'continent'   => $exists[0] ? $zone[0] : '',
946
+				'city'        => $exists[1] ? $zone[1] : '',
947
+				'subcity'     => $exists[2] ? $zone[2] : '',
948
+				't_continent' => $exists[3]
949
+					? translate(str_replace('_', ' ', $zone[0]), 'continents-cities')
950
+					: '',
951
+				't_city'      => $exists[4]
952
+					? translate(str_replace('_', ' ', $zone[1]), 'continents-cities')
953
+					: '',
954
+				't_subcity'   => $exists[5]
955
+					? translate(str_replace('_', ' ', $zone[2]), 'continents-cities')
956
+					: '',
957
+			);
958
+		}
959
+		usort($zone_data, '_wp_timezone_choice_usort_callback');
960
+		$structure = array();
961
+		if (empty($selected_zone)) {
962
+			$structure[] = '<option selected="selected" value="">' . __('Select a city') . '</option>';
963
+		}
964
+		foreach ($zone_data as $key => $zone) {
965
+			// Build value in an array to join later
966
+			$value = array($zone['continent']);
967
+			if (empty($zone['city'])) {
968
+				// It's at the continent level (generally won't happen)
969
+				$display = $zone['t_continent'];
970
+			} else {
971
+				// It's inside a continent group
972
+				// Continent optgroup
973
+				if (! isset($zone_data[ $key - 1 ]) || $zone_data[ $key - 1 ]['continent'] !== $zone['continent']) {
974
+					$label       = $zone['t_continent'];
975
+					$structure[] = '<optgroup label="' . esc_attr($label) . '">';
976
+				}
977
+				// Add the city to the value
978
+				$value[] = $zone['city'];
979
+				$display = $zone['t_city'];
980
+				if (! empty($zone['subcity'])) {
981
+					// Add the subcity to the value
982
+					$value[] = $zone['subcity'];
983
+					$display .= ' - ' . $zone['t_subcity'];
984
+				}
985
+			}
986
+			// Build the value
987
+			$value       = implode('/', $value);
988
+			$selected    = $value === $selected_zone ? ' selected="selected"' : '';
989
+			$structure[] = '<option value="' . esc_attr($value) . '"' . $selected . '>'
990
+						   . esc_html($display)
991
+						   . '</option>';
992
+			// Close continent optgroup
993
+			if (! empty($zone['city'])
994
+				&& (
995
+					! isset($zone_data[ $key + 1 ])
996
+					|| (isset($zone_data[ $key + 1 ]) && $zone_data[ $key + 1 ]['continent'] !== $zone['continent'])
997
+				)
998
+			) {
999
+				$structure[] = '</optgroup>';
1000
+			}
1001
+		}
1002
+		return implode("\n", $structure);
1003
+	}
1004
+
1005
+
1006
+	/**
1007
+	 * Shim for the WP function `get_user_locale` that was added in WordPress 4.7.0
1008
+	 *
1009
+	 * @param int|WP_User $user_id
1010
+	 * @return string
1011
+	 */
1012
+	public static function get_user_locale($user_id = 0)
1013
+	{
1014
+		if (function_exists('get_user_locale')) {
1015
+			return get_user_locale($user_id);
1016
+		}
1017
+		return get_locale();
1018
+	}
1019
+
1020
+
1021
+	/**
1022
+	 * Return the appropriate helper adapter for DTT related things.
1023
+	 *
1024
+	 * @return HelperInterface
1025
+	 * @throws InvalidArgumentException
1026
+	 * @throws InvalidDataTypeException
1027
+	 * @throws InvalidInterfaceException
1028
+	 */
1029
+	private static function getHelperAdapter() {
1030
+		$dtt_helper_fqcn = PHP_VERSION_ID < 50600
1031
+			? 'EventEspresso\core\services\helpers\datetime\PhpCompatLessFiveSixHelper'
1032
+			: 'EventEspresso\core\services\helpers\datetime\PhpCompatGreaterFiveSixHelper';
1033
+		return LoaderFactory::getLoader()->getShared($dtt_helper_fqcn);
1034
+	}
1035
+
1036
+
1037
+	/**
1038
+	 * Helper function for setting the timezone on a DateTime object.
1039
+	 * This is implemented to standardize a workaround for a PHP bug outlined in
1040
+	 * https://events.codebasehq.com/projects/event-espresso/tickets/11407 and
1041
+	 * https://events.codebasehq.com/projects/event-espresso/tickets/11233
1042
+	 *
1043
+	 * @param DateTime     $datetime
1044
+	 * @param DateTimeZone $timezone
1045
+	 */
1046
+	public static function setTimezone(DateTime $datetime, DateTimeZone $timezone)
1047
+	{
1048
+		$datetime->setTimezone($timezone);
1049
+		$datetime->getTimestamp();
1050
+	}
1051 1051
 }
1052 1052
\ No newline at end of file
Please login to merge, or discard this patch.
core/domain/CaffeinatedInterface.php 1 patch
Indentation   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -12,9 +12,9 @@
 block discarded – undo
12 12
  */
13 13
 interface CaffeinatedInterface
14 14
 {
15
-    /**
16
-     * Used to indicate when functionality is caffeinated or not.
17
-     * @return bool
18
-     */
19
-    public function isCaffeinated();
15
+	/**
16
+	 * Used to indicate when functionality is caffeinated or not.
17
+	 * @return bool
18
+	 */
19
+	public function isCaffeinated();
20 20
 }
21 21
\ No newline at end of file
Please login to merge, or discard this patch.
core/domain/Domain.php 2 patches
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -66,7 +66,7 @@
 block discarded – undo
66 66
      */
67 67
     private function setCaffeinated()
68 68
     {
69
-        $this->caffeinated = (! defined('EE_DECAF') || EE_DECAF !== true)
70
-            && is_readable($this->pluginPath() . 'caffeinated/brewing_regular.php');
69
+        $this->caffeinated = ( ! defined('EE_DECAF') || EE_DECAF !== true)
70
+            && is_readable($this->pluginPath().'caffeinated/brewing_regular.php');
71 71
     }
72 72
 }
Please login to merge, or discard this patch.
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -19,54 +19,54 @@
 block discarded – undo
19 19
 class Domain extends DomainBase implements CaffeinatedInterface
20 20
 {
21 21
 
22
-    /**
23
-     * URL path component used to denote an API request
24
-     */
25
-    const API_NAMESPACE = 'ee/v';
22
+	/**
23
+	 * URL path component used to denote an API request
24
+	 */
25
+	const API_NAMESPACE = 'ee/v';
26 26
 
27
-    /**
28
-     * Slug used for the context where a registration status is changed from a manual trigger in the Registration Admin
29
-     * Page ui.
30
-     */
31
-    const CONTEXT_REGISTRATION_STATUS_CHANGE_REGISTRATION_ADMIN
32
-        = 'manual_registration_status_change_from_registration_admin';
27
+	/**
28
+	 * Slug used for the context where a registration status is changed from a manual trigger in the Registration Admin
29
+	 * Page ui.
30
+	 */
31
+	const CONTEXT_REGISTRATION_STATUS_CHANGE_REGISTRATION_ADMIN
32
+		= 'manual_registration_status_change_from_registration_admin';
33 33
 
34
-    const CONTEXT_REGISTRATION_STATUS_CHANGE_REGISTRATION_ADMIN_NOTIFY
35
-        = 'manual_registration_status_change_from_registration_admin_and_notify';
34
+	const CONTEXT_REGISTRATION_STATUS_CHANGE_REGISTRATION_ADMIN_NOTIFY
35
+		= 'manual_registration_status_change_from_registration_admin_and_notify';
36 36
 
37 37
 
38
-    /**
39
-     * Whether or not EE core is the full premium version.
40
-     * @since 4.9.59.p
41
-     * @var bool
42
-     */
43
-    private $caffeinated;
38
+	/**
39
+	 * Whether or not EE core is the full premium version.
40
+	 * @since 4.9.59.p
41
+	 * @var bool
42
+	 */
43
+	private $caffeinated;
44 44
 
45 45
 
46
-    public function __construct(FilePath $plugin_file, Version $version)
47
-    {
48
-        parent::__construct($plugin_file, $version);
49
-        $this->setCaffeinated();
50
-    }
46
+	public function __construct(FilePath $plugin_file, Version $version)
47
+	{
48
+		parent::__construct($plugin_file, $version);
49
+		$this->setCaffeinated();
50
+	}
51 51
 
52
-    /**
53
-     * Whether or not EE core is the full premium version.
54
-     * @since 4.9.59.p
55
-     * @return bool
56
-     */
57
-    public function isCaffeinated()
58
-    {
59
-        return $this->caffeinated;
60
-    }
52
+	/**
53
+	 * Whether or not EE core is the full premium version.
54
+	 * @since 4.9.59.p
55
+	 * @return bool
56
+	 */
57
+	public function isCaffeinated()
58
+	{
59
+		return $this->caffeinated;
60
+	}
61 61
 
62 62
 
63
-    /**
64
-     * Setter for $is_caffeinated property.
65
-     * @since 4.9.59.p
66
-     */
67
-    private function setCaffeinated()
68
-    {
69
-        $this->caffeinated = (! defined('EE_DECAF') || EE_DECAF !== true)
70
-            && is_readable($this->pluginPath() . 'caffeinated/brewing_regular.php');
71
-    }
63
+	/**
64
+	 * Setter for $is_caffeinated property.
65
+	 * @since 4.9.59.p
66
+	 */
67
+	private function setCaffeinated()
68
+	{
69
+		$this->caffeinated = (! defined('EE_DECAF') || EE_DECAF !== true)
70
+			&& is_readable($this->pluginPath() . 'caffeinated/brewing_regular.php');
71
+	}
72 72
 }
Please login to merge, or discard this patch.