Completed
Branch BUG/ticket-embed-expired-event... (80a031)
by
unknown
21:16 queued 09:22
created
caffeinated/modules/recaptcha_invisible/EED_Recaptcha_Invisible.module.php 2 patches
Indentation   +328 added lines, -328 removed lines patch added patch discarded remove patch
@@ -18,332 +18,332 @@
 block discarded – undo
18 18
 class EED_Recaptcha_Invisible extends EED_Module
19 19
 {
20 20
 
21
-    /**
22
-     * @var EE_Registration_Config $config
23
-     */
24
-    private static $config;
25
-
26
-
27
-    /**
28
-     * @return EED_Module|EED_Recaptcha
29
-     */
30
-    public static function instance()
31
-    {
32
-        return parent::get_instance(__CLASS__);
33
-    }
34
-
35
-
36
-    /**
37
-     * @return void
38
-     * @throws InvalidInterfaceException
39
-     * @throws InvalidDataTypeException
40
-     * @throws InvalidArgumentException
41
-     */
42
-    public static function set_hooks()
43
-    {
44
-        EED_Recaptcha_Invisible::setProperties();
45
-        if (EED_Recaptcha_Invisible::useInvisibleRecaptcha()) {
46
-            if (EED_Recaptcha_Invisible::protectForm('ticket_selector')) {
47
-                // ticket selection
48
-                add_filter(
49
-                    'FHEE__EE_Ticket_Selector__after_ticket_selector_submit',
50
-                    array('EED_Recaptcha_Invisible', 'ticketSelectorForm'),
51
-                    10,
52
-                    3
53
-                );
54
-                add_action(
55
-                    'EED_Ticket_Selector__process_ticket_selections__before',
56
-                    array('EED_Recaptcha_Invisible', 'processTicketSelectorForm')
57
-                );
58
-            }
59
-            if (EED_Recaptcha_Invisible::protectForm('registration_form')) {
60
-                // checkout
61
-                add_action(
62
-                    'AHEE__EE_SPCO_Reg_Step__display_reg_form__reg_form',
63
-                    array('EED_Recaptcha_Invisible', 'spcoRegStepForm')
64
-                );
65
-                add_filter(
66
-                    'FHEE__EE_Form_Section_Proper__receive_form_submission__req_data',
67
-                    array('EED_Recaptcha_Invisible', 'receiveSpcoRegStepForm'),
68
-                    10,
69
-                    2
70
-                );
71
-            }
72
-            add_action('loop_end', array('EED_Recaptcha_Invisible', 'localizeScriptVars'));
73
-        }
74
-    }
75
-
76
-
77
-    /**
78
-     * @return void
79
-     * @throws InvalidInterfaceException
80
-     * @throws InvalidDataTypeException
81
-     * @throws InvalidArgumentException
82
-     */
83
-    public static function set_hooks_admin()
84
-    {
85
-        EED_Recaptcha_Invisible::setProperties();
86
-        if (EED_Recaptcha_Invisible::protectForm('ticket_selector')) {
87
-            add_action(
88
-                'EED_Ticket_Selector__process_ticket_selections__before',
89
-                array('EED_Recaptcha_Invisible', 'processTicketSelectorForm')
90
-            );
91
-        }
92
-        if (EED_Recaptcha_Invisible::protectForm('registration_form')) {
93
-            add_filter(
94
-                'FHEE__EE_Form_Section_Proper__receive_form_submission__req_data',
95
-                array('EED_Recaptcha_Invisible', 'receiveSpcoRegStepForm'),
96
-                10,
97
-                2
98
-            );
99
-        }
100
-        // admin settings
101
-        add_action(
102
-            'AHEE__Extend_Registration_Form_Admin_Page___reg_form_settings_template',
103
-            array('EED_Recaptcha_Invisible', 'adminSettings')
104
-        );
105
-        add_filter(
106
-            'FHEE__Extend_Registration_Form_Admin_Page___update_reg_form_settings__CFG_registration',
107
-            array('EED_Recaptcha_Invisible', 'updateAdminSettings')
108
-        );
109
-    }
110
-
111
-
112
-    /**
113
-     * @return void
114
-     * @throws InvalidInterfaceException
115
-     * @throws InvalidDataTypeException
116
-     * @throws InvalidArgumentException
117
-     */
118
-    public static function setProperties()
119
-    {
120
-
121
-        EED_Recaptcha_Invisible::$config = EE_Registry::instance()->CFG->registration;
122
-    }
123
-
124
-
125
-    /**
126
-     * @return boolean
127
-     */
128
-    public static function useInvisibleRecaptcha()
129
-    {
130
-        return EED_Recaptcha_Invisible::$config->use_captcha
131
-               && EED_Recaptcha_Invisible::$config->recaptcha_theme === 'invisible';
132
-    }
133
-
134
-
135
-    /**
136
-     * @param string $form
137
-     * @return boolean
138
-     */
139
-    public static function protectForm($form)
140
-    {
141
-        return is_array(EED_Recaptcha_Invisible::$config->recaptcha_protected_forms)
142
-               && in_array($form, EED_Recaptcha_Invisible::$config->recaptcha_protected_forms, true);
143
-    }
144
-
145
-
146
-    /**
147
-     * @return void
148
-     * @throws InvalidInterfaceException
149
-     * @throws InvalidDataTypeException
150
-     * @throws InvalidArgumentException
151
-     */
152
-    public static function localizeScriptVars()
153
-    {
154
-        /** @var \EventEspresso\core\services\request\Request $request */
155
-        $request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\Request');
156
-        // Invisible Recaptcha is ONLY ever required for the frontend and admin
157
-        // so we don't need to load any JS assets for other types of requests (like AJAX or API).
158
-        if (! ($request->isAdmin() || $request->isFrontend())) {
159
-            return;
160
-        }
161
-        wp_localize_script(
162
-            EE_Invisible_Recaptcha_Input::SCRIPT_HANDLE_ESPRESSO_INVISIBLE_RECAPTCHA,
163
-            'eeRecaptcha',
164
-            RecaptchaFactory::create()->getLocalizedVars()
165
-        );
166
-    }
167
-
168
-
169
-    /**
170
-     * @return string
171
-     */
172
-    public static function assetsUrl()
173
-    {
174
-        return plugin_dir_url(__FILE__) . 'assets' . DS;
175
-    }
176
-
177
-
178
-    /**
179
-     * @param \WP $WP
180
-     */
181
-    public function run($WP)
182
-    {
183
-    }
184
-
185
-
186
-    /**
187
-     * @param RequestInterface $request
188
-     * @return bool
189
-     * @throws InvalidArgumentException
190
-     * @throws InvalidDataTypeException
191
-     * @throws InvalidInterfaceException
192
-     * @throws RuntimeException
193
-     */
194
-    public static function verifyToken(RequestInterface $request)
195
-    {
196
-        return RecaptchaFactory::create()->verifyToken($request);
197
-    }
198
-
199
-
200
-    /**
201
-     * @param EE_Form_Section_Proper $reg_form
202
-     * @return void
203
-     * @throws EE_Error
204
-     * @throws InvalidArgumentException
205
-     * @throws InvalidDataTypeException
206
-     * @throws InvalidInterfaceException
207
-     * @throws DomainException
208
-     */
209
-    public static function spcoRegStepForm(EE_Form_Section_Proper $reg_form)
210
-    {
211
-        // do nothing if form isn't for a reg step or test has already been passed
212
-        if (! EED_Recaptcha_Invisible::processSpcoRegStepForm($reg_form)) {
213
-            return;
214
-        }
215
-        $default_hidden_inputs = $reg_form->get_subsection('default_hidden_inputs');
216
-        if ($default_hidden_inputs instanceof EE_Form_Section_Proper) {
217
-            $invisible_recaptcha = RecaptchaFactory::create();
218
-            $invisible_recaptcha->addToFormSection($default_hidden_inputs);
219
-        }
220
-    }
221
-
222
-
223
-    /**
224
-     * @param EE_Form_Section_Proper $reg_form
225
-     * @return bool
226
-     * @throws InvalidDataTypeException
227
-     * @throws InvalidInterfaceException
228
-     * @throws EE_Error
229
-     * @throws InvalidArgumentException
230
-     */
231
-    public static function processSpcoRegStepForm(EE_Form_Section_Proper $reg_form)
232
-    {
233
-        return strpos($reg_form->name(), 'reg-step-form') !== false
234
-               && ! RecaptchaFactory::create()->recaptchaPassed();
235
-    }
236
-
237
-
238
-    /**
239
-     * @param array|null             $req_data
240
-     * @param EE_Form_Section_Proper $reg_form
241
-     * @return array
242
-     * @throws EE_Error
243
-     * @throws InvalidArgumentException
244
-     * @throws InvalidDataTypeException
245
-     * @throws InvalidInterfaceException
246
-     * @throws RuntimeException
247
-     */
248
-    public static function receiveSpcoRegStepForm($req_data = array(), EE_Form_Section_Proper $reg_form)
249
-    {
250
-        // do nothing if form isn't for a reg step or test has already been passed
251
-        if (! EED_Recaptcha_Invisible::processSpcoRegStepForm($reg_form)) {
252
-            return $req_data;
253
-        }
254
-        /** @var RequestInterface $request */
255
-        $request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\RequestInterface');
256
-        if (! EED_Recaptcha_Invisible::verifyToken($request)) {
257
-            if ($request->isAjax()) {
258
-                $json_response = new EE_SPCO_JSON_Response();
259
-                $json_response->echoAndExit();
260
-            }
261
-            EEH_URL::safeRedirectAndExit(
262
-                EE_Registry::instance()->CFG->core->reg_page_url()
263
-            );
264
-        }
265
-        return $req_data;
266
-    }
267
-
268
-
269
-    /**
270
-     * @param string   $html
271
-     * @param EE_Event $event
272
-     * @param bool     $iframe
273
-     * @return string
274
-     * @throws EE_Error
275
-     * @throws InvalidArgumentException
276
-     * @throws InvalidDataTypeException
277
-     * @throws InvalidInterfaceException
278
-     * @throws ReflectionException
279
-     * @throws DomainException
280
-     */
281
-    public static function ticketSelectorForm($html = '', EE_Event $event, $iframe = false)
282
-    {
283
-        $recaptcha = RecaptchaFactory::create();
284
-        // do nothing if test has  already  been passed
285
-        if ($recaptcha->recaptchaPassed()) {
286
-            return $html;
287
-        }
288
-        $html .= $recaptcha->getInputHtml(
289
-            array(
290
-                'recaptcha_id'   => $event->ID(),
291
-                'iframe'         => $iframe,
292
-                'localized_vars' => $recaptcha->getLocalizedVars(),
293
-            )
294
-        );
295
-        return $html;
296
-    }
297
-
298
-
299
-    /**
300
-     * @return void
301
-     * @throws InvalidArgumentException
302
-     * @throws InvalidInterfaceException
303
-     * @throws InvalidDataTypeException
304
-     * @throws RuntimeException
305
-     */
306
-    public static function processTicketSelectorForm()
307
-    {
308
-        // do nothing if test has  already  been passed
309
-        if (RecaptchaFactory::create()->recaptchaPassed()) {
310
-            return;
311
-        }
312
-        /** @var RequestInterface $request */
313
-        $request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\RequestInterface');
314
-        if (! EED_Recaptcha_Invisible::verifyToken($request)) {
315
-            $event_id = $request->getRequestParam('tkt-slctr-event-id');
316
-            $return_url = $request->requestParamIsSet("tkt-slctr-return-url-{$event_id}")
317
-                ? $request->getRequestParam("tkt-slctr-return-url-{$event_id}")
318
-                : get_permalink($event_id);
319
-            EEH_URL::safeRedirectAndExit($return_url);
320
-        }
321
-    }
322
-
323
-
324
-    /**
325
-     * @throws EE_Error
326
-     * @throws InvalidArgumentException
327
-     * @throws InvalidDataTypeException
328
-     * @throws InvalidInterfaceException
329
-     */
330
-    public static function adminSettings()
331
-    {
332
-        RecaptchaFactory::getAdminModule()->adminSettings();
333
-    }
334
-
335
-
336
-    /**
337
-     * @param EE_Registration_Config $EE_Registration_Config
338
-     * @return EE_Registration_Config
339
-     * @throws EE_Error
340
-     * @throws InvalidArgumentException
341
-     * @throws InvalidDataTypeException
342
-     * @throws InvalidInterfaceException
343
-     * @throws ReflectionException
344
-     */
345
-    public static function updateAdminSettings(EE_Registration_Config $EE_Registration_Config)
346
-    {
347
-        return RecaptchaFactory::getAdminModule()->updateAdminSettings($EE_Registration_Config);
348
-    }
21
+	/**
22
+	 * @var EE_Registration_Config $config
23
+	 */
24
+	private static $config;
25
+
26
+
27
+	/**
28
+	 * @return EED_Module|EED_Recaptcha
29
+	 */
30
+	public static function instance()
31
+	{
32
+		return parent::get_instance(__CLASS__);
33
+	}
34
+
35
+
36
+	/**
37
+	 * @return void
38
+	 * @throws InvalidInterfaceException
39
+	 * @throws InvalidDataTypeException
40
+	 * @throws InvalidArgumentException
41
+	 */
42
+	public static function set_hooks()
43
+	{
44
+		EED_Recaptcha_Invisible::setProperties();
45
+		if (EED_Recaptcha_Invisible::useInvisibleRecaptcha()) {
46
+			if (EED_Recaptcha_Invisible::protectForm('ticket_selector')) {
47
+				// ticket selection
48
+				add_filter(
49
+					'FHEE__EE_Ticket_Selector__after_ticket_selector_submit',
50
+					array('EED_Recaptcha_Invisible', 'ticketSelectorForm'),
51
+					10,
52
+					3
53
+				);
54
+				add_action(
55
+					'EED_Ticket_Selector__process_ticket_selections__before',
56
+					array('EED_Recaptcha_Invisible', 'processTicketSelectorForm')
57
+				);
58
+			}
59
+			if (EED_Recaptcha_Invisible::protectForm('registration_form')) {
60
+				// checkout
61
+				add_action(
62
+					'AHEE__EE_SPCO_Reg_Step__display_reg_form__reg_form',
63
+					array('EED_Recaptcha_Invisible', 'spcoRegStepForm')
64
+				);
65
+				add_filter(
66
+					'FHEE__EE_Form_Section_Proper__receive_form_submission__req_data',
67
+					array('EED_Recaptcha_Invisible', 'receiveSpcoRegStepForm'),
68
+					10,
69
+					2
70
+				);
71
+			}
72
+			add_action('loop_end', array('EED_Recaptcha_Invisible', 'localizeScriptVars'));
73
+		}
74
+	}
75
+
76
+
77
+	/**
78
+	 * @return void
79
+	 * @throws InvalidInterfaceException
80
+	 * @throws InvalidDataTypeException
81
+	 * @throws InvalidArgumentException
82
+	 */
83
+	public static function set_hooks_admin()
84
+	{
85
+		EED_Recaptcha_Invisible::setProperties();
86
+		if (EED_Recaptcha_Invisible::protectForm('ticket_selector')) {
87
+			add_action(
88
+				'EED_Ticket_Selector__process_ticket_selections__before',
89
+				array('EED_Recaptcha_Invisible', 'processTicketSelectorForm')
90
+			);
91
+		}
92
+		if (EED_Recaptcha_Invisible::protectForm('registration_form')) {
93
+			add_filter(
94
+				'FHEE__EE_Form_Section_Proper__receive_form_submission__req_data',
95
+				array('EED_Recaptcha_Invisible', 'receiveSpcoRegStepForm'),
96
+				10,
97
+				2
98
+			);
99
+		}
100
+		// admin settings
101
+		add_action(
102
+			'AHEE__Extend_Registration_Form_Admin_Page___reg_form_settings_template',
103
+			array('EED_Recaptcha_Invisible', 'adminSettings')
104
+		);
105
+		add_filter(
106
+			'FHEE__Extend_Registration_Form_Admin_Page___update_reg_form_settings__CFG_registration',
107
+			array('EED_Recaptcha_Invisible', 'updateAdminSettings')
108
+		);
109
+	}
110
+
111
+
112
+	/**
113
+	 * @return void
114
+	 * @throws InvalidInterfaceException
115
+	 * @throws InvalidDataTypeException
116
+	 * @throws InvalidArgumentException
117
+	 */
118
+	public static function setProperties()
119
+	{
120
+
121
+		EED_Recaptcha_Invisible::$config = EE_Registry::instance()->CFG->registration;
122
+	}
123
+
124
+
125
+	/**
126
+	 * @return boolean
127
+	 */
128
+	public static function useInvisibleRecaptcha()
129
+	{
130
+		return EED_Recaptcha_Invisible::$config->use_captcha
131
+			   && EED_Recaptcha_Invisible::$config->recaptcha_theme === 'invisible';
132
+	}
133
+
134
+
135
+	/**
136
+	 * @param string $form
137
+	 * @return boolean
138
+	 */
139
+	public static function protectForm($form)
140
+	{
141
+		return is_array(EED_Recaptcha_Invisible::$config->recaptcha_protected_forms)
142
+			   && in_array($form, EED_Recaptcha_Invisible::$config->recaptcha_protected_forms, true);
143
+	}
144
+
145
+
146
+	/**
147
+	 * @return void
148
+	 * @throws InvalidInterfaceException
149
+	 * @throws InvalidDataTypeException
150
+	 * @throws InvalidArgumentException
151
+	 */
152
+	public static function localizeScriptVars()
153
+	{
154
+		/** @var \EventEspresso\core\services\request\Request $request */
155
+		$request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\Request');
156
+		// Invisible Recaptcha is ONLY ever required for the frontend and admin
157
+		// so we don't need to load any JS assets for other types of requests (like AJAX or API).
158
+		if (! ($request->isAdmin() || $request->isFrontend())) {
159
+			return;
160
+		}
161
+		wp_localize_script(
162
+			EE_Invisible_Recaptcha_Input::SCRIPT_HANDLE_ESPRESSO_INVISIBLE_RECAPTCHA,
163
+			'eeRecaptcha',
164
+			RecaptchaFactory::create()->getLocalizedVars()
165
+		);
166
+	}
167
+
168
+
169
+	/**
170
+	 * @return string
171
+	 */
172
+	public static function assetsUrl()
173
+	{
174
+		return plugin_dir_url(__FILE__) . 'assets' . DS;
175
+	}
176
+
177
+
178
+	/**
179
+	 * @param \WP $WP
180
+	 */
181
+	public function run($WP)
182
+	{
183
+	}
184
+
185
+
186
+	/**
187
+	 * @param RequestInterface $request
188
+	 * @return bool
189
+	 * @throws InvalidArgumentException
190
+	 * @throws InvalidDataTypeException
191
+	 * @throws InvalidInterfaceException
192
+	 * @throws RuntimeException
193
+	 */
194
+	public static function verifyToken(RequestInterface $request)
195
+	{
196
+		return RecaptchaFactory::create()->verifyToken($request);
197
+	}
198
+
199
+
200
+	/**
201
+	 * @param EE_Form_Section_Proper $reg_form
202
+	 * @return void
203
+	 * @throws EE_Error
204
+	 * @throws InvalidArgumentException
205
+	 * @throws InvalidDataTypeException
206
+	 * @throws InvalidInterfaceException
207
+	 * @throws DomainException
208
+	 */
209
+	public static function spcoRegStepForm(EE_Form_Section_Proper $reg_form)
210
+	{
211
+		// do nothing if form isn't for a reg step or test has already been passed
212
+		if (! EED_Recaptcha_Invisible::processSpcoRegStepForm($reg_form)) {
213
+			return;
214
+		}
215
+		$default_hidden_inputs = $reg_form->get_subsection('default_hidden_inputs');
216
+		if ($default_hidden_inputs instanceof EE_Form_Section_Proper) {
217
+			$invisible_recaptcha = RecaptchaFactory::create();
218
+			$invisible_recaptcha->addToFormSection($default_hidden_inputs);
219
+		}
220
+	}
221
+
222
+
223
+	/**
224
+	 * @param EE_Form_Section_Proper $reg_form
225
+	 * @return bool
226
+	 * @throws InvalidDataTypeException
227
+	 * @throws InvalidInterfaceException
228
+	 * @throws EE_Error
229
+	 * @throws InvalidArgumentException
230
+	 */
231
+	public static function processSpcoRegStepForm(EE_Form_Section_Proper $reg_form)
232
+	{
233
+		return strpos($reg_form->name(), 'reg-step-form') !== false
234
+			   && ! RecaptchaFactory::create()->recaptchaPassed();
235
+	}
236
+
237
+
238
+	/**
239
+	 * @param array|null             $req_data
240
+	 * @param EE_Form_Section_Proper $reg_form
241
+	 * @return array
242
+	 * @throws EE_Error
243
+	 * @throws InvalidArgumentException
244
+	 * @throws InvalidDataTypeException
245
+	 * @throws InvalidInterfaceException
246
+	 * @throws RuntimeException
247
+	 */
248
+	public static function receiveSpcoRegStepForm($req_data = array(), EE_Form_Section_Proper $reg_form)
249
+	{
250
+		// do nothing if form isn't for a reg step or test has already been passed
251
+		if (! EED_Recaptcha_Invisible::processSpcoRegStepForm($reg_form)) {
252
+			return $req_data;
253
+		}
254
+		/** @var RequestInterface $request */
255
+		$request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\RequestInterface');
256
+		if (! EED_Recaptcha_Invisible::verifyToken($request)) {
257
+			if ($request->isAjax()) {
258
+				$json_response = new EE_SPCO_JSON_Response();
259
+				$json_response->echoAndExit();
260
+			}
261
+			EEH_URL::safeRedirectAndExit(
262
+				EE_Registry::instance()->CFG->core->reg_page_url()
263
+			);
264
+		}
265
+		return $req_data;
266
+	}
267
+
268
+
269
+	/**
270
+	 * @param string   $html
271
+	 * @param EE_Event $event
272
+	 * @param bool     $iframe
273
+	 * @return string
274
+	 * @throws EE_Error
275
+	 * @throws InvalidArgumentException
276
+	 * @throws InvalidDataTypeException
277
+	 * @throws InvalidInterfaceException
278
+	 * @throws ReflectionException
279
+	 * @throws DomainException
280
+	 */
281
+	public static function ticketSelectorForm($html = '', EE_Event $event, $iframe = false)
282
+	{
283
+		$recaptcha = RecaptchaFactory::create();
284
+		// do nothing if test has  already  been passed
285
+		if ($recaptcha->recaptchaPassed()) {
286
+			return $html;
287
+		}
288
+		$html .= $recaptcha->getInputHtml(
289
+			array(
290
+				'recaptcha_id'   => $event->ID(),
291
+				'iframe'         => $iframe,
292
+				'localized_vars' => $recaptcha->getLocalizedVars(),
293
+			)
294
+		);
295
+		return $html;
296
+	}
297
+
298
+
299
+	/**
300
+	 * @return void
301
+	 * @throws InvalidArgumentException
302
+	 * @throws InvalidInterfaceException
303
+	 * @throws InvalidDataTypeException
304
+	 * @throws RuntimeException
305
+	 */
306
+	public static function processTicketSelectorForm()
307
+	{
308
+		// do nothing if test has  already  been passed
309
+		if (RecaptchaFactory::create()->recaptchaPassed()) {
310
+			return;
311
+		}
312
+		/** @var RequestInterface $request */
313
+		$request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\RequestInterface');
314
+		if (! EED_Recaptcha_Invisible::verifyToken($request)) {
315
+			$event_id = $request->getRequestParam('tkt-slctr-event-id');
316
+			$return_url = $request->requestParamIsSet("tkt-slctr-return-url-{$event_id}")
317
+				? $request->getRequestParam("tkt-slctr-return-url-{$event_id}")
318
+				: get_permalink($event_id);
319
+			EEH_URL::safeRedirectAndExit($return_url);
320
+		}
321
+	}
322
+
323
+
324
+	/**
325
+	 * @throws EE_Error
326
+	 * @throws InvalidArgumentException
327
+	 * @throws InvalidDataTypeException
328
+	 * @throws InvalidInterfaceException
329
+	 */
330
+	public static function adminSettings()
331
+	{
332
+		RecaptchaFactory::getAdminModule()->adminSettings();
333
+	}
334
+
335
+
336
+	/**
337
+	 * @param EE_Registration_Config $EE_Registration_Config
338
+	 * @return EE_Registration_Config
339
+	 * @throws EE_Error
340
+	 * @throws InvalidArgumentException
341
+	 * @throws InvalidDataTypeException
342
+	 * @throws InvalidInterfaceException
343
+	 * @throws ReflectionException
344
+	 */
345
+	public static function updateAdminSettings(EE_Registration_Config $EE_Registration_Config)
346
+	{
347
+		return RecaptchaFactory::getAdminModule()->updateAdminSettings($EE_Registration_Config);
348
+	}
349 349
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -155,7 +155,7 @@  discard block
 block discarded – undo
155 155
         $request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\Request');
156 156
         // Invisible Recaptcha is ONLY ever required for the frontend and admin
157 157
         // so we don't need to load any JS assets for other types of requests (like AJAX or API).
158
-        if (! ($request->isAdmin() || $request->isFrontend())) {
158
+        if ( ! ($request->isAdmin() || $request->isFrontend())) {
159 159
             return;
160 160
         }
161 161
         wp_localize_script(
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
      */
172 172
     public static function assetsUrl()
173 173
     {
174
-        return plugin_dir_url(__FILE__) . 'assets' . DS;
174
+        return plugin_dir_url(__FILE__).'assets'.DS;
175 175
     }
176 176
 
177 177
 
@@ -209,7 +209,7 @@  discard block
 block discarded – undo
209 209
     public static function spcoRegStepForm(EE_Form_Section_Proper $reg_form)
210 210
     {
211 211
         // do nothing if form isn't for a reg step or test has already been passed
212
-        if (! EED_Recaptcha_Invisible::processSpcoRegStepForm($reg_form)) {
212
+        if ( ! EED_Recaptcha_Invisible::processSpcoRegStepForm($reg_form)) {
213 213
             return;
214 214
         }
215 215
         $default_hidden_inputs = $reg_form->get_subsection('default_hidden_inputs');
@@ -248,12 +248,12 @@  discard block
 block discarded – undo
248 248
     public static function receiveSpcoRegStepForm($req_data = array(), EE_Form_Section_Proper $reg_form)
249 249
     {
250 250
         // do nothing if form isn't for a reg step or test has already been passed
251
-        if (! EED_Recaptcha_Invisible::processSpcoRegStepForm($reg_form)) {
251
+        if ( ! EED_Recaptcha_Invisible::processSpcoRegStepForm($reg_form)) {
252 252
             return $req_data;
253 253
         }
254 254
         /** @var RequestInterface $request */
255 255
         $request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\RequestInterface');
256
-        if (! EED_Recaptcha_Invisible::verifyToken($request)) {
256
+        if ( ! EED_Recaptcha_Invisible::verifyToken($request)) {
257 257
             if ($request->isAjax()) {
258 258
                 $json_response = new EE_SPCO_JSON_Response();
259 259
                 $json_response->echoAndExit();
@@ -311,7 +311,7 @@  discard block
 block discarded – undo
311 311
         }
312 312
         /** @var RequestInterface $request */
313 313
         $request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\RequestInterface');
314
-        if (! EED_Recaptcha_Invisible::verifyToken($request)) {
314
+        if ( ! EED_Recaptcha_Invisible::verifyToken($request)) {
315 315
             $event_id = $request->getRequestParam('tkt-slctr-event-id');
316 316
             $return_url = $request->requestParamIsSet("tkt-slctr-return-url-{$event_id}")
317 317
                 ? $request->getRequestParam("tkt-slctr-return-url-{$event_id}")
Please login to merge, or discard this patch.
caffeinated/modules/recaptcha_invisible/InvisibleRecaptcha.php 2 patches
Indentation   +228 added lines, -228 removed lines patch added patch discarded remove patch
@@ -27,258 +27,258 @@
 block discarded – undo
27 27
 class InvisibleRecaptcha
28 28
 {
29 29
 
30
-    const URL_GOOGLE_RECAPTCHA_API          = 'https://www.google.com/recaptcha/api/siteverify';
30
+	const URL_GOOGLE_RECAPTCHA_API          = 'https://www.google.com/recaptcha/api/siteverify';
31 31
 
32
-    const SESSION_DATA_KEY_RECAPTCHA_PASSED = 'recaptcha_passed';
32
+	const SESSION_DATA_KEY_RECAPTCHA_PASSED = 'recaptcha_passed';
33 33
 
34
-    /**
35
-     * @var EE_Registration_Config $config
36
-     */
37
-    private $config;
34
+	/**
35
+	 * @var EE_Registration_Config $config
36
+	 */
37
+	private $config;
38 38
 
39
-    /**
40
-     * @var EE_Session $session
41
-     */
42
-    private $session;
39
+	/**
40
+	 * @var EE_Session $session
41
+	 */
42
+	private $session;
43 43
 
44
-    /**
45
-     * @var boolean $recaptcha_passed
46
-     */
47
-    private $recaptcha_passed;
44
+	/**
45
+	 * @var boolean $recaptcha_passed
46
+	 */
47
+	private $recaptcha_passed;
48 48
 
49 49
 
50
-    /**
51
-     * InvisibleRecaptcha constructor.
52
-     *
53
-     * @param EE_Registration_Config $registration_config
54
-     * @param EE_Session             $session
55
-     */
56
-    public function __construct(EE_Registration_Config $registration_config, EE_Session $session)
57
-    {
58
-        $this->config = $registration_config;
59
-        $this->session = $session;
60
-    }
50
+	/**
51
+	 * InvisibleRecaptcha constructor.
52
+	 *
53
+	 * @param EE_Registration_Config $registration_config
54
+	 * @param EE_Session             $session
55
+	 */
56
+	public function __construct(EE_Registration_Config $registration_config, EE_Session $session)
57
+	{
58
+		$this->config = $registration_config;
59
+		$this->session = $session;
60
+	}
61 61
 
62 62
 
63
-    /**
64
-     * @return boolean
65
-     */
66
-    public function useInvisibleRecaptcha()
67
-    {
68
-        return $this->config->use_captcha && $this->config->recaptcha_theme === 'invisible';
69
-    }
63
+	/**
64
+	 * @return boolean
65
+	 */
66
+	public function useInvisibleRecaptcha()
67
+	{
68
+		return $this->config->use_captcha && $this->config->recaptcha_theme === 'invisible';
69
+	}
70 70
 
71 71
 
72
-    /**
73
-     * @param array $input_settings
74
-     * @return EE_Invisible_Recaptcha_Input
75
-     * @throws InvalidDataTypeException
76
-     * @throws InvalidInterfaceException
77
-     * @throws InvalidArgumentException
78
-     * @throws DomainException
79
-     */
80
-    public function getInput(array $input_settings = array())
81
-    {
82
-        return new EE_Invisible_Recaptcha_Input(
83
-            $input_settings,
84
-            $this->config
85
-        );
86
-    }
72
+	/**
73
+	 * @param array $input_settings
74
+	 * @return EE_Invisible_Recaptcha_Input
75
+	 * @throws InvalidDataTypeException
76
+	 * @throws InvalidInterfaceException
77
+	 * @throws InvalidArgumentException
78
+	 * @throws DomainException
79
+	 */
80
+	public function getInput(array $input_settings = array())
81
+	{
82
+		return new EE_Invisible_Recaptcha_Input(
83
+			$input_settings,
84
+			$this->config
85
+		);
86
+	}
87 87
 
88 88
 
89
-    /**
90
-     * @param array $input_settings
91
-     * @return string
92
-     * @throws EE_Error
93
-     * @throws InvalidDataTypeException
94
-     * @throws InvalidInterfaceException
95
-     * @throws InvalidArgumentException
96
-     * @throws DomainException
97
-     */
98
-    public function getInputHtml(array $input_settings = array())
99
-    {
100
-        return $this->getInput($input_settings)->get_html_for_input();
101
-    }
89
+	/**
90
+	 * @param array $input_settings
91
+	 * @return string
92
+	 * @throws EE_Error
93
+	 * @throws InvalidDataTypeException
94
+	 * @throws InvalidInterfaceException
95
+	 * @throws InvalidArgumentException
96
+	 * @throws DomainException
97
+	 */
98
+	public function getInputHtml(array $input_settings = array())
99
+	{
100
+		return $this->getInput($input_settings)->get_html_for_input();
101
+	}
102 102
 
103 103
 
104
-    /**
105
-     * @param EE_Form_Section_Proper $form
106
-     * @param array                  $input_settings
107
-     * @throws EE_Error
108
-     * @throws InvalidArgumentException
109
-     * @throws InvalidDataTypeException
110
-     * @throws InvalidInterfaceException
111
-     * @throws DomainException
112
-     */
113
-    public function addToFormSection(EE_Form_Section_Proper $form, array $input_settings = array())
114
-    {
115
-        $form->add_subsections(
116
-            array(
117
-                'espresso_recaptcha' => $this->getInput($input_settings),
118
-            ),
119
-            null,
120
-            false
121
-        );
122
-    }
104
+	/**
105
+	 * @param EE_Form_Section_Proper $form
106
+	 * @param array                  $input_settings
107
+	 * @throws EE_Error
108
+	 * @throws InvalidArgumentException
109
+	 * @throws InvalidDataTypeException
110
+	 * @throws InvalidInterfaceException
111
+	 * @throws DomainException
112
+	 */
113
+	public function addToFormSection(EE_Form_Section_Proper $form, array $input_settings = array())
114
+	{
115
+		$form->add_subsections(
116
+			array(
117
+				'espresso_recaptcha' => $this->getInput($input_settings),
118
+			),
119
+			null,
120
+			false
121
+		);
122
+	}
123 123
 
124 124
 
125
-    /**
126
-     * @param RequestInterface $request
127
-     * @return boolean
128
-     * @throws InvalidArgumentException
129
-     * @throws InvalidDataTypeException
130
-     * @throws InvalidInterfaceException
131
-     * @throws RuntimeException
132
-     */
133
-    public function verifyToken(RequestInterface $request)
134
-    {
135
-        static $previous_recaptcha_response = array();
136
-        $grecaptcha_response = $request->getRequestParam('g-recaptcha-response');
137
-        if ($grecaptcha_response === null) {
138
-            $this->generateError(
139
-                sprintf(
140
-                    esc_html__(
141
-                        'The "%1$s" parameter is missing.',
142
-                        'event_espresso'
143
-                    ),
144
-                    'g-recaptcha-response'
145
-                ),
146
-                true
147
-            );
148
-            return false;
149
-        }
150
-        // if this token has already been verified, then return previous response
151
-        if (isset($previous_recaptcha_response[ $grecaptcha_response ])) {
152
-            return $previous_recaptcha_response[ $grecaptcha_response ];
153
-        }
154
-        // will update to true if everything passes
155
-        $previous_recaptcha_response[ $grecaptcha_response ] = false;
156
-        $response                                            = wp_safe_remote_post(
157
-            InvisibleRecaptcha::URL_GOOGLE_RECAPTCHA_API,
158
-            array(
159
-                'body' => array(
160
-                    'secret'   => $this->config->recaptcha_privatekey,
161
-                    'response' => $grecaptcha_response,
162
-                    'remoteip' => $request->ipAddress(),
163
-                ),
164
-            )
165
-        );
166
-        if ($response instanceof WP_Error) {
167
-            $this->generateError($response->get_error_messages());
168
-            return false;
169
-        }
170
-        $results = json_decode(wp_remote_retrieve_body($response), true);
171
-        if (filter_var($results['success'], FILTER_VALIDATE_BOOLEAN) !== true) {
172
-            $errors   = array_map(
173
-                array($this, 'getErrorCode'),
174
-                $results['error-codes']
175
-            );
176
-            if (isset($results['challenge_ts'])) {
177
-                $errors[] = 'challenge timestamp: ' . $results['challenge_ts'] . '.';
178
-            }
179
-            $this->generateError(implode(' ', $errors), true);
180
-        }
181
-        $previous_recaptcha_response[ $grecaptcha_response ] = true;
182
-        add_action('shutdown', array($this, 'setSessionData'));
183
-        return true;
184
-    }
125
+	/**
126
+	 * @param RequestInterface $request
127
+	 * @return boolean
128
+	 * @throws InvalidArgumentException
129
+	 * @throws InvalidDataTypeException
130
+	 * @throws InvalidInterfaceException
131
+	 * @throws RuntimeException
132
+	 */
133
+	public function verifyToken(RequestInterface $request)
134
+	{
135
+		static $previous_recaptcha_response = array();
136
+		$grecaptcha_response = $request->getRequestParam('g-recaptcha-response');
137
+		if ($grecaptcha_response === null) {
138
+			$this->generateError(
139
+				sprintf(
140
+					esc_html__(
141
+						'The "%1$s" parameter is missing.',
142
+						'event_espresso'
143
+					),
144
+					'g-recaptcha-response'
145
+				),
146
+				true
147
+			);
148
+			return false;
149
+		}
150
+		// if this token has already been verified, then return previous response
151
+		if (isset($previous_recaptcha_response[ $grecaptcha_response ])) {
152
+			return $previous_recaptcha_response[ $grecaptcha_response ];
153
+		}
154
+		// will update to true if everything passes
155
+		$previous_recaptcha_response[ $grecaptcha_response ] = false;
156
+		$response                                            = wp_safe_remote_post(
157
+			InvisibleRecaptcha::URL_GOOGLE_RECAPTCHA_API,
158
+			array(
159
+				'body' => array(
160
+					'secret'   => $this->config->recaptcha_privatekey,
161
+					'response' => $grecaptcha_response,
162
+					'remoteip' => $request->ipAddress(),
163
+				),
164
+			)
165
+		);
166
+		if ($response instanceof WP_Error) {
167
+			$this->generateError($response->get_error_messages());
168
+			return false;
169
+		}
170
+		$results = json_decode(wp_remote_retrieve_body($response), true);
171
+		if (filter_var($results['success'], FILTER_VALIDATE_BOOLEAN) !== true) {
172
+			$errors   = array_map(
173
+				array($this, 'getErrorCode'),
174
+				$results['error-codes']
175
+			);
176
+			if (isset($results['challenge_ts'])) {
177
+				$errors[] = 'challenge timestamp: ' . $results['challenge_ts'] . '.';
178
+			}
179
+			$this->generateError(implode(' ', $errors), true);
180
+		}
181
+		$previous_recaptcha_response[ $grecaptcha_response ] = true;
182
+		add_action('shutdown', array($this, 'setSessionData'));
183
+		return true;
184
+	}
185 185
 
186 186
 
187
-    /**
188
-     * @param string $error_response
189
-     * @param bool   $show_errors
190
-     * @return void
191
-     * @throws RuntimeException
192
-     */
193
-    public function generateError($error_response = '', $show_errors = false)
194
-    {
195
-        throw new RuntimeException(
196
-            sprintf(
197
-                esc_html__(
198
-                    'We\'re sorry but an attempt to verify the form\'s reCAPTCHA has failed. %1$s %2$s Please try again.',
199
-                    'event_espresso'
200
-                ),
201
-                '<br />',
202
-                $show_errors || current_user_can('manage_options') ? $error_response : ''
203
-            )
204
-        );
205
-    }
187
+	/**
188
+	 * @param string $error_response
189
+	 * @param bool   $show_errors
190
+	 * @return void
191
+	 * @throws RuntimeException
192
+	 */
193
+	public function generateError($error_response = '', $show_errors = false)
194
+	{
195
+		throw new RuntimeException(
196
+			sprintf(
197
+				esc_html__(
198
+					'We\'re sorry but an attempt to verify the form\'s reCAPTCHA has failed. %1$s %2$s Please try again.',
199
+					'event_espresso'
200
+				),
201
+				'<br />',
202
+				$show_errors || current_user_can('manage_options') ? $error_response : ''
203
+			)
204
+		);
205
+	}
206 206
 
207 207
 
208
-    /**
209
-     * @param string $error_code
210
-     * @return string
211
-     */
212
-    public function getErrorCode(&$error_code)
213
-    {
214
-        $error_codes = array(
215
-            'missing-input-secret'   => 'The secret parameter is missing.',
216
-            'invalid-input-secret'   => 'The secret parameter is invalid or malformed.',
217
-            'missing-input-response' => 'The response parameter is missing.',
218
-            'invalid-input-response' => 'The response parameter is invalid or malformed.',
219
-            'bad-request'            => 'The request is invalid or malformed.',
220
-            'timeout-or-duplicate'   => 'The request took too long to be sent or was a duplicate of a previous request.',
221
-        );
222
-        return isset($error_codes[ $error_code ]) ? $error_codes[ $error_code ] : '';
223
-    }
208
+	/**
209
+	 * @param string $error_code
210
+	 * @return string
211
+	 */
212
+	public function getErrorCode(&$error_code)
213
+	{
214
+		$error_codes = array(
215
+			'missing-input-secret'   => 'The secret parameter is missing.',
216
+			'invalid-input-secret'   => 'The secret parameter is invalid or malformed.',
217
+			'missing-input-response' => 'The response parameter is missing.',
218
+			'invalid-input-response' => 'The response parameter is invalid or malformed.',
219
+			'bad-request'            => 'The request is invalid or malformed.',
220
+			'timeout-or-duplicate'   => 'The request took too long to be sent or was a duplicate of a previous request.',
221
+		);
222
+		return isset($error_codes[ $error_code ]) ? $error_codes[ $error_code ] : '';
223
+	}
224 224
 
225 225
 
226
-    /**
227
-     * @return array
228
-     * @throws InvalidInterfaceException
229
-     * @throws InvalidDataTypeException
230
-     * @throws InvalidArgumentException
231
-     */
232
-    public function getLocalizedVars()
233
-    {
234
-        return (array) apply_filters(
235
-            'FHEE__EventEspresso_caffeinated_modules_recaptcha_invisible_InvisibleRecaptcha__getLocalizedVars__localized_vars',
236
-            array(
237
-                'siteKey'          => $this->config->recaptcha_publickey,
238
-                'recaptcha_passed' => $this->recaptchaPassed(),
239
-                'wp_debug'         => WP_DEBUG,
240
-                'disable_submit'   => defined('EE_EVENT_QUEUE_BASE_URL'),
241
-            )
242
-        );
243
-    }
226
+	/**
227
+	 * @return array
228
+	 * @throws InvalidInterfaceException
229
+	 * @throws InvalidDataTypeException
230
+	 * @throws InvalidArgumentException
231
+	 */
232
+	public function getLocalizedVars()
233
+	{
234
+		return (array) apply_filters(
235
+			'FHEE__EventEspresso_caffeinated_modules_recaptcha_invisible_InvisibleRecaptcha__getLocalizedVars__localized_vars',
236
+			array(
237
+				'siteKey'          => $this->config->recaptcha_publickey,
238
+				'recaptcha_passed' => $this->recaptchaPassed(),
239
+				'wp_debug'         => WP_DEBUG,
240
+				'disable_submit'   => defined('EE_EVENT_QUEUE_BASE_URL'),
241
+			)
242
+		);
243
+	}
244 244
 
245 245
 
246
-    /**
247
-     * @return boolean
248
-     * @throws InvalidInterfaceException
249
-     * @throws InvalidDataTypeException
250
-     * @throws InvalidArgumentException
251
-     */
252
-    public function recaptchaPassed()
253
-    {
254
-        if ($this->recaptcha_passed !== null) {
255
-            return $this->recaptcha_passed;
256
-        }
257
-        // logged in means you have already passed a turing test of sorts
258
-        if ($this->useInvisibleRecaptcha() === false || is_user_logged_in()) {
259
-            $this->recaptcha_passed = true;
260
-            return $this->recaptcha_passed;
261
-        }
262
-        // was test already passed?
263
-        $this->recaptcha_passed = filter_var(
264
-            $this->session->get_session_data(
265
-                InvisibleRecaptcha::SESSION_DATA_KEY_RECAPTCHA_PASSED
266
-            ),
267
-            FILTER_VALIDATE_BOOLEAN
268
-        );
269
-        return $this->recaptcha_passed;
270
-    }
246
+	/**
247
+	 * @return boolean
248
+	 * @throws InvalidInterfaceException
249
+	 * @throws InvalidDataTypeException
250
+	 * @throws InvalidArgumentException
251
+	 */
252
+	public function recaptchaPassed()
253
+	{
254
+		if ($this->recaptcha_passed !== null) {
255
+			return $this->recaptcha_passed;
256
+		}
257
+		// logged in means you have already passed a turing test of sorts
258
+		if ($this->useInvisibleRecaptcha() === false || is_user_logged_in()) {
259
+			$this->recaptcha_passed = true;
260
+			return $this->recaptcha_passed;
261
+		}
262
+		// was test already passed?
263
+		$this->recaptcha_passed = filter_var(
264
+			$this->session->get_session_data(
265
+				InvisibleRecaptcha::SESSION_DATA_KEY_RECAPTCHA_PASSED
266
+			),
267
+			FILTER_VALIDATE_BOOLEAN
268
+		);
269
+		return $this->recaptcha_passed;
270
+	}
271 271
 
272 272
 
273
-    /**
274
-     * @throws InvalidArgumentException
275
-     * @throws InvalidDataTypeException
276
-     * @throws InvalidInterfaceException
277
-     */
278
-    public function setSessionData()
279
-    {
280
-        $this->session->set_session_data(
281
-            array(InvisibleRecaptcha::SESSION_DATA_KEY_RECAPTCHA_PASSED => true)
282
-        );
283
-    }
273
+	/**
274
+	 * @throws InvalidArgumentException
275
+	 * @throws InvalidDataTypeException
276
+	 * @throws InvalidInterfaceException
277
+	 */
278
+	public function setSessionData()
279
+	{
280
+		$this->session->set_session_data(
281
+			array(InvisibleRecaptcha::SESSION_DATA_KEY_RECAPTCHA_PASSED => true)
282
+		);
283
+	}
284 284
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -148,11 +148,11 @@  discard block
 block discarded – undo
148 148
             return false;
149 149
         }
150 150
         // if this token has already been verified, then return previous response
151
-        if (isset($previous_recaptcha_response[ $grecaptcha_response ])) {
152
-            return $previous_recaptcha_response[ $grecaptcha_response ];
151
+        if (isset($previous_recaptcha_response[$grecaptcha_response])) {
152
+            return $previous_recaptcha_response[$grecaptcha_response];
153 153
         }
154 154
         // will update to true if everything passes
155
-        $previous_recaptcha_response[ $grecaptcha_response ] = false;
155
+        $previous_recaptcha_response[$grecaptcha_response] = false;
156 156
         $response                                            = wp_safe_remote_post(
157 157
             InvisibleRecaptcha::URL_GOOGLE_RECAPTCHA_API,
158 158
             array(
@@ -169,16 +169,16 @@  discard block
 block discarded – undo
169 169
         }
170 170
         $results = json_decode(wp_remote_retrieve_body($response), true);
171 171
         if (filter_var($results['success'], FILTER_VALIDATE_BOOLEAN) !== true) {
172
-            $errors   = array_map(
172
+            $errors = array_map(
173 173
                 array($this, 'getErrorCode'),
174 174
                 $results['error-codes']
175 175
             );
176 176
             if (isset($results['challenge_ts'])) {
177
-                $errors[] = 'challenge timestamp: ' . $results['challenge_ts'] . '.';
177
+                $errors[] = 'challenge timestamp: '.$results['challenge_ts'].'.';
178 178
             }
179 179
             $this->generateError(implode(' ', $errors), true);
180 180
         }
181
-        $previous_recaptcha_response[ $grecaptcha_response ] = true;
181
+        $previous_recaptcha_response[$grecaptcha_response] = true;
182 182
         add_action('shutdown', array($this, 'setSessionData'));
183 183
         return true;
184 184
     }
@@ -219,7 +219,7 @@  discard block
 block discarded – undo
219 219
             'bad-request'            => 'The request is invalid or malformed.',
220 220
             'timeout-or-duplicate'   => 'The request took too long to be sent or was a duplicate of a previous request.',
221 221
         );
222
-        return isset($error_codes[ $error_code ]) ? $error_codes[ $error_code ] : '';
222
+        return isset($error_codes[$error_code]) ? $error_codes[$error_code] : '';
223 223
     }
224 224
 
225 225
 
Please login to merge, or discard this patch.
core/libraries/rest_api/controllers/model/Read.php 2 patches
Indentation   +1554 added lines, -1554 removed lines patch added patch discarded remove patch
@@ -45,1558 +45,1558 @@
 block discarded – undo
45 45
 {
46 46
 
47 47
 
48
-    /**
49
-     * @var CalculatedModelFields
50
-     */
51
-    protected $fields_calculator;
52
-
53
-
54
-    /**
55
-     * Read constructor.
56
-     * @param CalculatedModelFields $fields_calculator
57
-     */
58
-    public function __construct(CalculatedModelFields $fields_calculator)
59
-    {
60
-        parent::__construct();
61
-        $this->fields_calculator = $fields_calculator;
62
-    }
63
-
64
-
65
-    /**
66
-     * Handles requests to get all (or a filtered subset) of entities for a particular model
67
-     *
68
-     * @param WP_REST_Request $request
69
-     * @param string $version
70
-     * @param string $model_name
71
-     * @return WP_REST_Response|WP_Error
72
-     * @throws InvalidArgumentException
73
-     * @throws InvalidDataTypeException
74
-     * @throws InvalidInterfaceException
75
-     */
76
-    public static function handleRequestGetAll(WP_REST_Request $request, $version, $model_name)
77
-    {
78
-        $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
79
-        try {
80
-            $controller->setRequestedVersion($version);
81
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
82
-                return $controller->sendResponse(
83
-                    new WP_Error(
84
-                        'endpoint_parsing_error',
85
-                        sprintf(
86
-                            __(
87
-                                'There is no model for endpoint %s. Please contact event espresso support',
88
-                                'event_espresso'
89
-                            ),
90
-                            $model_name
91
-                        )
92
-                    )
93
-                );
94
-            }
95
-            return $controller->sendResponse(
96
-                $controller->getEntitiesFromModel(
97
-                    $controller->getModelVersionInfo()->loadModel($model_name),
98
-                    $request
99
-                )
100
-            );
101
-        } catch (Exception $e) {
102
-            return $controller->sendResponse($e);
103
-        }
104
-    }
105
-
106
-
107
-    /**
108
-     * Prepares and returns schema for any OPTIONS request.
109
-     *
110
-     * @param string $version The API endpoint version being used.
111
-     * @param string $model_name Something like `Event` or `Registration`
112
-     * @return array
113
-     * @throws InvalidArgumentException
114
-     * @throws InvalidDataTypeException
115
-     * @throws InvalidInterfaceException
116
-     */
117
-    public static function handleSchemaRequest($version, $model_name)
118
-    {
119
-        $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
120
-        try {
121
-            $controller->setRequestedVersion($version);
122
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
123
-                return array();
124
-            }
125
-            // get the model for this version
126
-            $model = $controller->getModelVersionInfo()->loadModel($model_name);
127
-            $model_schema = new JsonModelSchema($model, LoaderFactory::getLoader()->getShared('EventEspresso\core\libraries\rest_api\CalculatedModelFields'));
128
-            return $model_schema->getModelSchemaForRelations(
129
-                $controller->getModelVersionInfo()->relationSettings($model),
130
-                $controller->customizeSchemaForRestResponse(
131
-                    $model,
132
-                    $model_schema->getModelSchemaForFields(
133
-                        $controller->getModelVersionInfo()->fieldsOnModelInThisVersion($model),
134
-                        $model_schema->getInitialSchemaStructure()
135
-                    )
136
-                )
137
-            );
138
-        } catch (Exception $e) {
139
-            return array();
140
-        }
141
-    }
142
-
143
-
144
-    /**
145
-     * This loops through each field in the given schema for the model and does the following:
146
-     * - add any extra fields that are REST API specific and related to existing fields.
147
-     * - transform default values into the correct format for a REST API response.
148
-     *
149
-     * @param EEM_Base $model
150
-     * @param array    $schema
151
-     * @return array  The final schema.
152
-     */
153
-    protected function customizeSchemaForRestResponse(EEM_Base $model, array $schema)
154
-    {
155
-        foreach ($this->getModelVersionInfo()->fieldsOnModelInThisVersion($model) as $field_name => $field) {
156
-            $schema = $this->translateDefaultsForRestResponse(
157
-                $field_name,
158
-                $field,
159
-                $this->maybeAddExtraFieldsToSchema($field_name, $field, $schema)
160
-            );
161
-        }
162
-        return $schema;
163
-    }
164
-
165
-
166
-    /**
167
-     * This is used to ensure that the 'default' value set in the schema response is formatted correctly for the REST
168
-     * response.
169
-     *
170
-     * @param                      $field_name
171
-     * @param EE_Model_Field_Base  $field
172
-     * @param array                $schema
173
-     * @return array
174
-     * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
175
-     * did, let's know about it ASAP, so let the exception bubble up)
176
-     */
177
-    protected function translateDefaultsForRestResponse($field_name, EE_Model_Field_Base $field, array $schema)
178
-    {
179
-        if (isset($schema['properties'][ $field_name ]['default'])) {
180
-            if (is_array($schema['properties'][ $field_name ]['default'])) {
181
-                foreach ($schema['properties'][ $field_name ]['default'] as $default_key => $default_value) {
182
-                    if ($default_key === 'raw') {
183
-                        $schema['properties'][ $field_name ]['default'][ $default_key ] =
184
-                            ModelDataTranslator::prepareFieldValueForJson(
185
-                                $field,
186
-                                $default_value,
187
-                                $this->getModelVersionInfo()->requestedVersion()
188
-                            );
189
-                    }
190
-                }
191
-            } else {
192
-                $schema['properties'][ $field_name ]['default'] = ModelDataTranslator::prepareFieldValueForJson(
193
-                    $field,
194
-                    $schema['properties'][ $field_name ]['default'],
195
-                    $this->getModelVersionInfo()->requestedVersion()
196
-                );
197
-            }
198
-        }
199
-        return $schema;
200
-    }
201
-
202
-
203
-    /**
204
-     * Adds additional fields to the schema
205
-     * The REST API returns a GMT value field for each datetime field in the resource.  Thus the description about this
206
-     * needs to be added to the schema.
207
-     *
208
-     * @param                      $field_name
209
-     * @param EE_Model_Field_Base  $field
210
-     * @param array                $schema
211
-     * @return array
212
-     */
213
-    protected function maybeAddExtraFieldsToSchema($field_name, EE_Model_Field_Base $field, array $schema)
214
-    {
215
-        if ($field instanceof EE_Datetime_Field) {
216
-            $schema['properties'][ $field_name . '_gmt' ] = $field->getSchema();
217
-            // modify the description
218
-            $schema['properties'][ $field_name . '_gmt' ]['description'] = sprintf(
219
-                esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
220
-                wp_specialchars_decode($field->get_nicename(), ENT_QUOTES)
221
-            );
222
-        }
223
-        return $schema;
224
-    }
225
-
226
-
227
-    /**
228
-     * Used to figure out the route from the request when a `WP_REST_Request` object is not available
229
-     *
230
-     * @return string
231
-     */
232
-    protected function getRouteFromRequest()
233
-    {
234
-        if (isset($GLOBALS['wp'])
235
-            && $GLOBALS['wp'] instanceof \WP
236
-            && isset($GLOBALS['wp']->query_vars['rest_route'])
237
-        ) {
238
-            return $GLOBALS['wp']->query_vars['rest_route'];
239
-        } else {
240
-            return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
241
-        }
242
-    }
243
-
244
-
245
-    /**
246
-     * Gets a single entity related to the model indicated in the path and its id
247
-     *
248
-     * @param WP_REST_Request $request
249
-     * @param string $version
250
-     * @param string $model_name
251
-     * @return WP_REST_Response|WP_Error
252
-     * @throws InvalidDataTypeException
253
-     * @throws InvalidInterfaceException
254
-     * @throws InvalidArgumentException
255
-     */
256
-    public static function handleRequestGetOne(WP_REST_Request $request, $version, $model_name)
257
-    {
258
-        $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
259
-        try {
260
-            $controller->setRequestedVersion($version);
261
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
262
-                return $controller->sendResponse(
263
-                    new WP_Error(
264
-                        'endpoint_parsing_error',
265
-                        sprintf(
266
-                            __(
267
-                                'There is no model for endpoint %s. Please contact event espresso support',
268
-                                'event_espresso'
269
-                            ),
270
-                            $model_name
271
-                        )
272
-                    )
273
-                );
274
-            }
275
-            return $controller->sendResponse(
276
-                $controller->getEntityFromModel(
277
-                    $controller->getModelVersionInfo()->loadModel($model_name),
278
-                    $request
279
-                )
280
-            );
281
-        } catch (Exception $e) {
282
-            return $controller->sendResponse($e);
283
-        }
284
-    }
285
-
286
-
287
-    /**
288
-     * Gets all the related entities (or if its a belongs-to relation just the one)
289
-     * to the item with the given id
290
-     *
291
-     * @param WP_REST_Request $request
292
-     * @param string $version
293
-     * @param string $model_name
294
-     * @param string $related_model_name
295
-     * @return WP_REST_Response|WP_Error
296
-     * @throws InvalidDataTypeException
297
-     * @throws InvalidInterfaceException
298
-     * @throws InvalidArgumentException
299
-     */
300
-    public static function handleRequestGetRelated(
301
-        WP_REST_Request $request,
302
-        $version,
303
-        $model_name,
304
-        $related_model_name
305
-    ) {
306
-        $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
307
-        try {
308
-            $controller->setRequestedVersion($version);
309
-            $main_model = $controller->validateModel($model_name);
310
-            $controller->validateModel($related_model_name);
311
-            return $controller->sendResponse(
312
-                $controller->getEntitiesFromRelation(
313
-                    $request->get_param('id'),
314
-                    $main_model->related_settings_for($related_model_name),
315
-                    $request
316
-                )
317
-            );
318
-        } catch (Exception $e) {
319
-            return $controller->sendResponse($e);
320
-        }
321
-    }
322
-
323
-
324
-    /**
325
-     * Gets a collection for the given model and filters
326
-     *
327
-     * @param EEM_Base $model
328
-     * @param WP_REST_Request $request
329
-     * @return array
330
-     * @throws EE_Error
331
-     * @throws InvalidArgumentException
332
-     * @throws InvalidDataTypeException
333
-     * @throws InvalidInterfaceException
334
-     * @throws ReflectionException
335
-     * @throws RestException
336
-     */
337
-    public function getEntitiesFromModel($model, $request)
338
-    {
339
-        $query_params = $this->createModelQueryParams($model, $request->get_params());
340
-        if (! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
341
-            $model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
342
-            throw new RestException(
343
-                sprintf('rest_%s_cannot_list', $model_name_plural),
344
-                sprintf(
345
-                    __('Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso'),
346
-                    $model_name_plural,
347
-                    Capabilities::getMissingPermissionsString($model, $query_params['caps'])
348
-                ),
349
-                array('status' => 403)
350
-            );
351
-        }
352
-        if (! $request->get_header('no_rest_headers')) {
353
-            $this->setHeadersFromQueryParams($model, $query_params);
354
-        }
355
-        /** @type array $results */
356
-        $results = $model->get_all_wpdb_results($query_params);
357
-        $nice_results = array();
358
-        foreach ($results as $result) {
359
-            $nice_results[] =  $this->createEntityFromWpdbResult(
360
-                $model,
361
-                $result,
362
-                $request
363
-            );
364
-        }
365
-        return $nice_results;
366
-    }
367
-
368
-
369
-    /**
370
-     * Gets the collection for given relation object
371
-     * The same as Read::get_entities_from_model(), except if the relation
372
-     * is a HABTM relation, in which case it merges any non-foreign-key fields from
373
-     * the join-model-object into the results
374
-     *
375
-     * @param array $primary_model_query_params query params for finding the item from which
376
-     *                                                            relations will be based
377
-     * @param \EE_Model_Relation_Base $relation
378
-     * @param WP_REST_Request $request
379
-     * @return array
380
-     * @throws EE_Error
381
-     * @throws InvalidArgumentException
382
-     * @throws InvalidDataTypeException
383
-     * @throws InvalidInterfaceException
384
-     * @throws ReflectionException
385
-     * @throws RestException
386
-     * @throws \EventEspresso\core\exceptions\ModelConfigurationException
387
-     */
388
-    protected function getEntitiesFromRelationUsingModelQueryParams($primary_model_query_params, $relation, $request)
389
-    {
390
-        $context = $this->validateContext($request->get_param('caps'));
391
-        $model = $relation->get_this_model();
392
-        $related_model = $relation->get_other_model();
393
-        if (! isset($primary_model_query_params[0])) {
394
-            $primary_model_query_params[0] = array();
395
-        }
396
-        // check if they can access the 1st model object
397
-        $primary_model_query_params = array(
398
-            0       => $primary_model_query_params[0],
399
-            'limit' => 1,
400
-        );
401
-        if ($model instanceof EEM_Soft_Delete_Base) {
402
-            $primary_model_query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included(
403
-                $primary_model_query_params
404
-            );
405
-        }
406
-        $restricted_query_params = $primary_model_query_params;
407
-        $restricted_query_params['caps'] = $context;
408
-        $restricted_query_params['limit'] = 1;
409
-        $this->setDebugInfo('main model query params', $restricted_query_params);
410
-        $this->setDebugInfo('missing caps', Capabilities::getMissingPermissionsString($related_model, $context));
411
-        $primary_model_rows = $model->get_all_wpdb_results($restricted_query_params);
412
-        $primary_model_row = null;
413
-        if (is_array($primary_model_rows)) {
414
-            $primary_model_row = reset($primary_model_rows);
415
-        }
416
-        if (! (
417
-            Capabilities::currentUserHasPartialAccessTo($related_model, $context)
418
-            && $primary_model_row
419
-        )
420
-        ) {
421
-            if ($relation instanceof EE_Belongs_To_Relation) {
422
-                $related_model_name_maybe_plural = strtolower($related_model->get_this_model_name());
423
-            } else {
424
-                $related_model_name_maybe_plural = EEH_Inflector::pluralize_and_lower(
425
-                    $related_model->get_this_model_name()
426
-                );
427
-            }
428
-            throw new RestException(
429
-                sprintf('rest_%s_cannot_list', $related_model_name_maybe_plural),
430
-                sprintf(
431
-                    __(
432
-                        'Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s',
433
-                        'event_espresso'
434
-                    ),
435
-                    $related_model_name_maybe_plural,
436
-                    $relation->get_this_model()->get_this_model_name(),
437
-                    implode(
438
-                        ',',
439
-                        array_keys(
440
-                            Capabilities::getMissingPermissions($related_model, $context)
441
-                        )
442
-                    )
443
-                ),
444
-                array('status' => 403)
445
-            );
446
-        }
447
-
448
-        $this->checkPassword(
449
-            $model,
450
-            $primary_model_row,
451
-            $restricted_query_params,
452
-            $request
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
-     * Gets the collection for given relation object
501
-     * The same as Read::get_entities_from_model(), except if the relation
502
-     * is a HABTM relation, in which case it merges any non-foreign-key fields from
503
-     * the join-model-object into the results
504
-     *
505
-     * @param string                  $id the ID of the thing we are fetching related stuff from
506
-     * @param \EE_Model_Relation_Base $relation
507
-     * @param WP_REST_Request         $request
508
-     * @return array
509
-     * @throws EE_Error
510
-     */
511
-    public function getEntitiesFromRelation($id, $relation, $request)
512
-    {
513
-        if (! $relation->get_this_model()->has_primary_key_field()) {
514
-            throw new EE_Error(
515
-                sprintf(
516
-                    __(
517
-                    // @codingStandardsIgnoreStart
518
-                        'Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
519
-                        // @codingStandardsIgnoreEnd
520
-                        'event_espresso'
521
-                    ),
522
-                    $relation->get_this_model()->get_this_model_name()
523
-                )
524
-            );
525
-        }
526
-        // can we edit that main item?
527
-        // if not, show nothing but an error
528
-        // otherwise, please proceed
529
-        return $this->getEntitiesFromRelationUsingModelQueryParams(
530
-            array(
531
-                array(
532
-                    $relation->get_this_model()->primary_key_name() => $id,
533
-                ),
534
-            ),
535
-            $relation,
536
-            $request
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
-     * Changes database results into REST API entities
582
-     *
583
-     * @param EEM_Base $model
584
-     * @param array $db_row like results from $wpdb->get_results()
585
-     * @param WP_REST_Request $rest_request
586
-     * @param string $deprecated no longer used
587
-     * @return array ready for being converted into json for sending to client
588
-     * @throws EE_Error
589
-     * @throws RestException
590
-     * @throws InvalidDataTypeException
591
-     * @throws InvalidInterfaceException
592
-     * @throws InvalidArgumentException
593
-     * @throws ReflectionException
594
-     */
595
-    public function createEntityFromWpdbResult($model, $db_row, $rest_request, $deprecated = null)
596
-    {
597
-        if (! $rest_request instanceof WP_REST_Request) {
598
-            // ok so this was called in the old style, where the 3rd arg was
599
-            // $include, and the 4th arg was $context
600
-            // now setup the request just to avoid fatal errors, although we won't be able
601
-            // to truly make use of it because it's kinda devoid of info
602
-            $rest_request = new WP_REST_Request();
603
-            $rest_request->set_param('include', $rest_request);
604
-            $rest_request->set_param('caps', $deprecated);
605
-        }
606
-        if ($rest_request->get_param('caps') == null) {
607
-            $rest_request->set_param('caps', EEM_Base::caps_read);
608
-        }
609
-        $current_user_full_access_to_entity = $model->currentUserCan(
610
-            EEM_Base::caps_read_admin,
611
-            $model->deduce_fields_n_values_from_cols_n_values($db_row)
612
-        );
613
-        $entity_array = $this->createBareEntityFromWpdbResults($model, $db_row);
614
-        $entity_array = $this->addExtraFields($model, $db_row, $entity_array);
615
-        $entity_array['_links'] = $this->getEntityLinks($model, $db_row, $entity_array);
616
-        // when it's a regular read request for a model with a password and the password wasn't provided
617
-        // remove the password protected fields
618
-        $has_protected_fields = false;
619
-        try {
620
-            $this->checkPassword(
621
-                $model,
622
-                $db_row,
623
-                $model->alter_query_params_to_restrict_by_ID(
624
-                    $model->get_index_primary_key_string(
625
-                        $model->deduce_fields_n_values_from_cols_n_values($db_row)
626
-                    )
627
-                ),
628
-                $rest_request
629
-            );
630
-        } catch (RestPasswordRequiredException $e) {
631
-            if ($model->hasPassword()) {
632
-                // just remove protected fields
633
-                $has_protected_fields = true;
634
-                $entity_array = Capabilities::filterOutPasswordProtectedFields(
635
-                    $entity_array,
636
-                    $model,
637
-                    $this->getModelVersionInfo()
638
-                );
639
-            } else {
640
-                // that's a problem. None of this should be accessible if no password was provided
641
-                throw $e;
642
-            }
643
-        }
644
-
645
-        $entity_array['_calculated_fields'] = $this->getEntityCalculations($model, $db_row, $rest_request, $has_protected_fields);
646
-        $entity_array = apply_filters(
647
-            'FHEE__Read__create_entity_from_wpdb_results__entity_before_including_requested_models',
648
-            $entity_array,
649
-            $model,
650
-            $rest_request->get_param('caps'),
651
-            $rest_request,
652
-            $this
653
-        );
654
-        // add an empty protected property for now. If it's still around after we remove everything the request didn't
655
-        // want, we'll populate it then. k?
656
-        $entity_array['_protected'] = array();
657
-        // remove any properties the request didn't want. This way _protected won't bother mentioning them
658
-        $entity_array = $this->includeOnlyRequestedProperties($model, $rest_request, $entity_array);
659
-        $entity_array = $this->includeRequestedModels($model, $rest_request, $entity_array, $db_row, $has_protected_fields);
660
-        // if they still wanted the _protected property, add it.
661
-        if (isset($entity_array['_protected'])) {
662
-            $entity_array = $this->addProtectedProperty($model, $entity_array, $has_protected_fields);
663
-        }
664
-        $entity_array = apply_filters(
665
-            'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
666
-            $entity_array,
667
-            $model,
668
-            $rest_request->get_param('caps'),
669
-            $rest_request,
670
-            $this
671
-        );
672
-        if (! $current_user_full_access_to_entity) {
673
-            $result_without_inaccessible_fields = Capabilities::filterOutInaccessibleEntityFields(
674
-                $entity_array,
675
-                $model,
676
-                $rest_request->get_param('caps'),
677
-                $this->getModelVersionInfo()
678
-            );
679
-        } else {
680
-            $result_without_inaccessible_fields = $entity_array;
681
-        }
682
-        $this->setDebugInfo(
683
-            'inaccessible fields',
684
-            array_keys(array_diff_key((array) $entity_array, (array) $result_without_inaccessible_fields))
685
-        );
686
-        return apply_filters(
687
-            'FHEE__Read__create_entity_from_wpdb_results__entity_return',
688
-            $result_without_inaccessible_fields,
689
-            $model,
690
-            $rest_request->get_param('caps')
691
-        );
692
-    }
693
-
694
-    /**
695
-     * Returns an array describing which fields can be protected, and which actually were removed this request
696
-     * @since 4.9.74.p
697
-     * @param $model
698
-     * @param $results_so_far
699
-     * @param $protected
700
-     * @return array results
701
-     */
702
-    protected function addProtectedProperty(EEM_Base $model, $results_so_far, $protected)
703
-    {
704
-        if (! $model->hasPassword() || ! $protected) {
705
-            return $results_so_far;
706
-        }
707
-        $password_field = $model->getPasswordField();
708
-        $all_protected = array_merge(
709
-            array($password_field->get_name()),
710
-            $password_field->protectedFields()
711
-        );
712
-        $fields_included = array_keys($results_so_far);
713
-        $fields_included = array_intersect(
714
-            $all_protected,
715
-            $fields_included
716
-        );
717
-        foreach ($fields_included as $field_name) {
718
-            $results_so_far['_protected'][] = $field_name ;
719
-        }
720
-        return $results_so_far;
721
-    }
722
-
723
-    /**
724
-     * Creates a REST entity array (JSON object we're going to return in the response, but
725
-     * for now still a PHP array, but soon enough we'll call json_encode on it, don't worry),
726
-     * from $wpdb->get_row( $sql, ARRAY_A)
727
-     *
728
-     * @param EEM_Base $model
729
-     * @param array    $db_row
730
-     * @return array entity mostly ready for converting to JSON and sending in the response
731
-     */
732
-    protected function createBareEntityFromWpdbResults(EEM_Base $model, $db_row)
733
-    {
734
-        $result = $model->deduce_fields_n_values_from_cols_n_values($db_row);
735
-        $result = array_intersect_key(
736
-            $result,
737
-            $this->getModelVersionInfo()->fieldsOnModelInThisVersion($model)
738
-        );
739
-        // if this is a CPT, we need to set the global $post to it,
740
-        // otherwise shortcodes etc won't work properly while rendering it
741
-        if ($model instanceof \EEM_CPT_Base) {
742
-            $do_chevy_shuffle = true;
743
-        } else {
744
-            $do_chevy_shuffle = false;
745
-        }
746
-        if ($do_chevy_shuffle) {
747
-            global $post;
748
-            $old_post = $post;
749
-            $post = get_post($result[ $model->primary_key_name() ]);
750
-            if (! $post instanceof \WP_Post) {
751
-                // well that's weird, because $result is what we JUST fetched from the database
752
-                throw new RestException(
753
-                    'error_fetching_post_from_database_results',
754
-                    esc_html__(
755
-                        'An item was retrieved from the database but it\'s not a WP_Post like it should be.',
756
-                        'event_espresso'
757
-                    )
758
-                );
759
-            }
760
-            $model_object_classname = 'EE_' . $model->get_this_model_name();
761
-            $post->{$model_object_classname} = \EE_Registry::instance()->load_class(
762
-                $model_object_classname,
763
-                $result,
764
-                false,
765
-                false
766
-            );
767
-        }
768
-        foreach ($result as $field_name => $field_value) {
769
-            $field_obj = $model->field_settings_for($field_name);
770
-            if ($this->isSubclassOfOne($field_obj, $this->getModelVersionInfo()->fieldsIgnored())) {
771
-                unset($result[ $field_name ]);
772
-            } elseif ($this->isSubclassOfOne(
773
-                $field_obj,
774
-                $this->getModelVersionInfo()->fieldsThatHaveRenderedFormat()
775
-            )
776
-            ) {
777
-                $result[ $field_name ] = array(
778
-                    'raw'      => $this->prepareFieldObjValueForJson($field_obj, $field_value),
779
-                    'rendered' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
780
-                );
781
-            } elseif ($this->isSubclassOfOne(
782
-                $field_obj,
783
-                $this->getModelVersionInfo()->fieldsThatHavePrettyFormat()
784
-            )
785
-            ) {
786
-                $result[ $field_name ] = array(
787
-                    'raw'    => $this->prepareFieldObjValueForJson($field_obj, $field_value),
788
-                    'pretty' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
789
-                );
790
-            } elseif ($field_obj instanceof \EE_Datetime_Field) {
791
-                $field_value = $field_obj->prepare_for_set_from_db($field_value);
792
-                // if the value is null, but we're not supposed to permit null, then set to the field's default
793
-                if (is_null($field_value)) {
794
-                    $field_value = $field_obj->getDefaultDateTimeObj();
795
-                }
796
-                if (is_null($field_value)) {
797
-                    $gmt_date = $local_date = ModelDataTranslator::prepareFieldValuesForJson(
798
-                        $field_obj,
799
-                        $field_value,
800
-                        $this->getModelVersionInfo()->requestedVersion()
801
-                    );
802
-                } else {
803
-                    $timezone = $field_value->getTimezone();
804
-                    EEH_DTT_Helper::setTimezone($field_value, new DateTimeZone('UTC'));
805
-                    $gmt_date = ModelDataTranslator::prepareFieldValuesForJson(
806
-                        $field_obj,
807
-                        $field_value,
808
-                        $this->getModelVersionInfo()->requestedVersion()
809
-                    );
810
-                    EEH_DTT_Helper::setTimezone($field_value, $timezone);
811
-                    $local_date = ModelDataTranslator::prepareFieldValuesForJson(
812
-                        $field_obj,
813
-                        $field_value,
814
-                        $this->getModelVersionInfo()->requestedVersion()
815
-                    );
816
-                }
817
-                $result[ $field_name . '_gmt' ] = $gmt_date;
818
-                $result[ $field_name ] = $local_date;
819
-            } else {
820
-                $result[ $field_name ] = $this->prepareFieldObjValueForJson($field_obj, $field_value);
821
-            }
822
-        }
823
-        if ($do_chevy_shuffle) {
824
-            $post = $old_post;
825
-        }
826
-        return $result;
827
-    }
828
-
829
-
830
-    /**
831
-     * Takes a value all the way from the DB representation, to the model object's representation, to the
832
-     * user-facing PHP representation, to the REST API representation. (Assumes you've already taken from the DB
833
-     * representation using $field_obj->prepare_for_set_from_db())
834
-     *
835
-     * @param EE_Model_Field_Base $field_obj
836
-     * @param mixed               $value  as it's stored on a model object
837
-     * @param string              $format valid values are 'normal' (default), 'pretty', 'datetime_obj'
838
-     * @return mixed
839
-     * @throws ObjectDetectedException if $value contains a PHP object
840
-     */
841
-    protected function prepareFieldObjValueForJson(EE_Model_Field_Base $field_obj, $value, $format = 'normal')
842
-    {
843
-        $value = $field_obj->prepare_for_set_from_db($value);
844
-        switch ($format) {
845
-            case 'pretty':
846
-                $value = $field_obj->prepare_for_pretty_echoing($value);
847
-                break;
848
-            case 'normal':
849
-            default:
850
-                $value = $field_obj->prepare_for_get($value);
851
-                break;
852
-        }
853
-        return ModelDataTranslator::prepareFieldValuesForJson(
854
-            $field_obj,
855
-            $value,
856
-            $this->getModelVersionInfo()->requestedVersion()
857
-        );
858
-    }
859
-
860
-
861
-    /**
862
-     * Adds a few extra fields to the entity response
863
-     *
864
-     * @param EEM_Base $model
865
-     * @param array    $db_row
866
-     * @param array    $entity_array
867
-     * @return array modified entity
868
-     */
869
-    protected function addExtraFields(EEM_Base $model, $db_row, $entity_array)
870
-    {
871
-        if ($model instanceof EEM_CPT_Base) {
872
-            $entity_array['link'] = get_permalink($db_row[ $model->get_primary_key_field()->get_qualified_column() ]);
873
-        }
874
-        return $entity_array;
875
-    }
876
-
877
-
878
-    /**
879
-     * Gets links we want to add to the response
880
-     *
881
-     * @global \WP_REST_Server $wp_rest_server
882
-     * @param EEM_Base         $model
883
-     * @param array            $db_row
884
-     * @param array            $entity_array
885
-     * @return array the _links item in the entity
886
-     */
887
-    protected function getEntityLinks($model, $db_row, $entity_array)
888
-    {
889
-        // add basic links
890
-        $links = array();
891
-        if ($model->has_primary_key_field()) {
892
-            $links['self'] = array(
893
-                array(
894
-                    'href' => $this->getVersionedLinkTo(
895
-                        EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
896
-                        . '/'
897
-                        . $entity_array[ $model->primary_key_name() ]
898
-                    ),
899
-                ),
900
-            );
901
-        }
902
-        $links['collection'] = array(
903
-            array(
904
-                'href' => $this->getVersionedLinkTo(
905
-                    EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
906
-                ),
907
-            ),
908
-        );
909
-        // add links to related models
910
-        if ($model->has_primary_key_field()) {
911
-            foreach ($this->getModelVersionInfo()->relationSettings($model) as $relation_name => $relation_obj) {
912
-                $related_model_part = Read::getRelatedEntityName($relation_name, $relation_obj);
913
-                $links[ EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part ] = array(
914
-                    array(
915
-                        'href'   => $this->getVersionedLinkTo(
916
-                            EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
917
-                            . '/'
918
-                            . $entity_array[ $model->primary_key_name() ]
919
-                            . '/'
920
-                            . $related_model_part
921
-                        ),
922
-                        'single' => $relation_obj instanceof EE_Belongs_To_Relation ? true : false,
923
-                    ),
924
-                );
925
-            }
926
-        }
927
-        return $links;
928
-    }
929
-
930
-
931
-    /**
932
-     * Adds the included models indicated in the request to the entity provided
933
-     *
934
-     * @param EEM_Base $model
935
-     * @param WP_REST_Request $rest_request
936
-     * @param array $entity_array
937
-     * @param array $db_row
938
-     * @param boolean $included_items_protected if the original item is password protected, don't include any related models.
939
-     * @return array the modified entity
940
-     * @throws RestException
941
-     */
942
-    protected function includeRequestedModels(
943
-        EEM_Base $model,
944
-        WP_REST_Request $rest_request,
945
-        $entity_array,
946
-        $db_row = array(),
947
-        $included_items_protected = false
948
-    ) {
949
-        // if $db_row not included, hope the entity array has what we need
950
-        if (! $db_row) {
951
-            $db_row = $entity_array;
952
-        }
953
-        $relation_settings = $this->getModelVersionInfo()->relationSettings($model);
954
-        foreach ($relation_settings as $relation_name => $relation_obj) {
955
-            $related_fields_to_include = $this->explodeAndGetItemsPrefixedWith(
956
-                $rest_request->get_param('include'),
957
-                $relation_name
958
-            );
959
-            $related_fields_to_calculate = $this->explodeAndGetItemsPrefixedWith(
960
-                $rest_request->get_param('calculate'),
961
-                $relation_name
962
-            );
963
-            // did they specify they wanted to include a related model, or
964
-            // specific fields from a related model?
965
-            // or did they specify to calculate a field from a related model?
966
-            if ($related_fields_to_include || $related_fields_to_calculate) {
967
-                // if so, we should include at least some part of the related model
968
-                $pretend_related_request = new WP_REST_Request();
969
-                $pretend_related_request->set_query_params(
970
-                    array(
971
-                        'caps'      => $rest_request->get_param('caps'),
972
-                        'include'   => $related_fields_to_include,
973
-                        'calculate' => $related_fields_to_calculate,
974
-                        'password' => $rest_request->get_param('password')
975
-                    )
976
-                );
977
-                $pretend_related_request->add_header('no_rest_headers', true);
978
-                $primary_model_query_params = $model->alter_query_params_to_restrict_by_ID(
979
-                    $model->get_index_primary_key_string(
980
-                        $model->deduce_fields_n_values_from_cols_n_values($db_row)
981
-                    )
982
-                );
983
-                if (! $included_items_protected) {
984
-                    try {
985
-                        $related_results = $this->getEntitiesFromRelationUsingModelQueryParams(
986
-                            $primary_model_query_params,
987
-                            $relation_obj,
988
-                            $pretend_related_request
989
-                        );
990
-                    } catch (RestException $e) {
991
-                        $related_results = null;
992
-                    }
993
-                } else {
994
-                    // they're protected, hide them.
995
-                    $related_results = null;
996
-                    $entity_array['_protected'][] = Read::getRelatedEntityName($relation_name, $relation_obj);
997
-                }
998
-                if ($related_results instanceof WP_Error || $related_results === null) {
999
-                    $related_results = $relation_obj instanceof EE_Belongs_To_Relation ? null : array();
1000
-                }
1001
-                $entity_array[ Read::getRelatedEntityName($relation_name, $relation_obj) ] = $related_results;
1002
-            }
1003
-        }
1004
-        return $entity_array;
1005
-    }
1006
-
1007
-    /**
1008
-     * If the user has requested only specific properties (including meta properties like _links or _protected)
1009
-     * remove everything else.
1010
-     * @since 4.9.74.p
1011
-     * @param EEM_Base $model
1012
-     * @param WP_REST_Request $rest_request
1013
-     * @param $entity_array
1014
-     * @return array
1015
-     * @throws EE_Error
1016
-     */
1017
-    protected function includeOnlyRequestedProperties(
1018
-        EEM_Base $model,
1019
-        WP_REST_Request $rest_request,
1020
-        $entity_array
1021
-    ) {
1022
-
1023
-        $includes_for_this_model = $this->explodeAndGetItemsPrefixedWith($rest_request->get_param('include'), '');
1024
-        $includes_for_this_model = $this->removeModelNamesFromArray($includes_for_this_model);
1025
-        // if they passed in * or didn't specify any includes, return everything
1026
-        if (! in_array('*', $includes_for_this_model)
1027
-            && ! empty($includes_for_this_model)
1028
-        ) {
1029
-            if ($model->has_primary_key_field()) {
1030
-                // always include the primary key. ya just gotta know that at least
1031
-                $includes_for_this_model[] = $model->primary_key_name();
1032
-            }
1033
-            if ($this->explodeAndGetItemsPrefixedWith($rest_request->get_param('calculate'), '')) {
1034
-                $includes_for_this_model[] = '_calculated_fields';
1035
-            }
1036
-            $entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model));
1037
-        }
1038
-        return $entity_array;
1039
-    }
1040
-
1041
-
1042
-    /**
1043
-     * Returns a new array with all the names of models removed. Eg
1044
-     * array( 'Event', 'Datetime.*', 'foobar' ) would become array( 'Datetime.*', 'foobar' )
1045
-     *
1046
-     * @param array $arr
1047
-     * @return array
1048
-     */
1049
-    private function removeModelNamesFromArray($arr)
1050
-    {
1051
-        return array_diff($arr, array_keys(EE_Registry::instance()->non_abstract_db_models));
1052
-    }
1053
-
1054
-
1055
-    /**
1056
-     * Gets the calculated fields for the response
1057
-     *
1058
-     * @param EEM_Base        $model
1059
-     * @param array           $wpdb_row
1060
-     * @param WP_REST_Request $rest_request
1061
-     * @param boolean $row_is_protected whether this row is password protected or not
1062
-     * @return \stdClass the _calculations item in the entity
1063
-     * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
1064
-     * did, let's know about it ASAP, so let the exception bubble up)
1065
-     */
1066
-    protected function getEntityCalculations($model, $wpdb_row, $rest_request, $row_is_protected = false)
1067
-    {
1068
-        $calculated_fields = $this->explodeAndGetItemsPrefixedWith(
1069
-            $rest_request->get_param('calculate'),
1070
-            ''
1071
-        );
1072
-        // note: setting calculate=* doesn't do anything
1073
-        $calculated_fields_to_return = new \stdClass();
1074
-        $protected_fields = array();
1075
-        foreach ($calculated_fields as $field_to_calculate) {
1076
-            try {
1077
-                // it's password protected, so they shouldn't be able to read this. Remove the value
1078
-                $schema = $this->fields_calculator->getJsonSchemaForModel($model);
1079
-                if ($row_is_protected
1080
-                    && isset($schema['properties'][ $field_to_calculate ]['protected'])
1081
-                    && $schema['properties'][ $field_to_calculate ]['protected']) {
1082
-                    $calculated_value = null;
1083
-                    $protected_fields[] = $field_to_calculate;
1084
-                    if ($schema['properties'][ $field_to_calculate ]['type']) {
1085
-                        switch ($schema['properties'][ $field_to_calculate ]['type']) {
1086
-                            case 'boolean':
1087
-                                $calculated_value = false;
1088
-                                break;
1089
-                            case 'integer':
1090
-                                $calculated_value = 0;
1091
-                                break;
1092
-                            case 'string':
1093
-                                $calculated_value = '';
1094
-                                break;
1095
-                            case 'array':
1096
-                                $calculated_value = array();
1097
-                                break;
1098
-                            case 'object':
1099
-                                $calculated_value = new stdClass();
1100
-                                break;
1101
-                        }
1102
-                    }
1103
-                } else {
1104
-                    $calculated_value = ModelDataTranslator::prepareFieldValueForJson(
1105
-                        null,
1106
-                        $this->fields_calculator->retrieveCalculatedFieldValue(
1107
-                            $model,
1108
-                            $field_to_calculate,
1109
-                            $wpdb_row,
1110
-                            $rest_request,
1111
-                            $this
1112
-                        ),
1113
-                        $this->getModelVersionInfo()->requestedVersion()
1114
-                    );
1115
-                }
1116
-                $calculated_fields_to_return->{$field_to_calculate} = $calculated_value;
1117
-            } catch (RestException $e) {
1118
-                // if we don't have permission to read it, just leave it out. but let devs know about the problem
1119
-                $this->setResponseHeader(
1120
-                    'Notices-Field-Calculation-Errors['
1121
-                    . $e->getStringCode()
1122
-                    . ']['
1123
-                    . $model->get_this_model_name()
1124
-                    . ']['
1125
-                    . $field_to_calculate
1126
-                    . ']',
1127
-                    $e->getMessage(),
1128
-                    true
1129
-                );
1130
-            }
1131
-        }
1132
-        $calculated_fields_to_return->_protected = $protected_fields;
1133
-        return $calculated_fields_to_return;
1134
-    }
1135
-
1136
-
1137
-    /**
1138
-     * Gets the full URL to the resource, taking the requested version into account
1139
-     *
1140
-     * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
1141
-     * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
1142
-     */
1143
-    public function getVersionedLinkTo($link_part_after_version_and_slash)
1144
-    {
1145
-        return rest_url(
1146
-            EED_Core_Rest_Api::get_versioned_route_to(
1147
-                $link_part_after_version_and_slash,
1148
-                $this->getModelVersionInfo()->requestedVersion()
1149
-            )
1150
-        );
1151
-    }
1152
-
1153
-
1154
-    /**
1155
-     * Gets the correct lowercase name for the relation in the API according
1156
-     * to the relation's type
1157
-     *
1158
-     * @param string                  $relation_name
1159
-     * @param \EE_Model_Relation_Base $relation_obj
1160
-     * @return string
1161
-     */
1162
-    public static function getRelatedEntityName($relation_name, $relation_obj)
1163
-    {
1164
-        if ($relation_obj instanceof EE_Belongs_To_Relation) {
1165
-            return strtolower($relation_name);
1166
-        } else {
1167
-            return EEH_Inflector::pluralize_and_lower($relation_name);
1168
-        }
1169
-    }
1170
-
1171
-
1172
-    /**
1173
-     * Gets the one model object with the specified id for the specified model
1174
-     *
1175
-     * @param EEM_Base        $model
1176
-     * @param WP_REST_Request $request
1177
-     * @return array
1178
-     */
1179
-    public function getEntityFromModel($model, $request)
1180
-    {
1181
-        $context = $this->validateContext($request->get_param('caps'));
1182
-        return $this->getOneOrReportPermissionError($model, $request, $context);
1183
-    }
1184
-
1185
-
1186
-    /**
1187
-     * If a context is provided which isn't valid, maybe it was added in a future
1188
-     * version so just treat it as a default read
1189
-     *
1190
-     * @param string $context
1191
-     * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
1192
-     */
1193
-    public function validateContext($context)
1194
-    {
1195
-        if (! $context) {
1196
-            $context = EEM_Base::caps_read;
1197
-        }
1198
-        $valid_contexts = EEM_Base::valid_cap_contexts();
1199
-        if (in_array($context, $valid_contexts)) {
1200
-            return $context;
1201
-        } else {
1202
-            return EEM_Base::caps_read;
1203
-        }
1204
-    }
1205
-
1206
-
1207
-    /**
1208
-     * Verifies the passed in value is an allowable default where conditions value.
1209
-     *
1210
-     * @param $default_query_params
1211
-     * @return string
1212
-     */
1213
-    public function validateDefaultQueryParams($default_query_params)
1214
-    {
1215
-        $valid_default_where_conditions_for_api_calls = array(
1216
-            EEM_Base::default_where_conditions_all,
1217
-            EEM_Base::default_where_conditions_minimum_all,
1218
-            EEM_Base::default_where_conditions_minimum_others,
1219
-        );
1220
-        if (! $default_query_params) {
1221
-            $default_query_params = EEM_Base::default_where_conditions_all;
1222
-        }
1223
-        if (in_array(
1224
-            $default_query_params,
1225
-            $valid_default_where_conditions_for_api_calls,
1226
-            true
1227
-        )) {
1228
-            return $default_query_params;
1229
-        } else {
1230
-            return EEM_Base::default_where_conditions_all;
1231
-        }
1232
-    }
1233
-
1234
-
1235
-    /**
1236
-     * Translates API filter get parameter into model query params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions.
1237
-     * Note: right now the query parameter keys for fields (and related fields)
1238
-     * can be left as-is, but it's quite possible this will change someday.
1239
-     * Also, this method's contents might be candidate for moving to Model_Data_Translator
1240
-     *
1241
-     * @param EEM_Base $model
1242
-     * @param array    $query_parameters  from $_GET parameter @see Read:handle_request_get_all
1243
-     * @return array model query params (@see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions)
1244
-     *                                    or FALSE to indicate that absolutely no results should be returned
1245
-     * @throws EE_Error
1246
-     * @throws RestException
1247
-     */
1248
-    public function createModelQueryParams($model, $query_params)
1249
-    {
1250
-        $model_query_params = array();
1251
-        if (isset($query_params['where'])) {
1252
-            $model_query_params[0] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1253
-                $query_params['where'],
1254
-                $model,
1255
-                $this->getModelVersionInfo()->requestedVersion()
1256
-            );
1257
-        }
1258
-        if (isset($query_params['order_by'])) {
1259
-            $order_by = $query_params['order_by'];
1260
-        } elseif (isset($query_params['orderby'])) {
1261
-            $order_by = $query_params['orderby'];
1262
-        } else {
1263
-            $order_by = null;
1264
-        }
1265
-        if ($order_by !== null) {
1266
-            if (is_array($order_by)) {
1267
-                $order_by = ModelDataTranslator::prepareFieldNamesInArrayKeysFromJson($order_by);
1268
-            } else {
1269
-                // it's a single item
1270
-                $order_by = ModelDataTranslator::prepareFieldNameFromJson($order_by);
1271
-            }
1272
-            $model_query_params['order_by'] = $order_by;
1273
-        }
1274
-        if (isset($query_params['group_by'])) {
1275
-            $group_by = $query_params['group_by'];
1276
-        } elseif (isset($query_params['groupby'])) {
1277
-            $group_by = $query_params['groupby'];
1278
-        } else {
1279
-            $group_by = array_keys($model->get_combined_primary_key_fields());
1280
-        }
1281
-        // make sure they're all real names
1282
-        if (is_array($group_by)) {
1283
-            $group_by = ModelDataTranslator::prepareFieldNamesFromJson($group_by);
1284
-        }
1285
-        if ($group_by !== null) {
1286
-            $model_query_params['group_by'] = $group_by;
1287
-        }
1288
-        if (isset($query_params['having'])) {
1289
-            $model_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1290
-                $query_params['having'],
1291
-                $model,
1292
-                $this->getModelVersionInfo()->requestedVersion()
1293
-            );
1294
-        }
1295
-        if (isset($query_params['order'])) {
1296
-            $model_query_params['order'] = $query_params['order'];
1297
-        }
1298
-        if (isset($query_params['mine'])) {
1299
-            $model_query_params = $model->alter_query_params_to_only_include_mine($model_query_params);
1300
-        }
1301
-        if (isset($query_params['limit'])) {
1302
-            // limit should be either a string like '23' or '23,43', or an array with two items in it
1303
-            if (! is_array($query_params['limit'])) {
1304
-                $limit_array = explode(',', (string) $query_params['limit']);
1305
-            } else {
1306
-                $limit_array = $query_params['limit'];
1307
-            }
1308
-            $sanitized_limit = array();
1309
-            foreach ($limit_array as $key => $limit_part) {
1310
-                if ($this->debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1311
-                    throw new EE_Error(
1312
-                        sprintf(
1313
-                            __(
1314
-                            // @codingStandardsIgnoreStart
1315
-                                '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.',
1316
-                                // @codingStandardsIgnoreEnd
1317
-                                'event_espresso'
1318
-                            ),
1319
-                            wp_json_encode($query_params['limit'])
1320
-                        )
1321
-                    );
1322
-                }
1323
-                $sanitized_limit[] = (int) $limit_part;
1324
-            }
1325
-            $model_query_params['limit'] = implode(',', $sanitized_limit);
1326
-        } else {
1327
-            $model_query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
1328
-        }
1329
-        if (isset($query_params['caps'])) {
1330
-            $model_query_params['caps'] = $this->validateContext($query_params['caps']);
1331
-        } else {
1332
-            $model_query_params['caps'] = EEM_Base::caps_read;
1333
-        }
1334
-        if (isset($query_params['default_where_conditions'])) {
1335
-            $model_query_params['default_where_conditions'] = $this->validateDefaultQueryParams(
1336
-                $query_params['default_where_conditions']
1337
-            );
1338
-        }
1339
-        // if this is a model protected by a password on another model, exclude the password protected
1340
-        // entities by default. But if they passed in a password, try to show them all. If the password is wrong,
1341
-        // though, they'll get an error (see Read::createEntityFromWpdbResult() which calls Read::checkPassword)
1342
-        if (! $model->hasPassword()
1343
-            && $model->restrictedByRelatedModelPassword()
1344
-            && $model_query_params['caps'] === EEM_Base::caps_read) {
1345
-            if (empty($query_params['password'])) {
1346
-                $model_query_params['exclude_protected'] = true;
1347
-            }
1348
-        }
1349
-
1350
-        return apply_filters('FHEE__Read__create_model_query_params', $model_query_params, $query_params, $model);
1351
-    }
1352
-
1353
-
1354
-    /**
1355
-     * Changes the REST-style query params for use in the models
1356
-     *
1357
-     * @deprecated
1358
-     * @param EEM_Base $model
1359
-     * @param array    $query_params sub-array from @see EEM_Base::get_all()
1360
-     * @return array
1361
-     */
1362
-    public function prepareRestQueryParamsKeyForModels($model, $query_params)
1363
-    {
1364
-        $model_ready_query_params = array();
1365
-        foreach ($query_params as $key => $value) {
1366
-            if (is_array($value)) {
1367
-                $model_ready_query_params[ $key ] = $this->prepareRestQueryParamsKeyForModels($model, $value);
1368
-            } else {
1369
-                $model_ready_query_params[ $key ] = $value;
1370
-            }
1371
-        }
1372
-        return $model_ready_query_params;
1373
-    }
1374
-
1375
-
1376
-    /**
1377
-     * @deprecated instead use ModelDataTranslator::prepareFieldValuesFromJson()
1378
-     * @param $model
1379
-     * @param $query_params
1380
-     * @return array
1381
-     */
1382
-    public function prepareRestQueryParamsValuesForModels($model, $query_params)
1383
-    {
1384
-        $model_ready_query_params = array();
1385
-        foreach ($query_params as $key => $value) {
1386
-            if (is_array($value)) {
1387
-                $model_ready_query_params[ $key ] = $this->prepareRestQueryParamsValuesForModels($model, $value);
1388
-            } else {
1389
-                $model_ready_query_params[ $key ] = $value;
1390
-            }
1391
-        }
1392
-        return $model_ready_query_params;
1393
-    }
1394
-
1395
-
1396
-    /**
1397
-     * Explodes the string on commas, and only returns items with $prefix followed by a period.
1398
-     * If no prefix is specified, returns items with no period.
1399
-     *
1400
-     * @param string|array $string_to_explode eg "jibba,jabba, blah, blah, blah" or array('jibba', 'jabba' )
1401
-     * @param string       $prefix            "Event" or "foobar"
1402
-     * @return array $string_to_exploded exploded on COMMAS, and if a prefix was specified
1403
-     *                                        we only return strings starting with that and a period; if no prefix was
1404
-     *                                        specified we return all items containing NO periods
1405
-     */
1406
-    public function explodeAndGetItemsPrefixedWith($string_to_explode, $prefix)
1407
-    {
1408
-        if (is_string($string_to_explode)) {
1409
-            $exploded_contents = explode(',', $string_to_explode);
1410
-        } elseif (is_array($string_to_explode)) {
1411
-            $exploded_contents = $string_to_explode;
1412
-        } else {
1413
-            $exploded_contents = array();
1414
-        }
1415
-        // if the string was empty, we want an empty array
1416
-        $exploded_contents = array_filter($exploded_contents);
1417
-        $contents_with_prefix = array();
1418
-        foreach ($exploded_contents as $item) {
1419
-            $item = trim($item);
1420
-            // if no prefix was provided, so we look for items with no "." in them
1421
-            if (! $prefix) {
1422
-                // does this item have a period?
1423
-                if (strpos($item, '.') === false) {
1424
-                    // if not, then its what we're looking for
1425
-                    $contents_with_prefix[] = $item;
1426
-                }
1427
-            } elseif (strpos($item, $prefix . '.') === 0) {
1428
-                // this item has the prefix and a period, grab it
1429
-                $contents_with_prefix[] = substr(
1430
-                    $item,
1431
-                    strpos($item, $prefix . '.') + strlen($prefix . '.')
1432
-                );
1433
-            } elseif ($item === $prefix) {
1434
-                // this item is JUST the prefix
1435
-                // so let's grab everything after, which is a blank string
1436
-                $contents_with_prefix[] = '';
1437
-            }
1438
-        }
1439
-        return $contents_with_prefix;
1440
-    }
1441
-
1442
-
1443
-    /**
1444
-     * @deprecated since 4.8.36.rc.001 You should instead use Read::explode_and_get_items_prefixed_with.
1445
-     * Deprecated because its return values were really quite confusing- sometimes it returned
1446
-     * an empty array (when the include string was blank or '*') or sometimes it returned
1447
-     * array('*') (when you provided a model and a model of that kind was found).
1448
-     * Parses the $include_string so we fetch all the field names relating to THIS model
1449
-     * (ie have NO period in them), or for the provided model (ie start with the model
1450
-     * name and then a period).
1451
-     * @param string $include_string @see Read:handle_request_get_all
1452
-     * @param string $model_name
1453
-     * @return array of fields for this model. If $model_name is provided, then
1454
-     *                               the fields for that model, with the model's name removed from each.
1455
-     *                               If $include_string was blank or '*' returns an empty array
1456
-     */
1457
-    public function extractIncludesForThisModel($include_string, $model_name = null)
1458
-    {
1459
-        if (is_array($include_string)) {
1460
-            $include_string = implode(',', $include_string);
1461
-        }
1462
-        if ($include_string === '*' || $include_string === '') {
1463
-            return array();
1464
-        }
1465
-        $includes = explode(',', $include_string);
1466
-        $extracted_fields_to_include = array();
1467
-        if ($model_name) {
1468
-            foreach ($includes as $field_to_include) {
1469
-                $field_to_include = trim($field_to_include);
1470
-                if (strpos($field_to_include, $model_name . '.') === 0) {
1471
-                    // found the model name at the exact start
1472
-                    $field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1473
-                    $extracted_fields_to_include[] = $field_sans_model_name;
1474
-                } elseif ($field_to_include == $model_name) {
1475
-                    $extracted_fields_to_include[] = '*';
1476
-                }
1477
-            }
1478
-        } else {
1479
-            // look for ones with no period
1480
-            foreach ($includes as $field_to_include) {
1481
-                $field_to_include = trim($field_to_include);
1482
-                if (strpos($field_to_include, '.') === false
1483
-                    && ! $this->getModelVersionInfo()->isModelNameInThisVersion($field_to_include)
1484
-                ) {
1485
-                    $extracted_fields_to_include[] = $field_to_include;
1486
-                }
1487
-            }
1488
-        }
1489
-        return $extracted_fields_to_include;
1490
-    }
1491
-
1492
-
1493
-    /**
1494
-     * Gets the single item using the model according to the request in the context given, otherwise
1495
-     * returns that it's inaccessible to the current user
1496
-     *
1497
-     * @param EEM_Base $model
1498
-     * @param WP_REST_Request $request
1499
-     * @param null $context
1500
-     * @return array
1501
-     * @throws EE_Error
1502
-     */
1503
-    public function getOneOrReportPermissionError(EEM_Base $model, WP_REST_Request $request, $context = null)
1504
-    {
1505
-        $query_params = array(array($model->primary_key_name() => $request->get_param('id')), 'limit' => 1);
1506
-        if ($model instanceof EEM_Soft_Delete_Base) {
1507
-            $query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
1508
-        }
1509
-        $restricted_query_params = $query_params;
1510
-        $restricted_query_params['caps'] = $context;
1511
-        $this->setDebugInfo('model query params', $restricted_query_params);
1512
-        $model_rows = $model->get_all_wpdb_results($restricted_query_params);
1513
-        if (! empty($model_rows)) {
1514
-            return $this->createEntityFromWpdbResult(
1515
-                $model,
1516
-                reset($model_rows),
1517
-                $request
1518
-            );
1519
-        } else {
1520
-            // ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
1521
-            $lowercase_model_name = strtolower($model->get_this_model_name());
1522
-            if ($model->exists($query_params)) {
1523
-                // you got shafted- it existed but we didn't want to tell you!
1524
-                throw new RestException(
1525
-                    'rest_user_cannot_' . $context,
1526
-                    sprintf(
1527
-                        __('Sorry, you cannot %1$s this %2$s. Missing permissions are: %3$s', 'event_espresso'),
1528
-                        $context,
1529
-                        $lowercase_model_name,
1530
-                        Capabilities::getMissingPermissionsString(
1531
-                            $model,
1532
-                            $context
1533
-                        )
1534
-                    ),
1535
-                    array('status' => 403)
1536
-                );
1537
-            } else {
1538
-                // it's not you. It just doesn't exist
1539
-                throw new RestException(
1540
-                    sprintf('rest_%s_invalid_id', $lowercase_model_name),
1541
-                    sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
1542
-                    array('status' => 404)
1543
-                );
1544
-            }
1545
-        }
1546
-    }
1547
-
1548
-    /**
1549
-     * Checks that if this content requires a password to be read, that it's been provided and is correct.
1550
-     * @since 4.9.74.p
1551
-     * @param EEM_Base $model
1552
-     * @param $model_row
1553
-     * @param $query_params Adds 'default_where_conditions' => 'minimum' to ensure we don't confuse trashed with
1554
-     *                      password protected.
1555
-     * @param WP_REST_Request $request
1556
-     * @throws EE_Error
1557
-     * @throws InvalidArgumentException
1558
-     * @throws InvalidDataTypeException
1559
-     * @throws InvalidInterfaceException
1560
-     * @throws RestPasswordRequiredException
1561
-     * @throws RestPasswordIncorrectException
1562
-     * @throws \EventEspresso\core\exceptions\ModelConfigurationException
1563
-     * @throws ReflectionException
1564
-     */
1565
-    protected function checkPassword(EEM_Base $model, $model_row, $query_params, WP_REST_Request $request)
1566
-    {
1567
-        $query_params['default_where_conditions'] = 'minimum';
1568
-        // stuff is only "protected" for front-end requests. Elsewhere, you either get full permission to access the object
1569
-        // or you don't.
1570
-        $request_caps = $request->get_param('caps');
1571
-        if (isset($request_caps) && $request_caps !== EEM_Base::caps_read) {
1572
-            return;
1573
-        }
1574
-        // if this entity requires a password, they better give it and it better be right!
1575
-        if ($model->hasPassword()
1576
-            && $model_row[ $model->getPasswordField()->get_qualified_column() ] !== '') {
1577
-            if (empty($request['password'])) {
1578
-                throw new RestPasswordRequiredException();
1579
-            } elseif (!hash_equals(
1580
-                $model_row[ $model->getPasswordField()->get_qualified_column() ],
1581
-                $request['password']
1582
-            )) {
1583
-                throw new RestPasswordIncorrectException();
1584
-            }
1585
-        } // wait! maybe this content is password protected
1586
-        elseif ($model->restrictedByRelatedModelPassword()
1587
-            && $request->get_param('caps') === EEM_Base::caps_read) {
1588
-            $password_supplied = $request->get_param('password');
1589
-            if (empty($password_supplied)) {
1590
-                $query_params['exclude_protected'] = true;
1591
-                if (!$model->exists($query_params)) {
1592
-                    throw new RestPasswordRequiredException();
1593
-                }
1594
-            } else {
1595
-                $query_params[0][ $model->modelChainAndPassword() ] = $password_supplied;
1596
-                if (!$model->exists($query_params)) {
1597
-                    throw new RestPasswordIncorrectException();
1598
-                }
1599
-            }
1600
-        }
1601
-    }
48
+	/**
49
+	 * @var CalculatedModelFields
50
+	 */
51
+	protected $fields_calculator;
52
+
53
+
54
+	/**
55
+	 * Read constructor.
56
+	 * @param CalculatedModelFields $fields_calculator
57
+	 */
58
+	public function __construct(CalculatedModelFields $fields_calculator)
59
+	{
60
+		parent::__construct();
61
+		$this->fields_calculator = $fields_calculator;
62
+	}
63
+
64
+
65
+	/**
66
+	 * Handles requests to get all (or a filtered subset) of entities for a particular model
67
+	 *
68
+	 * @param WP_REST_Request $request
69
+	 * @param string $version
70
+	 * @param string $model_name
71
+	 * @return WP_REST_Response|WP_Error
72
+	 * @throws InvalidArgumentException
73
+	 * @throws InvalidDataTypeException
74
+	 * @throws InvalidInterfaceException
75
+	 */
76
+	public static function handleRequestGetAll(WP_REST_Request $request, $version, $model_name)
77
+	{
78
+		$controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
79
+		try {
80
+			$controller->setRequestedVersion($version);
81
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
82
+				return $controller->sendResponse(
83
+					new WP_Error(
84
+						'endpoint_parsing_error',
85
+						sprintf(
86
+							__(
87
+								'There is no model for endpoint %s. Please contact event espresso support',
88
+								'event_espresso'
89
+							),
90
+							$model_name
91
+						)
92
+					)
93
+				);
94
+			}
95
+			return $controller->sendResponse(
96
+				$controller->getEntitiesFromModel(
97
+					$controller->getModelVersionInfo()->loadModel($model_name),
98
+					$request
99
+				)
100
+			);
101
+		} catch (Exception $e) {
102
+			return $controller->sendResponse($e);
103
+		}
104
+	}
105
+
106
+
107
+	/**
108
+	 * Prepares and returns schema for any OPTIONS request.
109
+	 *
110
+	 * @param string $version The API endpoint version being used.
111
+	 * @param string $model_name Something like `Event` or `Registration`
112
+	 * @return array
113
+	 * @throws InvalidArgumentException
114
+	 * @throws InvalidDataTypeException
115
+	 * @throws InvalidInterfaceException
116
+	 */
117
+	public static function handleSchemaRequest($version, $model_name)
118
+	{
119
+		$controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
120
+		try {
121
+			$controller->setRequestedVersion($version);
122
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
123
+				return array();
124
+			}
125
+			// get the model for this version
126
+			$model = $controller->getModelVersionInfo()->loadModel($model_name);
127
+			$model_schema = new JsonModelSchema($model, LoaderFactory::getLoader()->getShared('EventEspresso\core\libraries\rest_api\CalculatedModelFields'));
128
+			return $model_schema->getModelSchemaForRelations(
129
+				$controller->getModelVersionInfo()->relationSettings($model),
130
+				$controller->customizeSchemaForRestResponse(
131
+					$model,
132
+					$model_schema->getModelSchemaForFields(
133
+						$controller->getModelVersionInfo()->fieldsOnModelInThisVersion($model),
134
+						$model_schema->getInitialSchemaStructure()
135
+					)
136
+				)
137
+			);
138
+		} catch (Exception $e) {
139
+			return array();
140
+		}
141
+	}
142
+
143
+
144
+	/**
145
+	 * This loops through each field in the given schema for the model and does the following:
146
+	 * - add any extra fields that are REST API specific and related to existing fields.
147
+	 * - transform default values into the correct format for a REST API response.
148
+	 *
149
+	 * @param EEM_Base $model
150
+	 * @param array    $schema
151
+	 * @return array  The final schema.
152
+	 */
153
+	protected function customizeSchemaForRestResponse(EEM_Base $model, array $schema)
154
+	{
155
+		foreach ($this->getModelVersionInfo()->fieldsOnModelInThisVersion($model) as $field_name => $field) {
156
+			$schema = $this->translateDefaultsForRestResponse(
157
+				$field_name,
158
+				$field,
159
+				$this->maybeAddExtraFieldsToSchema($field_name, $field, $schema)
160
+			);
161
+		}
162
+		return $schema;
163
+	}
164
+
165
+
166
+	/**
167
+	 * This is used to ensure that the 'default' value set in the schema response is formatted correctly for the REST
168
+	 * response.
169
+	 *
170
+	 * @param                      $field_name
171
+	 * @param EE_Model_Field_Base  $field
172
+	 * @param array                $schema
173
+	 * @return array
174
+	 * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
175
+	 * did, let's know about it ASAP, so let the exception bubble up)
176
+	 */
177
+	protected function translateDefaultsForRestResponse($field_name, EE_Model_Field_Base $field, array $schema)
178
+	{
179
+		if (isset($schema['properties'][ $field_name ]['default'])) {
180
+			if (is_array($schema['properties'][ $field_name ]['default'])) {
181
+				foreach ($schema['properties'][ $field_name ]['default'] as $default_key => $default_value) {
182
+					if ($default_key === 'raw') {
183
+						$schema['properties'][ $field_name ]['default'][ $default_key ] =
184
+							ModelDataTranslator::prepareFieldValueForJson(
185
+								$field,
186
+								$default_value,
187
+								$this->getModelVersionInfo()->requestedVersion()
188
+							);
189
+					}
190
+				}
191
+			} else {
192
+				$schema['properties'][ $field_name ]['default'] = ModelDataTranslator::prepareFieldValueForJson(
193
+					$field,
194
+					$schema['properties'][ $field_name ]['default'],
195
+					$this->getModelVersionInfo()->requestedVersion()
196
+				);
197
+			}
198
+		}
199
+		return $schema;
200
+	}
201
+
202
+
203
+	/**
204
+	 * Adds additional fields to the schema
205
+	 * The REST API returns a GMT value field for each datetime field in the resource.  Thus the description about this
206
+	 * needs to be added to the schema.
207
+	 *
208
+	 * @param                      $field_name
209
+	 * @param EE_Model_Field_Base  $field
210
+	 * @param array                $schema
211
+	 * @return array
212
+	 */
213
+	protected function maybeAddExtraFieldsToSchema($field_name, EE_Model_Field_Base $field, array $schema)
214
+	{
215
+		if ($field instanceof EE_Datetime_Field) {
216
+			$schema['properties'][ $field_name . '_gmt' ] = $field->getSchema();
217
+			// modify the description
218
+			$schema['properties'][ $field_name . '_gmt' ]['description'] = sprintf(
219
+				esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
220
+				wp_specialchars_decode($field->get_nicename(), ENT_QUOTES)
221
+			);
222
+		}
223
+		return $schema;
224
+	}
225
+
226
+
227
+	/**
228
+	 * Used to figure out the route from the request when a `WP_REST_Request` object is not available
229
+	 *
230
+	 * @return string
231
+	 */
232
+	protected function getRouteFromRequest()
233
+	{
234
+		if (isset($GLOBALS['wp'])
235
+			&& $GLOBALS['wp'] instanceof \WP
236
+			&& isset($GLOBALS['wp']->query_vars['rest_route'])
237
+		) {
238
+			return $GLOBALS['wp']->query_vars['rest_route'];
239
+		} else {
240
+			return isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
241
+		}
242
+	}
243
+
244
+
245
+	/**
246
+	 * Gets a single entity related to the model indicated in the path and its id
247
+	 *
248
+	 * @param WP_REST_Request $request
249
+	 * @param string $version
250
+	 * @param string $model_name
251
+	 * @return WP_REST_Response|WP_Error
252
+	 * @throws InvalidDataTypeException
253
+	 * @throws InvalidInterfaceException
254
+	 * @throws InvalidArgumentException
255
+	 */
256
+	public static function handleRequestGetOne(WP_REST_Request $request, $version, $model_name)
257
+	{
258
+		$controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
259
+		try {
260
+			$controller->setRequestedVersion($version);
261
+			if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
262
+				return $controller->sendResponse(
263
+					new WP_Error(
264
+						'endpoint_parsing_error',
265
+						sprintf(
266
+							__(
267
+								'There is no model for endpoint %s. Please contact event espresso support',
268
+								'event_espresso'
269
+							),
270
+							$model_name
271
+						)
272
+					)
273
+				);
274
+			}
275
+			return $controller->sendResponse(
276
+				$controller->getEntityFromModel(
277
+					$controller->getModelVersionInfo()->loadModel($model_name),
278
+					$request
279
+				)
280
+			);
281
+		} catch (Exception $e) {
282
+			return $controller->sendResponse($e);
283
+		}
284
+	}
285
+
286
+
287
+	/**
288
+	 * Gets all the related entities (or if its a belongs-to relation just the one)
289
+	 * to the item with the given id
290
+	 *
291
+	 * @param WP_REST_Request $request
292
+	 * @param string $version
293
+	 * @param string $model_name
294
+	 * @param string $related_model_name
295
+	 * @return WP_REST_Response|WP_Error
296
+	 * @throws InvalidDataTypeException
297
+	 * @throws InvalidInterfaceException
298
+	 * @throws InvalidArgumentException
299
+	 */
300
+	public static function handleRequestGetRelated(
301
+		WP_REST_Request $request,
302
+		$version,
303
+		$model_name,
304
+		$related_model_name
305
+	) {
306
+		$controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
307
+		try {
308
+			$controller->setRequestedVersion($version);
309
+			$main_model = $controller->validateModel($model_name);
310
+			$controller->validateModel($related_model_name);
311
+			return $controller->sendResponse(
312
+				$controller->getEntitiesFromRelation(
313
+					$request->get_param('id'),
314
+					$main_model->related_settings_for($related_model_name),
315
+					$request
316
+				)
317
+			);
318
+		} catch (Exception $e) {
319
+			return $controller->sendResponse($e);
320
+		}
321
+	}
322
+
323
+
324
+	/**
325
+	 * Gets a collection for the given model and filters
326
+	 *
327
+	 * @param EEM_Base $model
328
+	 * @param WP_REST_Request $request
329
+	 * @return array
330
+	 * @throws EE_Error
331
+	 * @throws InvalidArgumentException
332
+	 * @throws InvalidDataTypeException
333
+	 * @throws InvalidInterfaceException
334
+	 * @throws ReflectionException
335
+	 * @throws RestException
336
+	 */
337
+	public function getEntitiesFromModel($model, $request)
338
+	{
339
+		$query_params = $this->createModelQueryParams($model, $request->get_params());
340
+		if (! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
341
+			$model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
342
+			throw new RestException(
343
+				sprintf('rest_%s_cannot_list', $model_name_plural),
344
+				sprintf(
345
+					__('Sorry, you are not allowed to list %1$s. Missing permissions: %2$s', 'event_espresso'),
346
+					$model_name_plural,
347
+					Capabilities::getMissingPermissionsString($model, $query_params['caps'])
348
+				),
349
+				array('status' => 403)
350
+			);
351
+		}
352
+		if (! $request->get_header('no_rest_headers')) {
353
+			$this->setHeadersFromQueryParams($model, $query_params);
354
+		}
355
+		/** @type array $results */
356
+		$results = $model->get_all_wpdb_results($query_params);
357
+		$nice_results = array();
358
+		foreach ($results as $result) {
359
+			$nice_results[] =  $this->createEntityFromWpdbResult(
360
+				$model,
361
+				$result,
362
+				$request
363
+			);
364
+		}
365
+		return $nice_results;
366
+	}
367
+
368
+
369
+	/**
370
+	 * Gets the collection for given relation object
371
+	 * The same as Read::get_entities_from_model(), except if the relation
372
+	 * is a HABTM relation, in which case it merges any non-foreign-key fields from
373
+	 * the join-model-object into the results
374
+	 *
375
+	 * @param array $primary_model_query_params query params for finding the item from which
376
+	 *                                                            relations will be based
377
+	 * @param \EE_Model_Relation_Base $relation
378
+	 * @param WP_REST_Request $request
379
+	 * @return array
380
+	 * @throws EE_Error
381
+	 * @throws InvalidArgumentException
382
+	 * @throws InvalidDataTypeException
383
+	 * @throws InvalidInterfaceException
384
+	 * @throws ReflectionException
385
+	 * @throws RestException
386
+	 * @throws \EventEspresso\core\exceptions\ModelConfigurationException
387
+	 */
388
+	protected function getEntitiesFromRelationUsingModelQueryParams($primary_model_query_params, $relation, $request)
389
+	{
390
+		$context = $this->validateContext($request->get_param('caps'));
391
+		$model = $relation->get_this_model();
392
+		$related_model = $relation->get_other_model();
393
+		if (! isset($primary_model_query_params[0])) {
394
+			$primary_model_query_params[0] = array();
395
+		}
396
+		// check if they can access the 1st model object
397
+		$primary_model_query_params = array(
398
+			0       => $primary_model_query_params[0],
399
+			'limit' => 1,
400
+		);
401
+		if ($model instanceof EEM_Soft_Delete_Base) {
402
+			$primary_model_query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included(
403
+				$primary_model_query_params
404
+			);
405
+		}
406
+		$restricted_query_params = $primary_model_query_params;
407
+		$restricted_query_params['caps'] = $context;
408
+		$restricted_query_params['limit'] = 1;
409
+		$this->setDebugInfo('main model query params', $restricted_query_params);
410
+		$this->setDebugInfo('missing caps', Capabilities::getMissingPermissionsString($related_model, $context));
411
+		$primary_model_rows = $model->get_all_wpdb_results($restricted_query_params);
412
+		$primary_model_row = null;
413
+		if (is_array($primary_model_rows)) {
414
+			$primary_model_row = reset($primary_model_rows);
415
+		}
416
+		if (! (
417
+			Capabilities::currentUserHasPartialAccessTo($related_model, $context)
418
+			&& $primary_model_row
419
+		)
420
+		) {
421
+			if ($relation instanceof EE_Belongs_To_Relation) {
422
+				$related_model_name_maybe_plural = strtolower($related_model->get_this_model_name());
423
+			} else {
424
+				$related_model_name_maybe_plural = EEH_Inflector::pluralize_and_lower(
425
+					$related_model->get_this_model_name()
426
+				);
427
+			}
428
+			throw new RestException(
429
+				sprintf('rest_%s_cannot_list', $related_model_name_maybe_plural),
430
+				sprintf(
431
+					__(
432
+						'Sorry, you are not allowed to list %1$s related to %2$s. Missing permissions: %3$s',
433
+						'event_espresso'
434
+					),
435
+					$related_model_name_maybe_plural,
436
+					$relation->get_this_model()->get_this_model_name(),
437
+					implode(
438
+						',',
439
+						array_keys(
440
+							Capabilities::getMissingPermissions($related_model, $context)
441
+						)
442
+					)
443
+				),
444
+				array('status' => 403)
445
+			);
446
+		}
447
+
448
+		$this->checkPassword(
449
+			$model,
450
+			$primary_model_row,
451
+			$restricted_query_params,
452
+			$request
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
+	 * Gets the collection for given relation object
501
+	 * The same as Read::get_entities_from_model(), except if the relation
502
+	 * is a HABTM relation, in which case it merges any non-foreign-key fields from
503
+	 * the join-model-object into the results
504
+	 *
505
+	 * @param string                  $id the ID of the thing we are fetching related stuff from
506
+	 * @param \EE_Model_Relation_Base $relation
507
+	 * @param WP_REST_Request         $request
508
+	 * @return array
509
+	 * @throws EE_Error
510
+	 */
511
+	public function getEntitiesFromRelation($id, $relation, $request)
512
+	{
513
+		if (! $relation->get_this_model()->has_primary_key_field()) {
514
+			throw new EE_Error(
515
+				sprintf(
516
+					__(
517
+					// @codingStandardsIgnoreStart
518
+						'Read::get_entities_from_relation should only be called from a model with a primary key, it was called from %1$s',
519
+						// @codingStandardsIgnoreEnd
520
+						'event_espresso'
521
+					),
522
+					$relation->get_this_model()->get_this_model_name()
523
+				)
524
+			);
525
+		}
526
+		// can we edit that main item?
527
+		// if not, show nothing but an error
528
+		// otherwise, please proceed
529
+		return $this->getEntitiesFromRelationUsingModelQueryParams(
530
+			array(
531
+				array(
532
+					$relation->get_this_model()->primary_key_name() => $id,
533
+				),
534
+			),
535
+			$relation,
536
+			$request
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
+	 * Changes database results into REST API entities
582
+	 *
583
+	 * @param EEM_Base $model
584
+	 * @param array $db_row like results from $wpdb->get_results()
585
+	 * @param WP_REST_Request $rest_request
586
+	 * @param string $deprecated no longer used
587
+	 * @return array ready for being converted into json for sending to client
588
+	 * @throws EE_Error
589
+	 * @throws RestException
590
+	 * @throws InvalidDataTypeException
591
+	 * @throws InvalidInterfaceException
592
+	 * @throws InvalidArgumentException
593
+	 * @throws ReflectionException
594
+	 */
595
+	public function createEntityFromWpdbResult($model, $db_row, $rest_request, $deprecated = null)
596
+	{
597
+		if (! $rest_request instanceof WP_REST_Request) {
598
+			// ok so this was called in the old style, where the 3rd arg was
599
+			// $include, and the 4th arg was $context
600
+			// now setup the request just to avoid fatal errors, although we won't be able
601
+			// to truly make use of it because it's kinda devoid of info
602
+			$rest_request = new WP_REST_Request();
603
+			$rest_request->set_param('include', $rest_request);
604
+			$rest_request->set_param('caps', $deprecated);
605
+		}
606
+		if ($rest_request->get_param('caps') == null) {
607
+			$rest_request->set_param('caps', EEM_Base::caps_read);
608
+		}
609
+		$current_user_full_access_to_entity = $model->currentUserCan(
610
+			EEM_Base::caps_read_admin,
611
+			$model->deduce_fields_n_values_from_cols_n_values($db_row)
612
+		);
613
+		$entity_array = $this->createBareEntityFromWpdbResults($model, $db_row);
614
+		$entity_array = $this->addExtraFields($model, $db_row, $entity_array);
615
+		$entity_array['_links'] = $this->getEntityLinks($model, $db_row, $entity_array);
616
+		// when it's a regular read request for a model with a password and the password wasn't provided
617
+		// remove the password protected fields
618
+		$has_protected_fields = false;
619
+		try {
620
+			$this->checkPassword(
621
+				$model,
622
+				$db_row,
623
+				$model->alter_query_params_to_restrict_by_ID(
624
+					$model->get_index_primary_key_string(
625
+						$model->deduce_fields_n_values_from_cols_n_values($db_row)
626
+					)
627
+				),
628
+				$rest_request
629
+			);
630
+		} catch (RestPasswordRequiredException $e) {
631
+			if ($model->hasPassword()) {
632
+				// just remove protected fields
633
+				$has_protected_fields = true;
634
+				$entity_array = Capabilities::filterOutPasswordProtectedFields(
635
+					$entity_array,
636
+					$model,
637
+					$this->getModelVersionInfo()
638
+				);
639
+			} else {
640
+				// that's a problem. None of this should be accessible if no password was provided
641
+				throw $e;
642
+			}
643
+		}
644
+
645
+		$entity_array['_calculated_fields'] = $this->getEntityCalculations($model, $db_row, $rest_request, $has_protected_fields);
646
+		$entity_array = apply_filters(
647
+			'FHEE__Read__create_entity_from_wpdb_results__entity_before_including_requested_models',
648
+			$entity_array,
649
+			$model,
650
+			$rest_request->get_param('caps'),
651
+			$rest_request,
652
+			$this
653
+		);
654
+		// add an empty protected property for now. If it's still around after we remove everything the request didn't
655
+		// want, we'll populate it then. k?
656
+		$entity_array['_protected'] = array();
657
+		// remove any properties the request didn't want. This way _protected won't bother mentioning them
658
+		$entity_array = $this->includeOnlyRequestedProperties($model, $rest_request, $entity_array);
659
+		$entity_array = $this->includeRequestedModels($model, $rest_request, $entity_array, $db_row, $has_protected_fields);
660
+		// if they still wanted the _protected property, add it.
661
+		if (isset($entity_array['_protected'])) {
662
+			$entity_array = $this->addProtectedProperty($model, $entity_array, $has_protected_fields);
663
+		}
664
+		$entity_array = apply_filters(
665
+			'FHEE__Read__create_entity_from_wpdb_results__entity_before_inaccessible_field_removal',
666
+			$entity_array,
667
+			$model,
668
+			$rest_request->get_param('caps'),
669
+			$rest_request,
670
+			$this
671
+		);
672
+		if (! $current_user_full_access_to_entity) {
673
+			$result_without_inaccessible_fields = Capabilities::filterOutInaccessibleEntityFields(
674
+				$entity_array,
675
+				$model,
676
+				$rest_request->get_param('caps'),
677
+				$this->getModelVersionInfo()
678
+			);
679
+		} else {
680
+			$result_without_inaccessible_fields = $entity_array;
681
+		}
682
+		$this->setDebugInfo(
683
+			'inaccessible fields',
684
+			array_keys(array_diff_key((array) $entity_array, (array) $result_without_inaccessible_fields))
685
+		);
686
+		return apply_filters(
687
+			'FHEE__Read__create_entity_from_wpdb_results__entity_return',
688
+			$result_without_inaccessible_fields,
689
+			$model,
690
+			$rest_request->get_param('caps')
691
+		);
692
+	}
693
+
694
+	/**
695
+	 * Returns an array describing which fields can be protected, and which actually were removed this request
696
+	 * @since 4.9.74.p
697
+	 * @param $model
698
+	 * @param $results_so_far
699
+	 * @param $protected
700
+	 * @return array results
701
+	 */
702
+	protected function addProtectedProperty(EEM_Base $model, $results_so_far, $protected)
703
+	{
704
+		if (! $model->hasPassword() || ! $protected) {
705
+			return $results_so_far;
706
+		}
707
+		$password_field = $model->getPasswordField();
708
+		$all_protected = array_merge(
709
+			array($password_field->get_name()),
710
+			$password_field->protectedFields()
711
+		);
712
+		$fields_included = array_keys($results_so_far);
713
+		$fields_included = array_intersect(
714
+			$all_protected,
715
+			$fields_included
716
+		);
717
+		foreach ($fields_included as $field_name) {
718
+			$results_so_far['_protected'][] = $field_name ;
719
+		}
720
+		return $results_so_far;
721
+	}
722
+
723
+	/**
724
+	 * Creates a REST entity array (JSON object we're going to return in the response, but
725
+	 * for now still a PHP array, but soon enough we'll call json_encode on it, don't worry),
726
+	 * from $wpdb->get_row( $sql, ARRAY_A)
727
+	 *
728
+	 * @param EEM_Base $model
729
+	 * @param array    $db_row
730
+	 * @return array entity mostly ready for converting to JSON and sending in the response
731
+	 */
732
+	protected function createBareEntityFromWpdbResults(EEM_Base $model, $db_row)
733
+	{
734
+		$result = $model->deduce_fields_n_values_from_cols_n_values($db_row);
735
+		$result = array_intersect_key(
736
+			$result,
737
+			$this->getModelVersionInfo()->fieldsOnModelInThisVersion($model)
738
+		);
739
+		// if this is a CPT, we need to set the global $post to it,
740
+		// otherwise shortcodes etc won't work properly while rendering it
741
+		if ($model instanceof \EEM_CPT_Base) {
742
+			$do_chevy_shuffle = true;
743
+		} else {
744
+			$do_chevy_shuffle = false;
745
+		}
746
+		if ($do_chevy_shuffle) {
747
+			global $post;
748
+			$old_post = $post;
749
+			$post = get_post($result[ $model->primary_key_name() ]);
750
+			if (! $post instanceof \WP_Post) {
751
+				// well that's weird, because $result is what we JUST fetched from the database
752
+				throw new RestException(
753
+					'error_fetching_post_from_database_results',
754
+					esc_html__(
755
+						'An item was retrieved from the database but it\'s not a WP_Post like it should be.',
756
+						'event_espresso'
757
+					)
758
+				);
759
+			}
760
+			$model_object_classname = 'EE_' . $model->get_this_model_name();
761
+			$post->{$model_object_classname} = \EE_Registry::instance()->load_class(
762
+				$model_object_classname,
763
+				$result,
764
+				false,
765
+				false
766
+			);
767
+		}
768
+		foreach ($result as $field_name => $field_value) {
769
+			$field_obj = $model->field_settings_for($field_name);
770
+			if ($this->isSubclassOfOne($field_obj, $this->getModelVersionInfo()->fieldsIgnored())) {
771
+				unset($result[ $field_name ]);
772
+			} elseif ($this->isSubclassOfOne(
773
+				$field_obj,
774
+				$this->getModelVersionInfo()->fieldsThatHaveRenderedFormat()
775
+			)
776
+			) {
777
+				$result[ $field_name ] = array(
778
+					'raw'      => $this->prepareFieldObjValueForJson($field_obj, $field_value),
779
+					'rendered' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
780
+				);
781
+			} elseif ($this->isSubclassOfOne(
782
+				$field_obj,
783
+				$this->getModelVersionInfo()->fieldsThatHavePrettyFormat()
784
+			)
785
+			) {
786
+				$result[ $field_name ] = array(
787
+					'raw'    => $this->prepareFieldObjValueForJson($field_obj, $field_value),
788
+					'pretty' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
789
+				);
790
+			} elseif ($field_obj instanceof \EE_Datetime_Field) {
791
+				$field_value = $field_obj->prepare_for_set_from_db($field_value);
792
+				// if the value is null, but we're not supposed to permit null, then set to the field's default
793
+				if (is_null($field_value)) {
794
+					$field_value = $field_obj->getDefaultDateTimeObj();
795
+				}
796
+				if (is_null($field_value)) {
797
+					$gmt_date = $local_date = ModelDataTranslator::prepareFieldValuesForJson(
798
+						$field_obj,
799
+						$field_value,
800
+						$this->getModelVersionInfo()->requestedVersion()
801
+					);
802
+				} else {
803
+					$timezone = $field_value->getTimezone();
804
+					EEH_DTT_Helper::setTimezone($field_value, new DateTimeZone('UTC'));
805
+					$gmt_date = ModelDataTranslator::prepareFieldValuesForJson(
806
+						$field_obj,
807
+						$field_value,
808
+						$this->getModelVersionInfo()->requestedVersion()
809
+					);
810
+					EEH_DTT_Helper::setTimezone($field_value, $timezone);
811
+					$local_date = ModelDataTranslator::prepareFieldValuesForJson(
812
+						$field_obj,
813
+						$field_value,
814
+						$this->getModelVersionInfo()->requestedVersion()
815
+					);
816
+				}
817
+				$result[ $field_name . '_gmt' ] = $gmt_date;
818
+				$result[ $field_name ] = $local_date;
819
+			} else {
820
+				$result[ $field_name ] = $this->prepareFieldObjValueForJson($field_obj, $field_value);
821
+			}
822
+		}
823
+		if ($do_chevy_shuffle) {
824
+			$post = $old_post;
825
+		}
826
+		return $result;
827
+	}
828
+
829
+
830
+	/**
831
+	 * Takes a value all the way from the DB representation, to the model object's representation, to the
832
+	 * user-facing PHP representation, to the REST API representation. (Assumes you've already taken from the DB
833
+	 * representation using $field_obj->prepare_for_set_from_db())
834
+	 *
835
+	 * @param EE_Model_Field_Base $field_obj
836
+	 * @param mixed               $value  as it's stored on a model object
837
+	 * @param string              $format valid values are 'normal' (default), 'pretty', 'datetime_obj'
838
+	 * @return mixed
839
+	 * @throws ObjectDetectedException if $value contains a PHP object
840
+	 */
841
+	protected function prepareFieldObjValueForJson(EE_Model_Field_Base $field_obj, $value, $format = 'normal')
842
+	{
843
+		$value = $field_obj->prepare_for_set_from_db($value);
844
+		switch ($format) {
845
+			case 'pretty':
846
+				$value = $field_obj->prepare_for_pretty_echoing($value);
847
+				break;
848
+			case 'normal':
849
+			default:
850
+				$value = $field_obj->prepare_for_get($value);
851
+				break;
852
+		}
853
+		return ModelDataTranslator::prepareFieldValuesForJson(
854
+			$field_obj,
855
+			$value,
856
+			$this->getModelVersionInfo()->requestedVersion()
857
+		);
858
+	}
859
+
860
+
861
+	/**
862
+	 * Adds a few extra fields to the entity response
863
+	 *
864
+	 * @param EEM_Base $model
865
+	 * @param array    $db_row
866
+	 * @param array    $entity_array
867
+	 * @return array modified entity
868
+	 */
869
+	protected function addExtraFields(EEM_Base $model, $db_row, $entity_array)
870
+	{
871
+		if ($model instanceof EEM_CPT_Base) {
872
+			$entity_array['link'] = get_permalink($db_row[ $model->get_primary_key_field()->get_qualified_column() ]);
873
+		}
874
+		return $entity_array;
875
+	}
876
+
877
+
878
+	/**
879
+	 * Gets links we want to add to the response
880
+	 *
881
+	 * @global \WP_REST_Server $wp_rest_server
882
+	 * @param EEM_Base         $model
883
+	 * @param array            $db_row
884
+	 * @param array            $entity_array
885
+	 * @return array the _links item in the entity
886
+	 */
887
+	protected function getEntityLinks($model, $db_row, $entity_array)
888
+	{
889
+		// add basic links
890
+		$links = array();
891
+		if ($model->has_primary_key_field()) {
892
+			$links['self'] = array(
893
+				array(
894
+					'href' => $this->getVersionedLinkTo(
895
+						EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
896
+						. '/'
897
+						. $entity_array[ $model->primary_key_name() ]
898
+					),
899
+				),
900
+			);
901
+		}
902
+		$links['collection'] = array(
903
+			array(
904
+				'href' => $this->getVersionedLinkTo(
905
+					EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
906
+				),
907
+			),
908
+		);
909
+		// add links to related models
910
+		if ($model->has_primary_key_field()) {
911
+			foreach ($this->getModelVersionInfo()->relationSettings($model) as $relation_name => $relation_obj) {
912
+				$related_model_part = Read::getRelatedEntityName($relation_name, $relation_obj);
913
+				$links[ EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part ] = array(
914
+					array(
915
+						'href'   => $this->getVersionedLinkTo(
916
+							EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
917
+							. '/'
918
+							. $entity_array[ $model->primary_key_name() ]
919
+							. '/'
920
+							. $related_model_part
921
+						),
922
+						'single' => $relation_obj instanceof EE_Belongs_To_Relation ? true : false,
923
+					),
924
+				);
925
+			}
926
+		}
927
+		return $links;
928
+	}
929
+
930
+
931
+	/**
932
+	 * Adds the included models indicated in the request to the entity provided
933
+	 *
934
+	 * @param EEM_Base $model
935
+	 * @param WP_REST_Request $rest_request
936
+	 * @param array $entity_array
937
+	 * @param array $db_row
938
+	 * @param boolean $included_items_protected if the original item is password protected, don't include any related models.
939
+	 * @return array the modified entity
940
+	 * @throws RestException
941
+	 */
942
+	protected function includeRequestedModels(
943
+		EEM_Base $model,
944
+		WP_REST_Request $rest_request,
945
+		$entity_array,
946
+		$db_row = array(),
947
+		$included_items_protected = false
948
+	) {
949
+		// if $db_row not included, hope the entity array has what we need
950
+		if (! $db_row) {
951
+			$db_row = $entity_array;
952
+		}
953
+		$relation_settings = $this->getModelVersionInfo()->relationSettings($model);
954
+		foreach ($relation_settings as $relation_name => $relation_obj) {
955
+			$related_fields_to_include = $this->explodeAndGetItemsPrefixedWith(
956
+				$rest_request->get_param('include'),
957
+				$relation_name
958
+			);
959
+			$related_fields_to_calculate = $this->explodeAndGetItemsPrefixedWith(
960
+				$rest_request->get_param('calculate'),
961
+				$relation_name
962
+			);
963
+			// did they specify they wanted to include a related model, or
964
+			// specific fields from a related model?
965
+			// or did they specify to calculate a field from a related model?
966
+			if ($related_fields_to_include || $related_fields_to_calculate) {
967
+				// if so, we should include at least some part of the related model
968
+				$pretend_related_request = new WP_REST_Request();
969
+				$pretend_related_request->set_query_params(
970
+					array(
971
+						'caps'      => $rest_request->get_param('caps'),
972
+						'include'   => $related_fields_to_include,
973
+						'calculate' => $related_fields_to_calculate,
974
+						'password' => $rest_request->get_param('password')
975
+					)
976
+				);
977
+				$pretend_related_request->add_header('no_rest_headers', true);
978
+				$primary_model_query_params = $model->alter_query_params_to_restrict_by_ID(
979
+					$model->get_index_primary_key_string(
980
+						$model->deduce_fields_n_values_from_cols_n_values($db_row)
981
+					)
982
+				);
983
+				if (! $included_items_protected) {
984
+					try {
985
+						$related_results = $this->getEntitiesFromRelationUsingModelQueryParams(
986
+							$primary_model_query_params,
987
+							$relation_obj,
988
+							$pretend_related_request
989
+						);
990
+					} catch (RestException $e) {
991
+						$related_results = null;
992
+					}
993
+				} else {
994
+					// they're protected, hide them.
995
+					$related_results = null;
996
+					$entity_array['_protected'][] = Read::getRelatedEntityName($relation_name, $relation_obj);
997
+				}
998
+				if ($related_results instanceof WP_Error || $related_results === null) {
999
+					$related_results = $relation_obj instanceof EE_Belongs_To_Relation ? null : array();
1000
+				}
1001
+				$entity_array[ Read::getRelatedEntityName($relation_name, $relation_obj) ] = $related_results;
1002
+			}
1003
+		}
1004
+		return $entity_array;
1005
+	}
1006
+
1007
+	/**
1008
+	 * If the user has requested only specific properties (including meta properties like _links or _protected)
1009
+	 * remove everything else.
1010
+	 * @since 4.9.74.p
1011
+	 * @param EEM_Base $model
1012
+	 * @param WP_REST_Request $rest_request
1013
+	 * @param $entity_array
1014
+	 * @return array
1015
+	 * @throws EE_Error
1016
+	 */
1017
+	protected function includeOnlyRequestedProperties(
1018
+		EEM_Base $model,
1019
+		WP_REST_Request $rest_request,
1020
+		$entity_array
1021
+	) {
1022
+
1023
+		$includes_for_this_model = $this->explodeAndGetItemsPrefixedWith($rest_request->get_param('include'), '');
1024
+		$includes_for_this_model = $this->removeModelNamesFromArray($includes_for_this_model);
1025
+		// if they passed in * or didn't specify any includes, return everything
1026
+		if (! in_array('*', $includes_for_this_model)
1027
+			&& ! empty($includes_for_this_model)
1028
+		) {
1029
+			if ($model->has_primary_key_field()) {
1030
+				// always include the primary key. ya just gotta know that at least
1031
+				$includes_for_this_model[] = $model->primary_key_name();
1032
+			}
1033
+			if ($this->explodeAndGetItemsPrefixedWith($rest_request->get_param('calculate'), '')) {
1034
+				$includes_for_this_model[] = '_calculated_fields';
1035
+			}
1036
+			$entity_array = array_intersect_key($entity_array, array_flip($includes_for_this_model));
1037
+		}
1038
+		return $entity_array;
1039
+	}
1040
+
1041
+
1042
+	/**
1043
+	 * Returns a new array with all the names of models removed. Eg
1044
+	 * array( 'Event', 'Datetime.*', 'foobar' ) would become array( 'Datetime.*', 'foobar' )
1045
+	 *
1046
+	 * @param array $arr
1047
+	 * @return array
1048
+	 */
1049
+	private function removeModelNamesFromArray($arr)
1050
+	{
1051
+		return array_diff($arr, array_keys(EE_Registry::instance()->non_abstract_db_models));
1052
+	}
1053
+
1054
+
1055
+	/**
1056
+	 * Gets the calculated fields for the response
1057
+	 *
1058
+	 * @param EEM_Base        $model
1059
+	 * @param array           $wpdb_row
1060
+	 * @param WP_REST_Request $rest_request
1061
+	 * @param boolean $row_is_protected whether this row is password protected or not
1062
+	 * @return \stdClass the _calculations item in the entity
1063
+	 * @throws ObjectDetectedException if a default value has a PHP object, which should never do (and if we
1064
+	 * did, let's know about it ASAP, so let the exception bubble up)
1065
+	 */
1066
+	protected function getEntityCalculations($model, $wpdb_row, $rest_request, $row_is_protected = false)
1067
+	{
1068
+		$calculated_fields = $this->explodeAndGetItemsPrefixedWith(
1069
+			$rest_request->get_param('calculate'),
1070
+			''
1071
+		);
1072
+		// note: setting calculate=* doesn't do anything
1073
+		$calculated_fields_to_return = new \stdClass();
1074
+		$protected_fields = array();
1075
+		foreach ($calculated_fields as $field_to_calculate) {
1076
+			try {
1077
+				// it's password protected, so they shouldn't be able to read this. Remove the value
1078
+				$schema = $this->fields_calculator->getJsonSchemaForModel($model);
1079
+				if ($row_is_protected
1080
+					&& isset($schema['properties'][ $field_to_calculate ]['protected'])
1081
+					&& $schema['properties'][ $field_to_calculate ]['protected']) {
1082
+					$calculated_value = null;
1083
+					$protected_fields[] = $field_to_calculate;
1084
+					if ($schema['properties'][ $field_to_calculate ]['type']) {
1085
+						switch ($schema['properties'][ $field_to_calculate ]['type']) {
1086
+							case 'boolean':
1087
+								$calculated_value = false;
1088
+								break;
1089
+							case 'integer':
1090
+								$calculated_value = 0;
1091
+								break;
1092
+							case 'string':
1093
+								$calculated_value = '';
1094
+								break;
1095
+							case 'array':
1096
+								$calculated_value = array();
1097
+								break;
1098
+							case 'object':
1099
+								$calculated_value = new stdClass();
1100
+								break;
1101
+						}
1102
+					}
1103
+				} else {
1104
+					$calculated_value = ModelDataTranslator::prepareFieldValueForJson(
1105
+						null,
1106
+						$this->fields_calculator->retrieveCalculatedFieldValue(
1107
+							$model,
1108
+							$field_to_calculate,
1109
+							$wpdb_row,
1110
+							$rest_request,
1111
+							$this
1112
+						),
1113
+						$this->getModelVersionInfo()->requestedVersion()
1114
+					);
1115
+				}
1116
+				$calculated_fields_to_return->{$field_to_calculate} = $calculated_value;
1117
+			} catch (RestException $e) {
1118
+				// if we don't have permission to read it, just leave it out. but let devs know about the problem
1119
+				$this->setResponseHeader(
1120
+					'Notices-Field-Calculation-Errors['
1121
+					. $e->getStringCode()
1122
+					. ']['
1123
+					. $model->get_this_model_name()
1124
+					. ']['
1125
+					. $field_to_calculate
1126
+					. ']',
1127
+					$e->getMessage(),
1128
+					true
1129
+				);
1130
+			}
1131
+		}
1132
+		$calculated_fields_to_return->_protected = $protected_fields;
1133
+		return $calculated_fields_to_return;
1134
+	}
1135
+
1136
+
1137
+	/**
1138
+	 * Gets the full URL to the resource, taking the requested version into account
1139
+	 *
1140
+	 * @param string $link_part_after_version_and_slash eg "events/10/datetimes"
1141
+	 * @return string url eg "http://mysite.com/wp-json/ee/v4.6/events/10/datetimes"
1142
+	 */
1143
+	public function getVersionedLinkTo($link_part_after_version_and_slash)
1144
+	{
1145
+		return rest_url(
1146
+			EED_Core_Rest_Api::get_versioned_route_to(
1147
+				$link_part_after_version_and_slash,
1148
+				$this->getModelVersionInfo()->requestedVersion()
1149
+			)
1150
+		);
1151
+	}
1152
+
1153
+
1154
+	/**
1155
+	 * Gets the correct lowercase name for the relation in the API according
1156
+	 * to the relation's type
1157
+	 *
1158
+	 * @param string                  $relation_name
1159
+	 * @param \EE_Model_Relation_Base $relation_obj
1160
+	 * @return string
1161
+	 */
1162
+	public static function getRelatedEntityName($relation_name, $relation_obj)
1163
+	{
1164
+		if ($relation_obj instanceof EE_Belongs_To_Relation) {
1165
+			return strtolower($relation_name);
1166
+		} else {
1167
+			return EEH_Inflector::pluralize_and_lower($relation_name);
1168
+		}
1169
+	}
1170
+
1171
+
1172
+	/**
1173
+	 * Gets the one model object with the specified id for the specified model
1174
+	 *
1175
+	 * @param EEM_Base        $model
1176
+	 * @param WP_REST_Request $request
1177
+	 * @return array
1178
+	 */
1179
+	public function getEntityFromModel($model, $request)
1180
+	{
1181
+		$context = $this->validateContext($request->get_param('caps'));
1182
+		return $this->getOneOrReportPermissionError($model, $request, $context);
1183
+	}
1184
+
1185
+
1186
+	/**
1187
+	 * If a context is provided which isn't valid, maybe it was added in a future
1188
+	 * version so just treat it as a default read
1189
+	 *
1190
+	 * @param string $context
1191
+	 * @return string array key of EEM_Base::cap_contexts_to_cap_action_map()
1192
+	 */
1193
+	public function validateContext($context)
1194
+	{
1195
+		if (! $context) {
1196
+			$context = EEM_Base::caps_read;
1197
+		}
1198
+		$valid_contexts = EEM_Base::valid_cap_contexts();
1199
+		if (in_array($context, $valid_contexts)) {
1200
+			return $context;
1201
+		} else {
1202
+			return EEM_Base::caps_read;
1203
+		}
1204
+	}
1205
+
1206
+
1207
+	/**
1208
+	 * Verifies the passed in value is an allowable default where conditions value.
1209
+	 *
1210
+	 * @param $default_query_params
1211
+	 * @return string
1212
+	 */
1213
+	public function validateDefaultQueryParams($default_query_params)
1214
+	{
1215
+		$valid_default_where_conditions_for_api_calls = array(
1216
+			EEM_Base::default_where_conditions_all,
1217
+			EEM_Base::default_where_conditions_minimum_all,
1218
+			EEM_Base::default_where_conditions_minimum_others,
1219
+		);
1220
+		if (! $default_query_params) {
1221
+			$default_query_params = EEM_Base::default_where_conditions_all;
1222
+		}
1223
+		if (in_array(
1224
+			$default_query_params,
1225
+			$valid_default_where_conditions_for_api_calls,
1226
+			true
1227
+		)) {
1228
+			return $default_query_params;
1229
+		} else {
1230
+			return EEM_Base::default_where_conditions_all;
1231
+		}
1232
+	}
1233
+
1234
+
1235
+	/**
1236
+	 * Translates API filter get parameter into model query params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions.
1237
+	 * Note: right now the query parameter keys for fields (and related fields)
1238
+	 * can be left as-is, but it's quite possible this will change someday.
1239
+	 * Also, this method's contents might be candidate for moving to Model_Data_Translator
1240
+	 *
1241
+	 * @param EEM_Base $model
1242
+	 * @param array    $query_parameters  from $_GET parameter @see Read:handle_request_get_all
1243
+	 * @return array model query params (@see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions)
1244
+	 *                                    or FALSE to indicate that absolutely no results should be returned
1245
+	 * @throws EE_Error
1246
+	 * @throws RestException
1247
+	 */
1248
+	public function createModelQueryParams($model, $query_params)
1249
+	{
1250
+		$model_query_params = array();
1251
+		if (isset($query_params['where'])) {
1252
+			$model_query_params[0] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1253
+				$query_params['where'],
1254
+				$model,
1255
+				$this->getModelVersionInfo()->requestedVersion()
1256
+			);
1257
+		}
1258
+		if (isset($query_params['order_by'])) {
1259
+			$order_by = $query_params['order_by'];
1260
+		} elseif (isset($query_params['orderby'])) {
1261
+			$order_by = $query_params['orderby'];
1262
+		} else {
1263
+			$order_by = null;
1264
+		}
1265
+		if ($order_by !== null) {
1266
+			if (is_array($order_by)) {
1267
+				$order_by = ModelDataTranslator::prepareFieldNamesInArrayKeysFromJson($order_by);
1268
+			} else {
1269
+				// it's a single item
1270
+				$order_by = ModelDataTranslator::prepareFieldNameFromJson($order_by);
1271
+			}
1272
+			$model_query_params['order_by'] = $order_by;
1273
+		}
1274
+		if (isset($query_params['group_by'])) {
1275
+			$group_by = $query_params['group_by'];
1276
+		} elseif (isset($query_params['groupby'])) {
1277
+			$group_by = $query_params['groupby'];
1278
+		} else {
1279
+			$group_by = array_keys($model->get_combined_primary_key_fields());
1280
+		}
1281
+		// make sure they're all real names
1282
+		if (is_array($group_by)) {
1283
+			$group_by = ModelDataTranslator::prepareFieldNamesFromJson($group_by);
1284
+		}
1285
+		if ($group_by !== null) {
1286
+			$model_query_params['group_by'] = $group_by;
1287
+		}
1288
+		if (isset($query_params['having'])) {
1289
+			$model_query_params['having'] = ModelDataTranslator::prepareConditionsQueryParamsForModels(
1290
+				$query_params['having'],
1291
+				$model,
1292
+				$this->getModelVersionInfo()->requestedVersion()
1293
+			);
1294
+		}
1295
+		if (isset($query_params['order'])) {
1296
+			$model_query_params['order'] = $query_params['order'];
1297
+		}
1298
+		if (isset($query_params['mine'])) {
1299
+			$model_query_params = $model->alter_query_params_to_only_include_mine($model_query_params);
1300
+		}
1301
+		if (isset($query_params['limit'])) {
1302
+			// limit should be either a string like '23' or '23,43', or an array with two items in it
1303
+			if (! is_array($query_params['limit'])) {
1304
+				$limit_array = explode(',', (string) $query_params['limit']);
1305
+			} else {
1306
+				$limit_array = $query_params['limit'];
1307
+			}
1308
+			$sanitized_limit = array();
1309
+			foreach ($limit_array as $key => $limit_part) {
1310
+				if ($this->debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1311
+					throw new EE_Error(
1312
+						sprintf(
1313
+							__(
1314
+							// @codingStandardsIgnoreStart
1315
+								'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.',
1316
+								// @codingStandardsIgnoreEnd
1317
+								'event_espresso'
1318
+							),
1319
+							wp_json_encode($query_params['limit'])
1320
+						)
1321
+					);
1322
+				}
1323
+				$sanitized_limit[] = (int) $limit_part;
1324
+			}
1325
+			$model_query_params['limit'] = implode(',', $sanitized_limit);
1326
+		} else {
1327
+			$model_query_params['limit'] = EED_Core_Rest_Api::get_default_query_limit();
1328
+		}
1329
+		if (isset($query_params['caps'])) {
1330
+			$model_query_params['caps'] = $this->validateContext($query_params['caps']);
1331
+		} else {
1332
+			$model_query_params['caps'] = EEM_Base::caps_read;
1333
+		}
1334
+		if (isset($query_params['default_where_conditions'])) {
1335
+			$model_query_params['default_where_conditions'] = $this->validateDefaultQueryParams(
1336
+				$query_params['default_where_conditions']
1337
+			);
1338
+		}
1339
+		// if this is a model protected by a password on another model, exclude the password protected
1340
+		// entities by default. But if they passed in a password, try to show them all. If the password is wrong,
1341
+		// though, they'll get an error (see Read::createEntityFromWpdbResult() which calls Read::checkPassword)
1342
+		if (! $model->hasPassword()
1343
+			&& $model->restrictedByRelatedModelPassword()
1344
+			&& $model_query_params['caps'] === EEM_Base::caps_read) {
1345
+			if (empty($query_params['password'])) {
1346
+				$model_query_params['exclude_protected'] = true;
1347
+			}
1348
+		}
1349
+
1350
+		return apply_filters('FHEE__Read__create_model_query_params', $model_query_params, $query_params, $model);
1351
+	}
1352
+
1353
+
1354
+	/**
1355
+	 * Changes the REST-style query params for use in the models
1356
+	 *
1357
+	 * @deprecated
1358
+	 * @param EEM_Base $model
1359
+	 * @param array    $query_params sub-array from @see EEM_Base::get_all()
1360
+	 * @return array
1361
+	 */
1362
+	public function prepareRestQueryParamsKeyForModels($model, $query_params)
1363
+	{
1364
+		$model_ready_query_params = array();
1365
+		foreach ($query_params as $key => $value) {
1366
+			if (is_array($value)) {
1367
+				$model_ready_query_params[ $key ] = $this->prepareRestQueryParamsKeyForModels($model, $value);
1368
+			} else {
1369
+				$model_ready_query_params[ $key ] = $value;
1370
+			}
1371
+		}
1372
+		return $model_ready_query_params;
1373
+	}
1374
+
1375
+
1376
+	/**
1377
+	 * @deprecated instead use ModelDataTranslator::prepareFieldValuesFromJson()
1378
+	 * @param $model
1379
+	 * @param $query_params
1380
+	 * @return array
1381
+	 */
1382
+	public function prepareRestQueryParamsValuesForModels($model, $query_params)
1383
+	{
1384
+		$model_ready_query_params = array();
1385
+		foreach ($query_params as $key => $value) {
1386
+			if (is_array($value)) {
1387
+				$model_ready_query_params[ $key ] = $this->prepareRestQueryParamsValuesForModels($model, $value);
1388
+			} else {
1389
+				$model_ready_query_params[ $key ] = $value;
1390
+			}
1391
+		}
1392
+		return $model_ready_query_params;
1393
+	}
1394
+
1395
+
1396
+	/**
1397
+	 * Explodes the string on commas, and only returns items with $prefix followed by a period.
1398
+	 * If no prefix is specified, returns items with no period.
1399
+	 *
1400
+	 * @param string|array $string_to_explode eg "jibba,jabba, blah, blah, blah" or array('jibba', 'jabba' )
1401
+	 * @param string       $prefix            "Event" or "foobar"
1402
+	 * @return array $string_to_exploded exploded on COMMAS, and if a prefix was specified
1403
+	 *                                        we only return strings starting with that and a period; if no prefix was
1404
+	 *                                        specified we return all items containing NO periods
1405
+	 */
1406
+	public function explodeAndGetItemsPrefixedWith($string_to_explode, $prefix)
1407
+	{
1408
+		if (is_string($string_to_explode)) {
1409
+			$exploded_contents = explode(',', $string_to_explode);
1410
+		} elseif (is_array($string_to_explode)) {
1411
+			$exploded_contents = $string_to_explode;
1412
+		} else {
1413
+			$exploded_contents = array();
1414
+		}
1415
+		// if the string was empty, we want an empty array
1416
+		$exploded_contents = array_filter($exploded_contents);
1417
+		$contents_with_prefix = array();
1418
+		foreach ($exploded_contents as $item) {
1419
+			$item = trim($item);
1420
+			// if no prefix was provided, so we look for items with no "." in them
1421
+			if (! $prefix) {
1422
+				// does this item have a period?
1423
+				if (strpos($item, '.') === false) {
1424
+					// if not, then its what we're looking for
1425
+					$contents_with_prefix[] = $item;
1426
+				}
1427
+			} elseif (strpos($item, $prefix . '.') === 0) {
1428
+				// this item has the prefix and a period, grab it
1429
+				$contents_with_prefix[] = substr(
1430
+					$item,
1431
+					strpos($item, $prefix . '.') + strlen($prefix . '.')
1432
+				);
1433
+			} elseif ($item === $prefix) {
1434
+				// this item is JUST the prefix
1435
+				// so let's grab everything after, which is a blank string
1436
+				$contents_with_prefix[] = '';
1437
+			}
1438
+		}
1439
+		return $contents_with_prefix;
1440
+	}
1441
+
1442
+
1443
+	/**
1444
+	 * @deprecated since 4.8.36.rc.001 You should instead use Read::explode_and_get_items_prefixed_with.
1445
+	 * Deprecated because its return values were really quite confusing- sometimes it returned
1446
+	 * an empty array (when the include string was blank or '*') or sometimes it returned
1447
+	 * array('*') (when you provided a model and a model of that kind was found).
1448
+	 * Parses the $include_string so we fetch all the field names relating to THIS model
1449
+	 * (ie have NO period in them), or for the provided model (ie start with the model
1450
+	 * name and then a period).
1451
+	 * @param string $include_string @see Read:handle_request_get_all
1452
+	 * @param string $model_name
1453
+	 * @return array of fields for this model. If $model_name is provided, then
1454
+	 *                               the fields for that model, with the model's name removed from each.
1455
+	 *                               If $include_string was blank or '*' returns an empty array
1456
+	 */
1457
+	public function extractIncludesForThisModel($include_string, $model_name = null)
1458
+	{
1459
+		if (is_array($include_string)) {
1460
+			$include_string = implode(',', $include_string);
1461
+		}
1462
+		if ($include_string === '*' || $include_string === '') {
1463
+			return array();
1464
+		}
1465
+		$includes = explode(',', $include_string);
1466
+		$extracted_fields_to_include = array();
1467
+		if ($model_name) {
1468
+			foreach ($includes as $field_to_include) {
1469
+				$field_to_include = trim($field_to_include);
1470
+				if (strpos($field_to_include, $model_name . '.') === 0) {
1471
+					// found the model name at the exact start
1472
+					$field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1473
+					$extracted_fields_to_include[] = $field_sans_model_name;
1474
+				} elseif ($field_to_include == $model_name) {
1475
+					$extracted_fields_to_include[] = '*';
1476
+				}
1477
+			}
1478
+		} else {
1479
+			// look for ones with no period
1480
+			foreach ($includes as $field_to_include) {
1481
+				$field_to_include = trim($field_to_include);
1482
+				if (strpos($field_to_include, '.') === false
1483
+					&& ! $this->getModelVersionInfo()->isModelNameInThisVersion($field_to_include)
1484
+				) {
1485
+					$extracted_fields_to_include[] = $field_to_include;
1486
+				}
1487
+			}
1488
+		}
1489
+		return $extracted_fields_to_include;
1490
+	}
1491
+
1492
+
1493
+	/**
1494
+	 * Gets the single item using the model according to the request in the context given, otherwise
1495
+	 * returns that it's inaccessible to the current user
1496
+	 *
1497
+	 * @param EEM_Base $model
1498
+	 * @param WP_REST_Request $request
1499
+	 * @param null $context
1500
+	 * @return array
1501
+	 * @throws EE_Error
1502
+	 */
1503
+	public function getOneOrReportPermissionError(EEM_Base $model, WP_REST_Request $request, $context = null)
1504
+	{
1505
+		$query_params = array(array($model->primary_key_name() => $request->get_param('id')), 'limit' => 1);
1506
+		if ($model instanceof EEM_Soft_Delete_Base) {
1507
+			$query_params = $model->alter_query_params_so_deleted_and_undeleted_items_included($query_params);
1508
+		}
1509
+		$restricted_query_params = $query_params;
1510
+		$restricted_query_params['caps'] = $context;
1511
+		$this->setDebugInfo('model query params', $restricted_query_params);
1512
+		$model_rows = $model->get_all_wpdb_results($restricted_query_params);
1513
+		if (! empty($model_rows)) {
1514
+			return $this->createEntityFromWpdbResult(
1515
+				$model,
1516
+				reset($model_rows),
1517
+				$request
1518
+			);
1519
+		} else {
1520
+			// ok let's test to see if we WOULD have found it, had we not had restrictions from missing capabilities
1521
+			$lowercase_model_name = strtolower($model->get_this_model_name());
1522
+			if ($model->exists($query_params)) {
1523
+				// you got shafted- it existed but we didn't want to tell you!
1524
+				throw new RestException(
1525
+					'rest_user_cannot_' . $context,
1526
+					sprintf(
1527
+						__('Sorry, you cannot %1$s this %2$s. Missing permissions are: %3$s', 'event_espresso'),
1528
+						$context,
1529
+						$lowercase_model_name,
1530
+						Capabilities::getMissingPermissionsString(
1531
+							$model,
1532
+							$context
1533
+						)
1534
+					),
1535
+					array('status' => 403)
1536
+				);
1537
+			} else {
1538
+				// it's not you. It just doesn't exist
1539
+				throw new RestException(
1540
+					sprintf('rest_%s_invalid_id', $lowercase_model_name),
1541
+					sprintf(__('Invalid %s ID.', 'event_espresso'), $lowercase_model_name),
1542
+					array('status' => 404)
1543
+				);
1544
+			}
1545
+		}
1546
+	}
1547
+
1548
+	/**
1549
+	 * Checks that if this content requires a password to be read, that it's been provided and is correct.
1550
+	 * @since 4.9.74.p
1551
+	 * @param EEM_Base $model
1552
+	 * @param $model_row
1553
+	 * @param $query_params Adds 'default_where_conditions' => 'minimum' to ensure we don't confuse trashed with
1554
+	 *                      password protected.
1555
+	 * @param WP_REST_Request $request
1556
+	 * @throws EE_Error
1557
+	 * @throws InvalidArgumentException
1558
+	 * @throws InvalidDataTypeException
1559
+	 * @throws InvalidInterfaceException
1560
+	 * @throws RestPasswordRequiredException
1561
+	 * @throws RestPasswordIncorrectException
1562
+	 * @throws \EventEspresso\core\exceptions\ModelConfigurationException
1563
+	 * @throws ReflectionException
1564
+	 */
1565
+	protected function checkPassword(EEM_Base $model, $model_row, $query_params, WP_REST_Request $request)
1566
+	{
1567
+		$query_params['default_where_conditions'] = 'minimum';
1568
+		// stuff is only "protected" for front-end requests. Elsewhere, you either get full permission to access the object
1569
+		// or you don't.
1570
+		$request_caps = $request->get_param('caps');
1571
+		if (isset($request_caps) && $request_caps !== EEM_Base::caps_read) {
1572
+			return;
1573
+		}
1574
+		// if this entity requires a password, they better give it and it better be right!
1575
+		if ($model->hasPassword()
1576
+			&& $model_row[ $model->getPasswordField()->get_qualified_column() ] !== '') {
1577
+			if (empty($request['password'])) {
1578
+				throw new RestPasswordRequiredException();
1579
+			} elseif (!hash_equals(
1580
+				$model_row[ $model->getPasswordField()->get_qualified_column() ],
1581
+				$request['password']
1582
+			)) {
1583
+				throw new RestPasswordIncorrectException();
1584
+			}
1585
+		} // wait! maybe this content is password protected
1586
+		elseif ($model->restrictedByRelatedModelPassword()
1587
+			&& $request->get_param('caps') === EEM_Base::caps_read) {
1588
+			$password_supplied = $request->get_param('password');
1589
+			if (empty($password_supplied)) {
1590
+				$query_params['exclude_protected'] = true;
1591
+				if (!$model->exists($query_params)) {
1592
+					throw new RestPasswordRequiredException();
1593
+				}
1594
+			} else {
1595
+				$query_params[0][ $model->modelChainAndPassword() ] = $password_supplied;
1596
+				if (!$model->exists($query_params)) {
1597
+					throw new RestPasswordIncorrectException();
1598
+				}
1599
+			}
1600
+		}
1601
+	}
1602 1602
 }
Please login to merge, or discard this patch.
Spacing   +68 added lines, -68 removed lines patch added patch discarded remove patch
@@ -78,7 +78,7 @@  discard block
 block discarded – undo
78 78
         $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
79 79
         try {
80 80
             $controller->setRequestedVersion($version);
81
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
81
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
82 82
                 return $controller->sendResponse(
83 83
                     new WP_Error(
84 84
                         'endpoint_parsing_error',
@@ -119,7 +119,7 @@  discard block
 block discarded – undo
119 119
         $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
120 120
         try {
121 121
             $controller->setRequestedVersion($version);
122
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
122
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
123 123
                 return array();
124 124
             }
125 125
             // get the model for this version
@@ -176,11 +176,11 @@  discard block
 block discarded – undo
176 176
      */
177 177
     protected function translateDefaultsForRestResponse($field_name, EE_Model_Field_Base $field, array $schema)
178 178
     {
179
-        if (isset($schema['properties'][ $field_name ]['default'])) {
180
-            if (is_array($schema['properties'][ $field_name ]['default'])) {
181
-                foreach ($schema['properties'][ $field_name ]['default'] as $default_key => $default_value) {
179
+        if (isset($schema['properties'][$field_name]['default'])) {
180
+            if (is_array($schema['properties'][$field_name]['default'])) {
181
+                foreach ($schema['properties'][$field_name]['default'] as $default_key => $default_value) {
182 182
                     if ($default_key === 'raw') {
183
-                        $schema['properties'][ $field_name ]['default'][ $default_key ] =
183
+                        $schema['properties'][$field_name]['default'][$default_key] =
184 184
                             ModelDataTranslator::prepareFieldValueForJson(
185 185
                                 $field,
186 186
                                 $default_value,
@@ -189,9 +189,9 @@  discard block
 block discarded – undo
189 189
                     }
190 190
                 }
191 191
             } else {
192
-                $schema['properties'][ $field_name ]['default'] = ModelDataTranslator::prepareFieldValueForJson(
192
+                $schema['properties'][$field_name]['default'] = ModelDataTranslator::prepareFieldValueForJson(
193 193
                     $field,
194
-                    $schema['properties'][ $field_name ]['default'],
194
+                    $schema['properties'][$field_name]['default'],
195 195
                     $this->getModelVersionInfo()->requestedVersion()
196 196
                 );
197 197
             }
@@ -213,9 +213,9 @@  discard block
 block discarded – undo
213 213
     protected function maybeAddExtraFieldsToSchema($field_name, EE_Model_Field_Base $field, array $schema)
214 214
     {
215 215
         if ($field instanceof EE_Datetime_Field) {
216
-            $schema['properties'][ $field_name . '_gmt' ] = $field->getSchema();
216
+            $schema['properties'][$field_name.'_gmt'] = $field->getSchema();
217 217
             // modify the description
218
-            $schema['properties'][ $field_name . '_gmt' ]['description'] = sprintf(
218
+            $schema['properties'][$field_name.'_gmt']['description'] = sprintf(
219 219
                 esc_html__('%s - the value for this field is in GMT.', 'event_espresso'),
220 220
                 wp_specialchars_decode($field->get_nicename(), ENT_QUOTES)
221 221
             );
@@ -258,7 +258,7 @@  discard block
 block discarded – undo
258 258
         $controller = LoaderFactory::getLoader()->getNew('EventEspresso\core\libraries\rest_api\controllers\model\Read');
259 259
         try {
260 260
             $controller->setRequestedVersion($version);
261
-            if (! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
261
+            if ( ! $controller->getModelVersionInfo()->isModelNameInThisVersion($model_name)) {
262 262
                 return $controller->sendResponse(
263 263
                     new WP_Error(
264 264
                         'endpoint_parsing_error',
@@ -337,7 +337,7 @@  discard block
 block discarded – undo
337 337
     public function getEntitiesFromModel($model, $request)
338 338
     {
339 339
         $query_params = $this->createModelQueryParams($model, $request->get_params());
340
-        if (! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
340
+        if ( ! Capabilities::currentUserHasPartialAccessTo($model, $query_params['caps'])) {
341 341
             $model_name_plural = EEH_Inflector::pluralize_and_lower($model->get_this_model_name());
342 342
             throw new RestException(
343 343
                 sprintf('rest_%s_cannot_list', $model_name_plural),
@@ -349,14 +349,14 @@  discard block
 block discarded – undo
349 349
                 array('status' => 403)
350 350
             );
351 351
         }
352
-        if (! $request->get_header('no_rest_headers')) {
352
+        if ( ! $request->get_header('no_rest_headers')) {
353 353
             $this->setHeadersFromQueryParams($model, $query_params);
354 354
         }
355 355
         /** @type array $results */
356 356
         $results = $model->get_all_wpdb_results($query_params);
357 357
         $nice_results = array();
358 358
         foreach ($results as $result) {
359
-            $nice_results[] =  $this->createEntityFromWpdbResult(
359
+            $nice_results[] = $this->createEntityFromWpdbResult(
360 360
                 $model,
361 361
                 $result,
362 362
                 $request
@@ -390,7 +390,7 @@  discard block
 block discarded – undo
390 390
         $context = $this->validateContext($request->get_param('caps'));
391 391
         $model = $relation->get_this_model();
392 392
         $related_model = $relation->get_other_model();
393
-        if (! isset($primary_model_query_params[0])) {
393
+        if ( ! isset($primary_model_query_params[0])) {
394 394
             $primary_model_query_params[0] = array();
395 395
         }
396 396
         // check if they can access the 1st model object
@@ -413,7 +413,7 @@  discard block
 block discarded – undo
413 413
         if (is_array($primary_model_rows)) {
414 414
             $primary_model_row = reset($primary_model_rows);
415 415
         }
416
-        if (! (
416
+        if ( ! (
417 417
             Capabilities::currentUserHasPartialAccessTo($related_model, $context)
418 418
             && $primary_model_row
419 419
         )
@@ -453,13 +453,13 @@  discard block
 block discarded – undo
453 453
         );
454 454
         $query_params = $this->createModelQueryParams($relation->get_other_model(), $request->get_params());
455 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()
456
+            $query_params[0][$relation->get_this_model()->get_this_model_name()
457 457
                               . '.'
458
-                              . $where_condition_key ] = $where_condition_value;
458
+                              . $where_condition_key] = $where_condition_value;
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 */
@@ -510,7 +510,7 @@  discard block
 block discarded – undo
510 510
      */
511 511
     public function getEntitiesFromRelation($id, $relation, $request)
512 512
     {
513
-        if (! $relation->get_this_model()->has_primary_key_field()) {
513
+        if ( ! $relation->get_this_model()->has_primary_key_field()) {
514 514
             throw new EE_Error(
515 515
                 sprintf(
516 516
                     __(
@@ -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'])) {
@@ -594,7 +594,7 @@  discard block
 block discarded – undo
594 594
      */
595 595
     public function createEntityFromWpdbResult($model, $db_row, $rest_request, $deprecated = null)
596 596
     {
597
-        if (! $rest_request instanceof WP_REST_Request) {
597
+        if ( ! $rest_request instanceof WP_REST_Request) {
598 598
             // ok so this was called in the old style, where the 3rd arg was
599 599
             // $include, and the 4th arg was $context
600 600
             // now setup the request just to avoid fatal errors, although we won't be able
@@ -669,7 +669,7 @@  discard block
 block discarded – undo
669 669
             $rest_request,
670 670
             $this
671 671
         );
672
-        if (! $current_user_full_access_to_entity) {
672
+        if ( ! $current_user_full_access_to_entity) {
673 673
             $result_without_inaccessible_fields = Capabilities::filterOutInaccessibleEntityFields(
674 674
                 $entity_array,
675 675
                 $model,
@@ -701,7 +701,7 @@  discard block
 block discarded – undo
701 701
      */
702 702
     protected function addProtectedProperty(EEM_Base $model, $results_so_far, $protected)
703 703
     {
704
-        if (! $model->hasPassword() || ! $protected) {
704
+        if ( ! $model->hasPassword() || ! $protected) {
705 705
             return $results_so_far;
706 706
         }
707 707
         $password_field = $model->getPasswordField();
@@ -715,7 +715,7 @@  discard block
 block discarded – undo
715 715
             $fields_included
716 716
         );
717 717
         foreach ($fields_included as $field_name) {
718
-            $results_so_far['_protected'][] = $field_name ;
718
+            $results_so_far['_protected'][] = $field_name;
719 719
         }
720 720
         return $results_so_far;
721 721
     }
@@ -746,8 +746,8 @@  discard block
 block discarded – undo
746 746
         if ($do_chevy_shuffle) {
747 747
             global $post;
748 748
             $old_post = $post;
749
-            $post = get_post($result[ $model->primary_key_name() ]);
750
-            if (! $post instanceof \WP_Post) {
749
+            $post = get_post($result[$model->primary_key_name()]);
750
+            if ( ! $post instanceof \WP_Post) {
751 751
                 // well that's weird, because $result is what we JUST fetched from the database
752 752
                 throw new RestException(
753 753
                     'error_fetching_post_from_database_results',
@@ -757,7 +757,7 @@  discard block
 block discarded – undo
757 757
                     )
758 758
                 );
759 759
             }
760
-            $model_object_classname = 'EE_' . $model->get_this_model_name();
760
+            $model_object_classname = 'EE_'.$model->get_this_model_name();
761 761
             $post->{$model_object_classname} = \EE_Registry::instance()->load_class(
762 762
                 $model_object_classname,
763 763
                 $result,
@@ -768,13 +768,13 @@  discard block
 block discarded – undo
768 768
         foreach ($result as $field_name => $field_value) {
769 769
             $field_obj = $model->field_settings_for($field_name);
770 770
             if ($this->isSubclassOfOne($field_obj, $this->getModelVersionInfo()->fieldsIgnored())) {
771
-                unset($result[ $field_name ]);
771
+                unset($result[$field_name]);
772 772
             } elseif ($this->isSubclassOfOne(
773 773
                 $field_obj,
774 774
                 $this->getModelVersionInfo()->fieldsThatHaveRenderedFormat()
775 775
             )
776 776
             ) {
777
-                $result[ $field_name ] = array(
777
+                $result[$field_name] = array(
778 778
                     'raw'      => $this->prepareFieldObjValueForJson($field_obj, $field_value),
779 779
                     'rendered' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
780 780
                 );
@@ -783,7 +783,7 @@  discard block
 block discarded – undo
783 783
                 $this->getModelVersionInfo()->fieldsThatHavePrettyFormat()
784 784
             )
785 785
             ) {
786
-                $result[ $field_name ] = array(
786
+                $result[$field_name] = array(
787 787
                     'raw'    => $this->prepareFieldObjValueForJson($field_obj, $field_value),
788 788
                     'pretty' => $this->prepareFieldObjValueForJson($field_obj, $field_value, 'pretty'),
789 789
                 );
@@ -814,10 +814,10 @@  discard block
 block discarded – undo
814 814
                         $this->getModelVersionInfo()->requestedVersion()
815 815
                     );
816 816
                 }
817
-                $result[ $field_name . '_gmt' ] = $gmt_date;
818
-                $result[ $field_name ] = $local_date;
817
+                $result[$field_name.'_gmt'] = $gmt_date;
818
+                $result[$field_name] = $local_date;
819 819
             } else {
820
-                $result[ $field_name ] = $this->prepareFieldObjValueForJson($field_obj, $field_value);
820
+                $result[$field_name] = $this->prepareFieldObjValueForJson($field_obj, $field_value);
821 821
             }
822 822
         }
823 823
         if ($do_chevy_shuffle) {
@@ -869,7 +869,7 @@  discard block
 block discarded – undo
869 869
     protected function addExtraFields(EEM_Base $model, $db_row, $entity_array)
870 870
     {
871 871
         if ($model instanceof EEM_CPT_Base) {
872
-            $entity_array['link'] = get_permalink($db_row[ $model->get_primary_key_field()->get_qualified_column() ]);
872
+            $entity_array['link'] = get_permalink($db_row[$model->get_primary_key_field()->get_qualified_column()]);
873 873
         }
874 874
         return $entity_array;
875 875
     }
@@ -894,7 +894,7 @@  discard block
 block discarded – undo
894 894
                     'href' => $this->getVersionedLinkTo(
895 895
                         EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
896 896
                         . '/'
897
-                        . $entity_array[ $model->primary_key_name() ]
897
+                        . $entity_array[$model->primary_key_name()]
898 898
                     ),
899 899
                 ),
900 900
             );
@@ -910,12 +910,12 @@  discard block
 block discarded – undo
910 910
         if ($model->has_primary_key_field()) {
911 911
             foreach ($this->getModelVersionInfo()->relationSettings($model) as $relation_name => $relation_obj) {
912 912
                 $related_model_part = Read::getRelatedEntityName($relation_name, $relation_obj);
913
-                $links[ EED_Core_Rest_Api::ee_api_link_namespace . $related_model_part ] = array(
913
+                $links[EED_Core_Rest_Api::ee_api_link_namespace.$related_model_part] = array(
914 914
                     array(
915 915
                         'href'   => $this->getVersionedLinkTo(
916 916
                             EEH_Inflector::pluralize_and_lower($model->get_this_model_name())
917 917
                             . '/'
918
-                            . $entity_array[ $model->primary_key_name() ]
918
+                            . $entity_array[$model->primary_key_name()]
919 919
                             . '/'
920 920
                             . $related_model_part
921 921
                         ),
@@ -947,7 +947,7 @@  discard block
 block discarded – undo
947 947
         $included_items_protected = false
948 948
     ) {
949 949
         // if $db_row not included, hope the entity array has what we need
950
-        if (! $db_row) {
950
+        if ( ! $db_row) {
951 951
             $db_row = $entity_array;
952 952
         }
953 953
         $relation_settings = $this->getModelVersionInfo()->relationSettings($model);
@@ -980,7 +980,7 @@  discard block
 block discarded – undo
980 980
                         $model->deduce_fields_n_values_from_cols_n_values($db_row)
981 981
                     )
982 982
                 );
983
-                if (! $included_items_protected) {
983
+                if ( ! $included_items_protected) {
984 984
                     try {
985 985
                         $related_results = $this->getEntitiesFromRelationUsingModelQueryParams(
986 986
                             $primary_model_query_params,
@@ -998,7 +998,7 @@  discard block
 block discarded – undo
998 998
                 if ($related_results instanceof WP_Error || $related_results === null) {
999 999
                     $related_results = $relation_obj instanceof EE_Belongs_To_Relation ? null : array();
1000 1000
                 }
1001
-                $entity_array[ Read::getRelatedEntityName($relation_name, $relation_obj) ] = $related_results;
1001
+                $entity_array[Read::getRelatedEntityName($relation_name, $relation_obj)] = $related_results;
1002 1002
             }
1003 1003
         }
1004 1004
         return $entity_array;
@@ -1023,7 +1023,7 @@  discard block
 block discarded – undo
1023 1023
         $includes_for_this_model = $this->explodeAndGetItemsPrefixedWith($rest_request->get_param('include'), '');
1024 1024
         $includes_for_this_model = $this->removeModelNamesFromArray($includes_for_this_model);
1025 1025
         // if they passed in * or didn't specify any includes, return everything
1026
-        if (! in_array('*', $includes_for_this_model)
1026
+        if ( ! in_array('*', $includes_for_this_model)
1027 1027
             && ! empty($includes_for_this_model)
1028 1028
         ) {
1029 1029
             if ($model->has_primary_key_field()) {
@@ -1077,12 +1077,12 @@  discard block
 block discarded – undo
1077 1077
                 // it's password protected, so they shouldn't be able to read this. Remove the value
1078 1078
                 $schema = $this->fields_calculator->getJsonSchemaForModel($model);
1079 1079
                 if ($row_is_protected
1080
-                    && isset($schema['properties'][ $field_to_calculate ]['protected'])
1081
-                    && $schema['properties'][ $field_to_calculate ]['protected']) {
1080
+                    && isset($schema['properties'][$field_to_calculate]['protected'])
1081
+                    && $schema['properties'][$field_to_calculate]['protected']) {
1082 1082
                     $calculated_value = null;
1083 1083
                     $protected_fields[] = $field_to_calculate;
1084
-                    if ($schema['properties'][ $field_to_calculate ]['type']) {
1085
-                        switch ($schema['properties'][ $field_to_calculate ]['type']) {
1084
+                    if ($schema['properties'][$field_to_calculate]['type']) {
1085
+                        switch ($schema['properties'][$field_to_calculate]['type']) {
1086 1086
                             case 'boolean':
1087 1087
                                 $calculated_value = false;
1088 1088
                                 break;
@@ -1192,7 +1192,7 @@  discard block
 block discarded – undo
1192 1192
      */
1193 1193
     public function validateContext($context)
1194 1194
     {
1195
-        if (! $context) {
1195
+        if ( ! $context) {
1196 1196
             $context = EEM_Base::caps_read;
1197 1197
         }
1198 1198
         $valid_contexts = EEM_Base::valid_cap_contexts();
@@ -1217,7 +1217,7 @@  discard block
 block discarded – undo
1217 1217
             EEM_Base::default_where_conditions_minimum_all,
1218 1218
             EEM_Base::default_where_conditions_minimum_others,
1219 1219
         );
1220
-        if (! $default_query_params) {
1220
+        if ( ! $default_query_params) {
1221 1221
             $default_query_params = EEM_Base::default_where_conditions_all;
1222 1222
         }
1223 1223
         if (in_array(
@@ -1300,14 +1300,14 @@  discard block
 block discarded – undo
1300 1300
         }
1301 1301
         if (isset($query_params['limit'])) {
1302 1302
             // limit should be either a string like '23' or '23,43', or an array with two items in it
1303
-            if (! is_array($query_params['limit'])) {
1303
+            if ( ! is_array($query_params['limit'])) {
1304 1304
                 $limit_array = explode(',', (string) $query_params['limit']);
1305 1305
             } else {
1306 1306
                 $limit_array = $query_params['limit'];
1307 1307
             }
1308 1308
             $sanitized_limit = array();
1309 1309
             foreach ($limit_array as $key => $limit_part) {
1310
-                if ($this->debug_mode && (! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1310
+                if ($this->debug_mode && ( ! is_numeric($limit_part) || count($sanitized_limit) > 2)) {
1311 1311
                     throw new EE_Error(
1312 1312
                         sprintf(
1313 1313
                             __(
@@ -1339,7 +1339,7 @@  discard block
 block discarded – undo
1339 1339
         // if this is a model protected by a password on another model, exclude the password protected
1340 1340
         // entities by default. But if they passed in a password, try to show them all. If the password is wrong,
1341 1341
         // though, they'll get an error (see Read::createEntityFromWpdbResult() which calls Read::checkPassword)
1342
-        if (! $model->hasPassword()
1342
+        if ( ! $model->hasPassword()
1343 1343
             && $model->restrictedByRelatedModelPassword()
1344 1344
             && $model_query_params['caps'] === EEM_Base::caps_read) {
1345 1345
             if (empty($query_params['password'])) {
@@ -1364,9 +1364,9 @@  discard block
 block discarded – undo
1364 1364
         $model_ready_query_params = array();
1365 1365
         foreach ($query_params as $key => $value) {
1366 1366
             if (is_array($value)) {
1367
-                $model_ready_query_params[ $key ] = $this->prepareRestQueryParamsKeyForModels($model, $value);
1367
+                $model_ready_query_params[$key] = $this->prepareRestQueryParamsKeyForModels($model, $value);
1368 1368
             } else {
1369
-                $model_ready_query_params[ $key ] = $value;
1369
+                $model_ready_query_params[$key] = $value;
1370 1370
             }
1371 1371
         }
1372 1372
         return $model_ready_query_params;
@@ -1384,9 +1384,9 @@  discard block
 block discarded – undo
1384 1384
         $model_ready_query_params = array();
1385 1385
         foreach ($query_params as $key => $value) {
1386 1386
             if (is_array($value)) {
1387
-                $model_ready_query_params[ $key ] = $this->prepareRestQueryParamsValuesForModels($model, $value);
1387
+                $model_ready_query_params[$key] = $this->prepareRestQueryParamsValuesForModels($model, $value);
1388 1388
             } else {
1389
-                $model_ready_query_params[ $key ] = $value;
1389
+                $model_ready_query_params[$key] = $value;
1390 1390
             }
1391 1391
         }
1392 1392
         return $model_ready_query_params;
@@ -1418,17 +1418,17 @@  discard block
 block discarded – undo
1418 1418
         foreach ($exploded_contents as $item) {
1419 1419
             $item = trim($item);
1420 1420
             // if no prefix was provided, so we look for items with no "." in them
1421
-            if (! $prefix) {
1421
+            if ( ! $prefix) {
1422 1422
                 // does this item have a period?
1423 1423
                 if (strpos($item, '.') === false) {
1424 1424
                     // if not, then its what we're looking for
1425 1425
                     $contents_with_prefix[] = $item;
1426 1426
                 }
1427
-            } elseif (strpos($item, $prefix . '.') === 0) {
1427
+            } elseif (strpos($item, $prefix.'.') === 0) {
1428 1428
                 // this item has the prefix and a period, grab it
1429 1429
                 $contents_with_prefix[] = substr(
1430 1430
                     $item,
1431
-                    strpos($item, $prefix . '.') + strlen($prefix . '.')
1431
+                    strpos($item, $prefix.'.') + strlen($prefix.'.')
1432 1432
                 );
1433 1433
             } elseif ($item === $prefix) {
1434 1434
                 // this item is JUST the prefix
@@ -1467,9 +1467,9 @@  discard block
 block discarded – undo
1467 1467
         if ($model_name) {
1468 1468
             foreach ($includes as $field_to_include) {
1469 1469
                 $field_to_include = trim($field_to_include);
1470
-                if (strpos($field_to_include, $model_name . '.') === 0) {
1470
+                if (strpos($field_to_include, $model_name.'.') === 0) {
1471 1471
                     // found the model name at the exact start
1472
-                    $field_sans_model_name = str_replace($model_name . '.', '', $field_to_include);
1472
+                    $field_sans_model_name = str_replace($model_name.'.', '', $field_to_include);
1473 1473
                     $extracted_fields_to_include[] = $field_sans_model_name;
1474 1474
                 } elseif ($field_to_include == $model_name) {
1475 1475
                     $extracted_fields_to_include[] = '*';
@@ -1510,7 +1510,7 @@  discard block
 block discarded – undo
1510 1510
         $restricted_query_params['caps'] = $context;
1511 1511
         $this->setDebugInfo('model query params', $restricted_query_params);
1512 1512
         $model_rows = $model->get_all_wpdb_results($restricted_query_params);
1513
-        if (! empty($model_rows)) {
1513
+        if ( ! empty($model_rows)) {
1514 1514
             return $this->createEntityFromWpdbResult(
1515 1515
                 $model,
1516 1516
                 reset($model_rows),
@@ -1522,7 +1522,7 @@  discard block
 block discarded – undo
1522 1522
             if ($model->exists($query_params)) {
1523 1523
                 // you got shafted- it existed but we didn't want to tell you!
1524 1524
                 throw new RestException(
1525
-                    'rest_user_cannot_' . $context,
1525
+                    'rest_user_cannot_'.$context,
1526 1526
                     sprintf(
1527 1527
                         __('Sorry, you cannot %1$s this %2$s. Missing permissions are: %3$s', 'event_espresso'),
1528 1528
                         $context,
@@ -1573,11 +1573,11 @@  discard block
 block discarded – undo
1573 1573
         }
1574 1574
         // if this entity requires a password, they better give it and it better be right!
1575 1575
         if ($model->hasPassword()
1576
-            && $model_row[ $model->getPasswordField()->get_qualified_column() ] !== '') {
1576
+            && $model_row[$model->getPasswordField()->get_qualified_column()] !== '') {
1577 1577
             if (empty($request['password'])) {
1578 1578
                 throw new RestPasswordRequiredException();
1579
-            } elseif (!hash_equals(
1580
-                $model_row[ $model->getPasswordField()->get_qualified_column() ],
1579
+            } elseif ( ! hash_equals(
1580
+                $model_row[$model->getPasswordField()->get_qualified_column()],
1581 1581
                 $request['password']
1582 1582
             )) {
1583 1583
                 throw new RestPasswordIncorrectException();
@@ -1588,12 +1588,12 @@  discard block
 block discarded – undo
1588 1588
             $password_supplied = $request->get_param('password');
1589 1589
             if (empty($password_supplied)) {
1590 1590
                 $query_params['exclude_protected'] = true;
1591
-                if (!$model->exists($query_params)) {
1591
+                if ( ! $model->exists($query_params)) {
1592 1592
                     throw new RestPasswordRequiredException();
1593 1593
                 }
1594 1594
             } else {
1595
-                $query_params[0][ $model->modelChainAndPassword() ] = $password_supplied;
1596
-                if (!$model->exists($query_params)) {
1595
+                $query_params[0][$model->modelChainAndPassword()] = $password_supplied;
1596
+                if ( ! $model->exists($query_params)) {
1597 1597
                     throw new RestPasswordIncorrectException();
1598 1598
                 }
1599 1599
             }
Please login to merge, or discard this patch.
espresso.php 1 patch
Indentation   +80 added lines, -80 removed lines patch added patch discarded remove patch
@@ -38,103 +38,103 @@
 block discarded – undo
38 38
  * @since           4.0
39 39
  */
40 40
 if (function_exists('espresso_version')) {
41
-    if (! function_exists('espresso_duplicate_plugin_error')) {
42
-        /**
43
-         *    espresso_duplicate_plugin_error
44
-         *    displays if more than one version of EE is activated at the same time
45
-         */
46
-        function espresso_duplicate_plugin_error()
47
-        {
48
-            ?>
41
+	if (! function_exists('espresso_duplicate_plugin_error')) {
42
+		/**
43
+		 *    espresso_duplicate_plugin_error
44
+		 *    displays if more than one version of EE is activated at the same time
45
+		 */
46
+		function espresso_duplicate_plugin_error()
47
+		{
48
+			?>
49 49
             <div class="error">
50 50
                 <p>
51 51
                     <?php
52
-                    echo esc_html__(
53
-                        'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
-                        'event_espresso'
55
-                    ); ?>
52
+					echo esc_html__(
53
+						'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
54
+						'event_espresso'
55
+					); ?>
56 56
                 </p>
57 57
             </div>
58 58
             <?php
59
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
60
-        }
61
-    }
62
-    add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
59
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
60
+		}
61
+	}
62
+	add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
63 63
 } else {
64
-    define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
-    if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
-        /**
67
-         * espresso_minimum_php_version_error
68
-         *
69
-         * @return void
70
-         */
71
-        function espresso_minimum_php_version_error()
72
-        {
73
-            ?>
64
+	define('EE_MIN_PHP_VER_REQUIRED', '5.4.0');
65
+	if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
66
+		/**
67
+		 * espresso_minimum_php_version_error
68
+		 *
69
+		 * @return void
70
+		 */
71
+		function espresso_minimum_php_version_error()
72
+		{
73
+			?>
74 74
             <div class="error">
75 75
                 <p>
76 76
                     <?php
77
-                    printf(
78
-                        esc_html__(
79
-                            'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
-                            'event_espresso'
81
-                        ),
82
-                        EE_MIN_PHP_VER_REQUIRED,
83
-                        PHP_VERSION,
84
-                        '<br/>',
85
-                        '<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
-                    );
87
-                    ?>
77
+					printf(
78
+						esc_html__(
79
+							'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
80
+							'event_espresso'
81
+						),
82
+						EE_MIN_PHP_VER_REQUIRED,
83
+						PHP_VERSION,
84
+						'<br/>',
85
+						'<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>'
86
+					);
87
+					?>
88 88
                 </p>
89 89
             </div>
90 90
             <?php
91
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
92
-        }
91
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
92
+		}
93 93
 
94
-        add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
-    } else {
96
-        define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
-        /**
98
-         * espresso_version
99
-         * Returns the plugin version
100
-         *
101
-         * @return string
102
-         */
103
-        function espresso_version()
104
-        {
105
-            return apply_filters('FHEE__espresso__espresso_version', '4.9.77.rc.002');
106
-        }
94
+		add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
95
+	} else {
96
+		define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
97
+		/**
98
+		 * espresso_version
99
+		 * Returns the plugin version
100
+		 *
101
+		 * @return string
102
+		 */
103
+		function espresso_version()
104
+		{
105
+			return apply_filters('FHEE__espresso__espresso_version', '4.9.77.rc.002');
106
+		}
107 107
 
108
-        /**
109
-         * espresso_plugin_activation
110
-         * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
-         */
112
-        function espresso_plugin_activation()
113
-        {
114
-            update_option('ee_espresso_activation', true);
115
-        }
108
+		/**
109
+		 * espresso_plugin_activation
110
+		 * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
111
+		 */
112
+		function espresso_plugin_activation()
113
+		{
114
+			update_option('ee_espresso_activation', true);
115
+		}
116 116
 
117
-        register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
117
+		register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
118 118
 
119
-        require_once __DIR__ . '/core/bootstrap_espresso.php';
120
-        bootstrap_espresso();
121
-    }
119
+		require_once __DIR__ . '/core/bootstrap_espresso.php';
120
+		bootstrap_espresso();
121
+	}
122 122
 }
123 123
 if (! function_exists('espresso_deactivate_plugin')) {
124
-    /**
125
-     *    deactivate_plugin
126
-     * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
-     *
128
-     * @access public
129
-     * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
-     * @return    void
131
-     */
132
-    function espresso_deactivate_plugin($plugin_basename = '')
133
-    {
134
-        if (! function_exists('deactivate_plugins')) {
135
-            require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
-        }
137
-        unset($_GET['activate'], $_REQUEST['activate']);
138
-        deactivate_plugins($plugin_basename);
139
-    }
124
+	/**
125
+	 *    deactivate_plugin
126
+	 * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
127
+	 *
128
+	 * @access public
129
+	 * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
130
+	 * @return    void
131
+	 */
132
+	function espresso_deactivate_plugin($plugin_basename = '')
133
+	{
134
+		if (! function_exists('deactivate_plugins')) {
135
+			require_once ABSPATH . 'wp-admin/includes/plugin.php';
136
+		}
137
+		unset($_GET['activate'], $_REQUEST['activate']);
138
+		deactivate_plugins($plugin_basename);
139
+	}
140 140
 }
Please login to merge, or discard this patch.